diff --git a/.agents/skills/create-feed/SKILL.md b/.agents/skills/create-feed/SKILL.md
index 3a70d71..4bac95d 100644
--- a/.agents/skills/create-feed/SKILL.md
+++ b/.agents/skills/create-feed/SKILL.md
@@ -1,234 +1,157 @@
---
name: create-feed
-description: Create new RSS Feed sources. Use this skill when users want to add new data sources, create Feed routes, or ask how to scrape website data.
+description: Create or update RSSBook Feed sources and routes, including Source metadata, route handlers, scraping logic, route tests, and RSSBook utility usage such as cache, date, ofetch, load, formatHTML, parse, filter, sort, union, and intersection.
---
-# Creating a New Feed Guide
+# Create RSSBook Feeds
-This skill helps you create new RSS Feed sources in the RSSBook project.
+Use this skill when adding or updating a Feed route in RSSBook, migrating route logic from another RSS project, scraping a website, or testing a Source route.
-## Core Concepts
+## Core Workflow
-### 1. Source (Data Source)
+1. Read nearby routes in the target category and follow local patterns.
+2. Create the Source at `pkgs/rssbook/src/routers/feeds/{category}/{slug}/index.ts`; use `bun source:new` when scaffolding from an existing template is useful.
+3. Register it in `pkgs/rssbook/src/routers/feeds/{category}/index.ts`.
+4. Use RSSBook utilities from the handler context instead of importing alternate fetch/parser stacks.
+5. Add one explicit route test per Feed route.
+6. Run formatting and TypeScript. Run source route tests with `bun source:test` or `bun source:test:all` only when route network checks are intended.
-A Source is a data source definition, representing a website or service (e.g., GitHub, Twitter). Each Source can contain multiple Feeds.
+## Feed Development Checklist
-**File Location**: `src/routers/feeds/{category}/{slug}/index.ts`
+- Use the core pattern `new Source(...).feed(meta, (app) => app.get(...))`.
+- Place a source at `pkgs/rssbook/src/routers/feeds/{category}/{slug}/index.ts`.
+- Register it in `pkgs/rssbook/src/routers/feeds/{category}/index.ts`.
+- Use lowercase kebab-case source slugs.
+- Use GET routes for feed handlers.
+- Return `Data` and validate with `satisfies Data`.
+- Use `cache.tryGet()` around network-heavy or scrape-heavy work.
+- Use injected `ofetch`, `parse`, `date`, `load`, `formatHTML`, `toAbsoluteURL`, and other handler-context utilities when available.
+- Keep source-specific tests beside the source when adding meaningful behavior.
-### 2. Feed
+## Required References
-A Feed is a specific route under a Source, defining how to fetch and return data.
+Read only the references needed for the task:
-### 3. Category
+- New Source structure, route migration patterns, metadata naming, and route testing: [references/feed-routes.md](references/feed-routes.md)
+- RSSBook utility APIs (`cache`, `date`, `ofetch`, `load`, `formatHTML`, feed transforms): [references/utils.md](references/utils.md)
-A Category organizes multiple Sources, such as `programming`, `news`, `blog`, etc.
-
-## Creation Steps
-
-### Step 1: Determine Category and Slug
-
-- **Category**: Choose an existing category or create a new one
-- **Slug**: Lowercase letters and hyphens, e.g., `my-source`
-
-### Step 2: Create Source File
-
-Create a file at `src/routers/feeds/{category}/{slug}/index.ts`:
+## Source Shape
```typescript
import type { Data, DataItem } from "@/types";
import { Source, t } from "@/utils";
export default new Source({
- slug: "example-source", // Must match the folder name
- title: "Example Source",
- description: "Brief description of this data source",
- domain: "example.com", // Domain of the source website
- config: {
- // Optional: required configuration parameters
- API_KEY: {
- description: "API Key",
- required: true,
- default: "your-default-key",
- },
- },
+ description: "Official news and notices from Example University (示例大学).",
+ domain: "example.edu",
+ slug: "example-university",
+ title: "Example University",
}).feed(
- {
- title: "Feed Title",
- description: "Detailed description of the Feed (supports Markdown)",
- fulltext: true,
- language: ["zh-CN", "en"],
- maintainer: { name: "Your Name" },
- withImage: "If-Present",
- },
- (app) => app.get("/path/:param", async (context) => {
- // Fetching logic
- return data satisfies Data;
- }),
+ {
+ description: "Fetch latest notices from a specified website category.",
+ fulltext: true,
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Latest Notices",
+ withImage: "If-Present",
+ },
+ (app) =>
+ app.get(
+ "/notices/:category",
+ async ({ cache, date, formatHTML, load, ofetch, params, toAbsoluteURL }) => {
+ return data satisfies Data;
+ },
+ {
+ params: t.Object({
+ category: t.String({
+ description: "Website category ID, for example 1234.",
+ }),
+ }),
+ },
+ ),
);
```
-### Step 3: Register to Category
-
-Register in `src/routers/feeds/{category}/index.ts`:
+For a minimal route without params:
```typescript
-import { Category } from "@/utils";
-import mySource from "./my-source";
+import type { Data } from "@/types";
+import { Source } from "@/utils";
-export default new Category("category-name", "Category description").use({
- mySource,
-});
+export default new Source({
+ description: "Example feed source.",
+ domain: "example.com",
+ slug: "example",
+ title: "Example",
+}).feed(
+ {
+ description: "Latest items from Example.",
+ title: "Example Feed",
+ },
+ (app) =>
+ app.get("/latest", async ({ cache, meta: { domain } }) =>
+ cache.tryGet("example:latest", async () => {
+ return { title: "Example Feed", link: `https://${domain}`, item: [] } satisfies Data;
+ }),
+ ),
+);
```
-## Handler Context
+## Data And Utilities
-The context object received by the handler function contains:
+Use `pkgs/rssbook/src/types/data.ts` as the source of truth. Feed handlers return `Data`; items should include stable `title`, `link`, and, when possible, `id` and `date`. Prefer absolute links.
-### Props
+Common utilities are exported from `@/utils` and often injected into handler context:
-| Property | Description |
-|----------|-------------|
-| `meta` | Metadata for the source and Feed (domain, config, title, etc.) |
-| `params` | Route parameters (e.g., `:username`) |
-| `query` | Query parameters |
-| `lang` | Request language (parsed from Accept-Language) |
+- `parse()` for RSS, Atom, JSON Feed, and raw data parsing.
+- `render()` for output generation.
+- `filter()`, `sort()`, `union()`, and `intersection()` for feed operations.
+- `load()` for HTML parsing.
+- `formatHTML()` for sanitizing and normalizing HTML fragments.
+- `toAbsoluteURL()` for link normalization.
+- `Cache` / `cache.tryGet()` for storage-backed caching.
-### Functions
+### Feed Context Properties
-| Function | Description |
-|----------|-------------|
-| `cache` | Cache object, use `cache.tryGet(key, fn)` |
-| `date` | Date parsing function |
-| `ofetch` | Enhanced fetch function |
-| `load` | HTML parser (jQuery-like) |
-| `formatHTML` | HTML cleanup and formatting |
-| `toAbsoluteURL` | Relative URL to absolute URL |
-| `parse` | Parse RSS/Atom Feed |
-| `logger` | Logging tool |
+The handler function receives these properties:
-## Data Type Structure
+- **Metadata**: `meta` (source metadata including `slug`, `title`, `description`, `domain`, `config`), `lang` (request language)
+- **Request**: `params` (route parameters), `query` (query parameters), `headers` (request headers)
+- **Utilities**: `cache`, `date`, `ofetch`, `load`, `formatHTML`, `toAbsoluteURL`, `parse`, `logger`, `uuid`, `config`
-The returned data must conform to the `Data` type:
+## Metadata Rules
-```typescript
-interface Data {
- title: string; // Feed title
- link: string; // Feed link
- description?: string; // Feed description
- language?: string; // Language code
- item?: DataItem[]; // Feed items
- updated?: Date; // Update time
-}
-
-interface DataItem {
- title: string; // Item title
- link: string; // Item link
- description?: string; // Summary
- content?: string; // Full text content
- date?: Date; // Publication date
- author?: Author[]; // Authors
- category?: Category[]; // Categories
- image?: string; // Image
- id?: string; // Unique identifier
-}
-```
+- Use English for `Source.title`, route `title`, route `description`, parameter descriptions, and config descriptions.
+- If the website is primarily in another language, mention the native name in `description`, for example `Example University (示例大学)`.
+- Keep `slug` lowercase hyphen-case and matching the folder name.
+- Keep returned Feed item content in the source language. Only route metadata needs to be English/i18n-friendly.
+
+## Testing Rules
-## Example: API Data Source
+- Put tests beside the Source, for example `pkgs/rssbook/src/routers/feeds/school/example-university/index.test.ts`.
+- Use `getRouteData` from `@/utils/tests/source`.
+- Write one explicit `test(...)` per route. Do not generate route tests with a loop.
+- Route tests should verify the route returns valid `Data` from a real page; do not test Source metadata directly.
+- Default `bun test` intentionally ignores `pkgs/rssbook/src/routers/feeds/**`; use the source scripts for route tests.
```typescript
-export default new Source({
- slug: "github",
- title: "GitHub",
- description: "GitHub code hosting platform",
- domain: "github.com",
-}).feed(
- {
- title: "User Events",
- description: "Fetch GitHub user activity events",
- language: ["en"],
- maintainer: { name: "RSSBook" },
- },
- (app) => app.get(
- "/events/:username",
- async ({ params: { username }, cache, date, ofetch, meta: { domain } }) => {
- const link = `https://api.${domain}/users/${username}/events`;
-
- const items = await cache.tryGet(link, async (url) => {
- const events = await ofetch(url, { responseType: "json" });
- return events.map((event) => ({
- title: event.title,
- link: event.url,
- date: date(event.created_at),
- description: event.description,
- } satisfies DataItem));
- });
-
- return {
- title: `GitHub Events - ${username}`,
- link,
- item: items,
- } satisfies Data;
- },
- {
- params: t.Object({
- username: t.String({ description: "GitHub username" }),
- }),
- },
- ),
-);
-```
+import { describe, expect, test } from "bun:test";
+import { getRouteData } from "@/utils/tests/source";
+import source from ".";
-## Example: HTML Web Scraping
+describe("Example University", () => {
+ test("fetches latest notices", async () => {
+ const data = await getRouteData(source, "/notices/1234");
-```typescript
-export default new Source({
- slug: "blog",
- title: "Blog",
- description: "Blog website",
- domain: "blog.example.com",
-}).feed(
- {
- title: "Latest Articles",
- description: "Fetch latest blog articles",
- fulltext: true,
- language: ["zh-CN"],
- maintainer: { name: "RSSBook" },
- },
- (app) => app.get(
- "/",
- async ({ cache, date, ofetch, load, formatHTML, toAbsoluteURL, meta: { domain } }) => {
- const rootURL = `https://${domain}`;
-
- const items = await cache.tryGet(rootURL, async (url) => {
- const html = await ofetch(url, { responseType: "text" });
- const $ = load(html);
-
- return $("article.post").toArray().map((el) => {
- const $el = $(el);
- return {
- title: $el.find("h2").text().trim(),
- link: toAbsoluteURL($el.find("a").attr("href") || "", rootURL),
- date: date($el.find(".date").text()),
- description: $el.find(".excerpt").text().trim(),
- } satisfies DataItem;
- });
- });
-
- return {
- title: "Blog Latest Articles",
- link: rootURL,
- item: items,
- } satisfies Data;
- },
- ),
-);
+ expect(data.title).toContain("Example University");
+ expect(data.item?.length).toBeGreaterThan(0);
+ }, 20_000);
+});
```
-## Best Practices
+## Project Scripts
-1. **Use Cache**: Always use `cache.tryGet()` to avoid repeated requests
-2. **Type Safety**: Use `satisfies Data` and `satisfies DataItem` to ensure type correctness
-3. **Error Handling**: Use try-catch when fetching full text content
-4. **Concurrent Requests**: Use `Promise.all()` to fetch multiple pages concurrently
-5. **URL Handling**: Use `toAbsoluteURL()` for relative links
-6. **HTML Cleanup**: Use `formatHTML()` to clean HTML content
+- `bun source:new`: interactive Source scaffold. It copies a template from `pkgs/rssbook/src/routers/feeds/_example` into `pkgs/rssbook/src/routers/feeds/{category}/{slug}`. After scaffolding, update metadata, register the Source in the category `index.ts`, and write colocated route tests in `index.test.ts`.
+- `bun source:test`: finds modified Sources under `pkgs/rssbook/src/routers/feeds/**` using git status, including untracked files, and runs each modified Source's colocated `index.test.ts`. If a Source has no test file, it offers to create a `getRouteData`-based template.
+- `bun source:test:all`: runs all Source tests under `pkgs/rssbook/src/routers/feeds`.
+- `bun test`: runs non-Source tests and ignores `pkgs/rssbook/src/routers/feeds/**`, because Source route tests often depend on real network conditions.
diff --git a/.agents/skills/create-feed/references/feed-routes.md b/.agents/skills/create-feed/references/feed-routes.md
new file mode 100644
index 0000000..becae74
--- /dev/null
+++ b/.agents/skills/create-feed/references/feed-routes.md
@@ -0,0 +1,164 @@
+# RSSBook Feed Route Reference
+
+## File Placement
+
+- Source file: `pkgs/rssbook/src/routers/feeds/{category}/{slug}/index.ts`
+- Source test: `pkgs/rssbook/src/routers/feeds/{category}/{slug}/index.test.ts`
+- Category registry: `pkgs/rssbook/src/routers/feeds/{category}/index.ts`
+
+Use `bun source:new` to scaffold a Source from templates in `pkgs/rssbook/src/routers/feeds/_example` when that is faster than creating files manually. The scaffold is only a starting point; update metadata, routes, and tests to follow the rules below.
+
+Register the Source:
+
+```typescript
+import { Category } from "@/utils";
+import example from "./example";
+
+export default new Category("school", "Educational resources and school-related news.").use({
+ example,
+});
+```
+
+## Route Design
+
+- Every Feed route must be a GET route.
+- Route paths are source-local, for example `"/www/:category"`.
+- Full public path becomes `/feeds/{category}/{slug}{routePath}`.
+- Use `t.Object(...)` schemas for params/query where useful.
+- Return `Data` and use `satisfies Data`; map items with `satisfies DataItem`.
+
+## Migrating Existing RSS Logic
+
+When migrating from another RSS project:
+
+1. Identify each old route and make one `.feed(...)` per route.
+2. Replace direct `fetch` with handler `ofetch`.
+3. Replace external `cheerio.load` imports with handler `load`.
+4. Replace custom HTML cleaners with handler `formatHTML`.
+5. Replace manual absolute URL construction with `toAbsoluteURL`.
+6. Wrap network work in `cache.tryGet(cacheKey, async () => ...)`.
+7. Keep file/external-link guards when fetching full article pages.
+8. Return RSSBook `Data`; let `renderPlugin` produce RSS/Atom/JSON.
+
+Example full-text enrichment:
+
+```typescript
+const items = await Promise.all(
+ listItems.map(async (item) => {
+ if (!shouldFetchArticle(item.link)) return item;
+
+ try {
+ const html = await ofetch(item.link, { responseType: "text" });
+ const $ = load(html);
+ const contentHTML = $("div.read").first().html();
+
+ if (contentHTML) {
+ item.description = formatHTML(contentHTML, item.link);
+ }
+ } catch {
+ return item;
+ }
+
+ return item;
+ }),
+);
+```
+
+## Metadata and Naming
+
+- Use English for all route metadata because route metadata is i18n-facing.
+- Use English for:
+ - `Source.title`
+ - `Source.description`
+ - route `title`
+ - route `description`
+ - param/query/config descriptions
+- If a source site is not English, include the original name in description:
+
+```typescript
+new Source({
+ description: "Official website and notices from Example University (示例大学).",
+ domain: "example.edu",
+ slug: "example-university",
+ title: "Example University",
+});
+```
+
+- Returned `Data.title`, `Data.description`, item titles, and item descriptions may stay in the source site's language.
+- Keep maintainers as project/user names, not translated labels.
+
+## Route Tests
+
+Use `getRouteData` from `@/utils/tests/source`.
+
+Helper signature:
+
+```typescript
+getRouteData(source, path, {
+ config?: Record;
+ query?: Record;
+ type?: "raw" | "json";
+});
+```
+
+Rules:
+
+- One explicit `test(...)` per route.
+- Do not loop over routes to create tests.
+- Test route availability and returned `Data`, not Source metadata definitions.
+- Prefer real pages for source tests when the route depends on live website structure.
+- Use a longer timeout, usually `20_000`, for live website scraping.
+- If Bun exits nonzero while all assertions pass, check `bunfig.toml`; this repo has coverage enabled with a threshold.
+- Use `bun source:test` to run tests for modified Sources and `bun source:test:all` to run all Source tests.
+- Default `bun test` ignores `pkgs/rssbook/src/routers/feeds/**`; do not rely on it to run Source route tests.
+
+Example:
+
+```typescript
+import { describe, expect, test } from "bun:test";
+import type { Data } from "@/types";
+import { getRouteData } from "@/utils/tests/source";
+import source from ".";
+
+describe("Example University", () => {
+ test("fetches official website news", async () => {
+ const data = await getRouteData(source, "/news/1234");
+
+ expectData(data);
+ }, 20_000);
+
+ test("fetches academic notices", async () => {
+ const data = await getRouteData(source, "/notices/5678");
+
+ expectData(data);
+ }, 20_000);
+});
+
+function expectData(data: Data) {
+ expect(data.title.length).toBeGreaterThan(0);
+ expect(data.link).toStartWith("https://");
+ expect(data.item?.length).toBeGreaterThan(0);
+}
+```
+
+## Common Scraping Guards
+
+Skip article-page enrichment for:
+
+- Missing links
+- File links such as PDF, Office files, images, archives, audio, and video
+- External domains when the source only supports same-site pages
+
+```typescript
+const FILE_LINK_RE = /\.(pdf|docx?|xlsx?|xls|zip|rar|png|jpe?g|gif|bmp|mp4|mp3|txt)$/i;
+
+function shouldFetchArticle(link: string, rootURL: string): boolean {
+ if (!link || FILE_LINK_RE.test(link)) return false;
+
+ try {
+ return new URL(link).origin === new URL(rootURL).origin;
+ } catch {
+ return false;
+ }
+}
+```
diff --git a/.agents/skills/create-feed/references/utils.md b/.agents/skills/create-feed/references/utils.md
new file mode 100644
index 0000000..a3657f6
--- /dev/null
+++ b/.agents/skills/create-feed/references/utils.md
@@ -0,0 +1,232 @@
+# RSSBook Utility Reference
+
+Use these utilities from the Feed handler context whenever possible:
+
+```typescript
+async ({ cache, date, formatHTML, load, ofetch, toAbsoluteURL }) => {
+ // route logic
+}
+```
+
+## Feed Context Properties
+
+The handler function receives the following properties:
+
+### Metadata
+
+| Property | Type | Description |
+| --- | --- | --- |
+| `meta` | `object` | Source metadata object |
+| `meta.slug` | `string` | Source slug identifier |
+| `meta.title` | `string` | Source display title |
+| `meta.description` | `string` | Source description (Markdown) |
+| `meta.domain` | `string` | Source website domain |
+| `meta.config` | `Record` | Configuration values with defaults resolved |
+| `lang` | `string` | Request language from Accept-Language header |
+
+### Request Parameters
+
+| Property | Type | Description |
+| --- | --- | --- |
+| `params` | `Record` | Route parameters (`:param`) |
+| `query` | `Record` | Query parameters (`?key=value`) |
+| `headers` | `Record` | Request headers |
+
+### Injected Utilities
+
+| Property | Type | Description |
+| --- | --- | --- |
+| `cache` | `Cache` | Cache manager for storage-backed caching |
+| `date` | `(...args) => Date` | Date parsing function |
+| `ofetch` | `(url, options?) => Promise` | HTTP request function |
+| `load` | `(html) => CheerioAPI` | HTML parsing function (Cheerio) |
+| `formatHTML` | `(html, baseURL?) => string` | HTML sanitization function |
+| `toAbsoluteURL` | `(url, baseURL) => string` | URL conversion function |
+| `parse` | `(data, type?) => Data` | Feed parsing function |
+| `logger` | `Logger` | Logging utility |
+| `uuid` | `() => string` | UUID generation function |
+| `config` | `Record` | Raw configuration from request |
+
+## Cache
+
+Cache stores and reuses expensive fetch results.
+
+```typescript
+const data = await cache.tryGet("cache-key", async () => {
+ return fetchedData;
+});
+```
+
+Useful methods:
+
+| Method | Description |
+| --- | --- |
+| `tryGet(key, fetcher, maxAgeMs?)` | Get cached value or execute `fetcher` |
+| `get(key)` | Read cached value |
+| `set(key, value, maxAgeMs?)` | Store cached value |
+| `del(key)` | Delete cached value |
+
+With custom expiration:
+
+```typescript
+const data = await cache.tryGet(
+ `user:${userId}`,
+ async () => await fetchUser(userId),
+ 5 * 60 * 1000,
+);
+```
+
+## Date Parsing
+
+`date(...)` returns a `Date` object. It supports ISO strings, common date strings, timestamps, relative English text, and Chinese relative dates.
+
+```typescript
+date("2024-01-15");
+date("2024/01/15");
+date("3 days ago");
+date("昨天");
+date(1705312200);
+date("2024-01-15 10:00", +8);
+```
+
+## HTTP Requests: ofetch
+
+Use handler `ofetch` instead of direct `fetch`.
+
+```typescript
+const json = await ofetch("https://api.example.com/data", {
+ responseType: "json",
+});
+
+const html = await ofetch("https://example.com", {
+ responseType: "text",
+});
+
+const data = await ofetch(url, {
+ headers: { Authorization: "Bearer token" },
+ responseType: "json",
+});
+```
+
+Defaults include RSSBook user agent, retry, logging, and timeout behavior.
+
+## HTML Parsing: load
+
+`load` is Cheerio-based.
+
+```typescript
+const html = await ofetch(url, { responseType: "text" });
+const $ = load(html);
+
+const items = $("article")
+ .toArray()
+ .map((el) => {
+ const $el = $(el);
+ return {
+ link: $el.find("a").attr("href"),
+ title: $el.find("h2").text().trim(),
+ };
+ });
+```
+
+Common methods: `$(selector)`, `.text()`, `.html()`, `.attr(name)`, `.find(selector)`, `.toArray()`, `.first()`, `.last()`, `.parent()`, `.children()`.
+
+## HTML Formatting: formatHTML
+
+`formatHTML` sanitizes HTML and can convert relative links to absolute URLs.
+
+```typescript
+const clean = formatHTML(rawHtml);
+const cleanWithUrls = formatHTML(rawHtml, "https://example.com/post");
+```
+
+It removes dangerous tags and attributes while preserving safe content tags, links, images, tables, media, and basic formatting.
+
+## URL Processing: toAbsoluteURL
+
+```typescript
+toAbsoluteURL("/path/to/page", "https://example.com");
+toAbsoluteURL("../image.png", "https://example.com/blog/post");
+toAbsoluteURL("https://other.com/page", "https://example.com");
+```
+
+## Feed Parsing: parse
+
+Use `parse` when the upstream already has RSS, Atom, JSON Feed, or raw structured feed data.
+
+```typescript
+const xml = await ofetch(feedUrl, { responseType: "text" });
+const data = parse(xml);
+
+const dataFromJson = parse(jsonObject, "raw");
+```
+
+## Feed Filtering: filter
+
+```typescript
+import { filter } from "@/utils/feeds";
+
+const filtered = filter(data, {
+ author: {
+ include: ["John"],
+ },
+ categories: {
+ exclude: ["ads"],
+ include: ["tech"],
+ },
+ date: {
+ after: "2024-01-01",
+ before: "2024-12-31",
+ },
+ keywords: {
+ caseSensitive: false,
+ exclude: ["sponsored"],
+ include: ["AI"],
+ },
+ limit: {
+ count: 10,
+ fromStart: true,
+ },
+});
+
+const custom = filter(data, (item, index) => item.title?.includes("important") && index < 20);
+```
+
+## Feed Sorting: sort
+
+```typescript
+import { sort } from "@/utils/feeds";
+
+const newestFirst = sort(data, "date", true);
+const oldestFirst = sort(data, "date", false);
+const byTitle = sort(data, (a, b) => a.title.localeCompare(b.title));
+```
+
+## Feed Merging: union
+
+```typescript
+import { union } from "@/utils/feeds";
+
+const merged = union(feed1, feed2);
+
+const mergedWithMetadata = union(
+ baseFeed,
+ [feed1, feed2, feed3],
+ { title: "Combined Feed" },
+ {
+ hashFn: (item) => item.id || item.link,
+ },
+);
+```
+
+## Feed Intersection: intersection
+
+```typescript
+import { intersection } from "@/utils/feeds";
+
+const common = intersection(feed1, feed2);
+
+const commonWithMetadata = intersection(baseFeed, [feed1, feed2], {
+ title: "Common Articles",
+});
+```
diff --git a/.agents/skills/elysiajs/SKILL.md b/.agents/skills/elysiajs/SKILL.md
new file mode 100644
index 0000000..d707a64
--- /dev/null
+++ b/.agents/skills/elysiajs/SKILL.md
@@ -0,0 +1,475 @@
+---
+name: elysiajs
+description: Create backend with ElysiaJS, a type-safe, high-performance framework.
+---
+
+# ElysiaJS Development Skill
+
+Always consult [elysiajs.com/llms.txt](https://elysiajs.com/llms.txt) for code examples and latest API.
+
+## Overview
+
+ElysiaJS is a TypeScript framework for building Bun-first (but not limited to Bun) type-safe, high-performance backend servers. This skill provides comprehensive guidance for developing with Elysia, including routing, validation, authentication, plugins, integrations, and deployment.
+
+## When to Use This Skill
+
+Trigger this skill when the user asks to:
+- Create or modify ElysiaJS routes, handlers, or servers
+- Setup validation with TypeBox or other schema libraries (Zod, Valibot)
+- Implement authentication (JWT, session-based, macros, guards)
+- Add plugins (CORS, OpenAPI, Static files, JWT)
+- Integrate with external services (Drizzle ORM, Better Auth, Next.js, Eden Treaty)
+- Setup WebSocket endpoints for real-time features
+- Create unit tests for Elysia instances
+- Deploy Elysia servers to production
+
+## Quick Start
+Quick scaffold:
+```bash
+bun create elysia app
+```
+
+### Basic Server
+```typescript
+import { Elysia, t, status } from 'elysia'
+
+const app = new Elysia()
+ .get('/', () => 'Hello World')
+ .post('/user', ({ body }) => body, {
+ body: t.Object({
+ name: t.String(),
+ age: t.Number()
+ })
+ })
+ .get('/id/:id', ({ params: { id } }) => {
+ if(id > 1_000_000) return status(404, 'Not Found')
+
+ return id
+ }, {
+ params: t.Object({
+ id: t.Number({
+ minimum: 1
+ })
+ }),
+ response: {
+ 200: t.Number(),
+ 404: t.Literal('Not Found')
+ }
+ })
+ .listen(3000)
+```
+
+## Basic Usage
+
+### HTTP Methods
+```typescript
+import { Elysia } from 'elysia'
+
+new Elysia()
+ .get('/', 'GET')
+ .post('/', 'POST')
+ .put('/', 'PUT')
+ .patch('/', 'PATCH')
+ .delete('/', 'DELETE')
+ .options('/', 'OPTIONS')
+ .head('/', 'HEAD')
+```
+
+### Path Parameters
+```typescript
+.get('/user/:id', ({ params: { id } }) => id)
+.get('/post/:id/:slug', ({ params }) => params)
+```
+
+### Query Parameters
+```typescript
+.get('/search', ({ query }) => query.q)
+// GET /search?q=elysia → "elysia"
+```
+
+### Request Body
+```typescript
+.post('/user', ({ body }) => body)
+```
+
+### Headers
+```typescript
+.get('/', ({ headers }) => headers.authorization)
+```
+
+## TypeBox Validation
+
+### Basic Types
+```typescript
+import { Elysia, t } from 'elysia'
+
+.post('/user', ({ body }) => body, {
+ body: t.Object({
+ name: t.String(),
+ age: t.Number(),
+ email: t.String({ format: 'email' }),
+ website: t.Optional(t.String({ format: 'uri' }))
+ })
+})
+```
+
+### Nested Objects
+```typescript
+body: t.Object({
+ user: t.Object({
+ name: t.String(),
+ address: t.Object({
+ street: t.String(),
+ city: t.String()
+ })
+ })
+})
+```
+
+### Arrays
+```typescript
+body: t.Object({
+ tags: t.Array(t.String()),
+ users: t.Array(t.Object({
+ id: t.String(),
+ name: t.String()
+ }))
+})
+```
+
+### File Upload
+```typescript
+.post('/upload', ({ body }) => body.file, {
+ body: t.Object({
+ file: t.File({
+ type: 'image', // image/* mime types
+ maxSize: '5m' // 5 megabytes
+ }),
+ files: t.Files({ // Multiple files
+ type: ['image/png', 'image/jpeg']
+ })
+ })
+})
+```
+
+### Response Validation
+```typescript
+.get('/user/:id', ({ params: { id } }) => ({
+ id,
+ name: 'John',
+ email: 'john@example.com'
+}), {
+ params: t.Object({
+ id: t.Number()
+ }),
+ response: {
+ 200: t.Object({
+ id: t.Number(),
+ name: t.String(),
+ email: t.String()
+ }),
+ 404: t.String()
+ }
+})
+```
+
+## Standard Schema (Zod, Valibot, ArkType)
+
+### Zod
+```typescript
+import { z } from 'zod'
+
+.post('/user', ({ body }) => body, {
+ body: z.object({
+ name: z.string(),
+ age: z.number().min(0),
+ email: z.string().email()
+ })
+})
+```
+
+## Error Handling
+
+```typescript
+.get('/user/:id', ({ params: { id }, status }) => {
+ const user = findUser(id)
+
+ if (!user) {
+ return status(404, 'User not found')
+ }
+
+ return user
+})
+```
+
+## Guards (Apply to Multiple Routes)
+
+```typescript
+.guard({
+ params: t.Object({
+ id: t.Number()
+ })
+}, app => app
+ .get('/user/:id', ({ params: { id } }) => id)
+ .delete('/user/:id', ({ params: { id } }) => id)
+)
+```
+
+## Macro
+
+```typescript
+.macro({
+ hi: (word: string) => ({
+ beforeHandle() { console.log(word) }
+ })
+})
+.get('/', () => 'hi', { hi: 'Elysia' })
+```
+
+### Project Structure (Recommended)
+Elysia takes an unopinionated approach but based on user request. But without any specific preference, we recommend a feature-based and domain driven folder structure where each feature has its own folder containing controllers, services, and models.
+
+```
+src/
+├── index.ts # Main server entry
+├── modules/
+│ ├── auth/
+│ │ ├── index.ts # Auth routes (Elysia instance)
+│ │ ├── service.ts # Business logic
+│ │ └── model.ts # TypeBox schemas/DTOs
+│ └── user/
+│ ├── index.ts
+│ ├── service.ts
+│ └── model.ts
+└── plugins/
+ └── custom.ts
+
+public/ # Static files (if using static plugin)
+test/ # Unit tests
+```
+
+Each file has its own responsibility as follows:
+- **Controller (index.ts)**: Handle HTTP routing, request validation, and cookie.
+- **Service (service.ts)**: Handle business logic, decoupled from Elysia controller if possible.
+- **Model (model.ts)**: Define the data structure and validation for the request and response.
+
+## Best Practice
+Elysia is unopinionated on design pattern, but if not provided, we can relies on MVC pattern pair with feature based folder structure.
+
+- Controller:
+ - Prefers Elysia as a controller for HTTP dependant controller
+ - For non HTTP dependent, prefers service instead unless explicitly asked
+ - Use `onError` to handle local custom errors
+ - Register Model to Elysia instance via `Elysia.models({ ...models })` and prefix model by namespace `Elysia.prefix('model', 'Namespace.')
+ - Prefers Reference Model by name provided by Elysia instead of using an actual `Model.name`
+- Service:
+ - Prefers class (or abstract class if possible)
+ - Prefers interface/type derive from `Model`
+ - Return `status` (`import { status } from 'elysia'`) for error
+ - Prefers `return Error` instead of `throw Error`
+- Models:
+ - Always export validation model and type of validation model
+ - Custom Error should be in contains in Model
+
+## Elysia Key Concept
+Elysia has a every important concepts/rules to understand before use.
+
+## Encapsulation - Isolates by Default
+
+Lifecycles (hooks, middleware) **don't leak** between instances unless scoped.
+
+**Scope levels:**
+- `local` (default) - current instance + descendants
+- `scoped` - parent + current + descendants
+- `global` - all instances
+
+```ts
+.onBeforeHandle(() => {}) // only local instance
+.onBeforeHandle({ as: 'global' }, () => {}) // exports to all
+```
+
+## Method Chaining - Required for Types
+
+**Must chain**. Each method returns new type reference.
+
+❌ Don't:
+```ts
+const app = new Elysia()
+app.state('build', 1) // loses type
+app.get('/', ({ store }) => store.build) // build doesn't exists
+```
+
+✅ Do:
+```ts
+new Elysia()
+ .state('build', 1)
+ .get('/', ({ store }) => store.build)
+```
+
+## Explicit Dependencies
+
+Each instance independent. **Declare what you use.**
+
+```ts
+const auth = new Elysia()
+ .decorate('Auth', Auth)
+ .model(Auth.models)
+
+new Elysia()
+ .get('/', ({ Auth }) => Auth.getProfile()) // Auth doesn't exists
+
+new Elysia()
+ .use(auth) // must declare
+ .get('/', ({ Auth }) => Auth.getProfile())
+```
+
+**Global scope when:**
+- No types added (cors, helmet)
+- Global lifecycle (logging, tracing)
+
+**Explicit when:**
+- Adds types (state, models)
+- Business logic (auth, db)
+
+## Deduplication
+
+Plugins re-execute unless named:
+
+```ts
+new Elysia() // rerun on `.use`
+new Elysia({ name: 'ip' }) // runs once across all instances
+```
+
+## Order Matters
+
+Events apply to routes **registered after** them.
+
+```ts
+.onBeforeHandle(() => console.log('1'))
+.get('/', () => 'hi') // has hook
+.onBeforeHandle(() => console.log('2')) // doesn't affect '/'
+```
+
+## Type Inference
+
+**Inline functions only** for accurate types.
+
+For controllers, destructure in inline wrapper:
+
+```ts
+.post('/', ({ body }) => Controller.greet(body), {
+ body: t.Object({ name: t.String() })
+})
+```
+
+Get type from schema:
+```ts
+type MyType = typeof MyType.static
+```
+
+## Reference Model
+Model can be reference by name, especially great for documenting an API
+```ts
+new Elysia()
+ .model({
+ book: t.Object({
+ name: t.String()
+ })
+ })
+ .post('/', ({ body }) => body.name, {
+ body: 'book'
+ })
+```
+
+Model can be renamed by using `.prefix` / `.suffix`
+```ts
+new Elysia()
+ .model({
+ book: t.Object({
+ name: t.String()
+ })
+ })
+ .prefix('model', 'Namespace')
+ .post('/', ({ body }) => body.name, {
+ body: 'Namespace.Book'
+ })
+```
+
+Once `prefix`, model name will be capitalized by default.
+
+## Technical Terms
+The following are technical terms that is use for Elysia:
+- `OpenAPI Type Gen` - function name `fromTypes` from `@elysiajs/openapi` for generating OpenAPI from types, see `plugins/openapi.md`
+- `Eden`, `Eden Treaty` - e2e type safe RPC client for share type from backend to frontend
+
+## Resources
+Use the following references as needed.
+
+It's recommended to checkout `route.md` for as it contains the most important foundation building blocks with examples.
+
+`plugin.md` and `validation.md` is important as well but can be check as needed.
+
+### references/
+Detailed documentation split by topic:
+- `bun-fullstack-dev-server.md` - Bun Fullstack Dev Server with HMR. React without bundler.
+- `cookie.md` - Detailed documentation on cookie
+- `deployment.md` - Production deployment guide / Docker
+- `eden.md` - e2e type safe RPC client for share type from backend to frontend
+- `guard.md` - Setting validation/lifecycle all at once
+- `macro.md` - Compose multiple schema/lifecycle as a reusable Elysia via key-value (recommended for complex setup, eg. authentication, authorization, Role-based Access Check)
+- `plugin.md` - Decouple part of Elysia into a standalone component
+- `route.md` - Elysia foundation building block: Routing, Handler and Context
+- `testing.md` - Unit tests with examples
+- `validation.md` - Setup input/output validation and list of all custom validation rules
+- `websocket.md` - Real-time features
+
+### plugins/
+Detailed documentation, usage and configuration reference for official Elysia plugin:
+- `bearer.md` - Add bearer capability to Elysia (`@elysiajs/bearer`)
+- `cors.md` - Out of box configuration for CORS (`@elysiajs/cors`)
+- `cron.md` - Run cron job with access to Elysia context (`@elysiajs/cron`)
+- `graphql-apollo.md` - Integration GraphQL Apollo (`@elysiajs/graphql-apollo`)
+- `graphql-yoga.md` - Integration with GraphQL Yoga (`@elysiajs/graphql-yoga`)
+- `html.md` - HTML and JSX plugin setup and usage (`@elysiajs/html`)
+- `jwt.md` - JWT / JWK plugin (`@elysiajs/jwt`)
+- `openapi.md` - OpenAPI documentation and OpenAPI Type Gen / OpenAPI from types (`@elysiajs/openapi`)
+- `opentelemetry.md` - OpenTelemetry, instrumentation, and record span utilities (`@elysiajs/opentelemetry`)
+- `server-timing.md` - Server Timing metric for debug (`@elysiajs/server-timing`)
+- `static.md` - Serve static files/folders for Elysia Server (`@elysiajs/static`)
+
+### integrations/
+Guide to integrate Elysia with external library/runtime:
+- `ai-sdk.md` - Using Vercel AI SDK with Elysia
+- `astro.md` - Elysia in Astro API route
+- `better-auth.md` - Integrate Elysia with better-auth
+- `cloudflare-worker.md` - Elysia on Cloudflare Worker adapter
+- `deno.md` - Elysia on Deno
+- `drizzle.md` - Integrate Elysia with Drizzle ORM
+- `expo.md` - Elysia in Expo API route
+- `nextjs.md` - Elysia in Nextjs API route
+- `nodejs.md` - Run Elysia on Node.js
+- `nuxt.md` - Elysia on API route
+- `prisma.md` - Integrate Elysia with Prisma
+- `react-email.d` - Create and Send Email with React and Elysia
+- `sveltekit.md` - Run Elysia on Svelte Kit API route
+- `tanstack-start.md` - Run Elysia on Tanstack Start / React Query
+- `vercel.md` - Deploy Elysia to Vercel
+
+### examples/ (optional)
+- `basic.ts` - Basic Elysia example
+- `body-parser.ts` - Custom body parser example via `.onParse`
+- `complex.ts` - Comprehensive usage of Elysia server
+- `cookie.ts` - Setting cookie
+- `error.ts` - Error handling
+- `file.ts` - Returning local file from server
+- `guard.ts` - Setting mulitple validation schema and lifecycle
+- `map-response.ts` - Custom response mapper
+- `redirect.ts` - Redirect response
+- `rename.ts` - Rename context's property
+- `schema.ts` - Setup validation
+- `state.ts` - Setup global state
+- `upload-file.ts` - File upload with validation
+- `websocket.ts` - Web Socket for realtime communication
+
+### patterns/ (optional)
+- `patterns/mvc.md` - Detail guideline for using Elysia with MVC patterns
diff --git a/.agents/skills/elysiajs/examples/basic.ts b/.agents/skills/elysiajs/examples/basic.ts
new file mode 100644
index 0000000..61c8d14
--- /dev/null
+++ b/.agents/skills/elysiajs/examples/basic.ts
@@ -0,0 +1,9 @@
+import { Elysia, t } from 'elysia'
+
+new Elysia()
+ .get('/', 'Hello Elysia')
+ .post('/', ({ body: { name } }) => name, {
+ body: t.Object({
+ name: t.String()
+ })
+ })
diff --git a/.agents/skills/elysiajs/examples/body-parser.ts b/.agents/skills/elysiajs/examples/body-parser.ts
new file mode 100644
index 0000000..533c7bf
--- /dev/null
+++ b/.agents/skills/elysiajs/examples/body-parser.ts
@@ -0,0 +1,33 @@
+import { Elysia, t } from 'elysia'
+
+const app = new Elysia()
+ // Add custom body parser
+ .onParse(async ({ request, contentType }) => {
+ switch (contentType) {
+ case 'application/Elysia':
+ return request.text()
+ }
+ })
+ .post('/', ({ body: { username } }) => `Hi ${username}`, {
+ body: t.Object({
+ id: t.Number(),
+ username: t.String()
+ })
+ })
+ // Increase id by 1 from body before main handler
+ .post('/transform', ({ body }) => body, {
+ transform: ({ body }) => {
+ body.id = body.id + 1
+ },
+ body: t.Object({
+ id: t.Number(),
+ username: t.String()
+ }),
+ detail: {
+ summary: 'A'
+ }
+ })
+ .post('/mirror', ({ body }) => body)
+ .listen(3000)
+
+console.log('🦊 Elysia is running at :8080')
diff --git a/.agents/skills/elysiajs/examples/complex.ts b/.agents/skills/elysiajs/examples/complex.ts
new file mode 100644
index 0000000..436eda0
--- /dev/null
+++ b/.agents/skills/elysiajs/examples/complex.ts
@@ -0,0 +1,112 @@
+import { Elysia, t, file } from 'elysia'
+
+const loggerPlugin = new Elysia()
+ .get('/hi', () => 'Hi')
+ .decorate('log', () => 'A')
+ .decorate('date', () => new Date())
+ .state('fromPlugin', 'From Logger')
+ .use((app) => app.state('abc', 'abc'))
+
+const app = new Elysia()
+ .onRequest(({ set }) => {
+ set.headers = {
+ 'Access-Control-Allow-Origin': '*'
+ }
+ })
+ .onError(({ code }) => {
+ if (code === 'NOT_FOUND')
+ return 'Not Found :('
+ })
+ .use(loggerPlugin)
+ .state('build', Date.now())
+ .get('/', 'Elysia')
+ .get('/tako', file('./example/takodachi.png'))
+ .get('/json', () => ({
+ hi: 'world'
+ }))
+ .get('/root/plugin/log', ({ log, store: { build } }) => {
+ log()
+
+ return build
+ })
+ .get('/wildcard/*', () => 'Hi Wildcard')
+ .get('/query', () => 'Elysia', {
+ beforeHandle: ({ query }) => {
+ console.log('Name:', query?.name)
+
+ if (query?.name === 'aom') return 'Hi saltyaom'
+ },
+ query: t.Object({
+ name: t.String()
+ })
+ })
+ .post('/json', async ({ body }) => body, {
+ body: t.Object({
+ name: t.String(),
+ additional: t.String()
+ })
+ })
+ .post('/transform-body', async ({ body }) => body, {
+ beforeHandle: (ctx) => {
+ ctx.body = {
+ ...ctx.body,
+ additional: 'Elysia'
+ }
+ },
+ body: t.Object({
+ name: t.String(),
+ additional: t.String()
+ })
+ })
+ .get('/id/:id', ({ params: { id } }) => id, {
+ transform({ params }) {
+ params.id = +params.id
+ },
+ params: t.Object({
+ id: t.Number()
+ })
+ })
+ .post('/new/:id', async ({ body, params }) => body, {
+ params: t.Object({
+ id: t.Number()
+ }),
+ body: t.Object({
+ username: t.String()
+ })
+ })
+ .get('/trailing-slash', () => 'A')
+ .group('/group', (app) =>
+ app
+ .onBeforeHandle(({ query }) => {
+ if (query?.name === 'aom') return 'Hi saltyaom'
+ })
+ .get('/', () => 'From Group')
+ .get('/hi', () => 'HI GROUP')
+ .get('/elysia', () => 'Welcome to Elysian Realm')
+ .get('/fbk', () => 'FuBuKing')
+ )
+ .get('/response-header', ({ set }) => {
+ set.status = 404
+ set.headers['a'] = 'b'
+
+ return 'A'
+ })
+ .get('/this/is/my/deep/nested/root', () => 'Hi')
+ .get('/build', ({ store: { build } }) => build)
+ .get('/ref', ({ date }) => date())
+ .get('/response', () => new Response('Hi'))
+ .get('/error', () => new Error('Something went wrong'))
+ .get('/401', ({ set }) => {
+ set.status = 401
+
+ return 'Status should be 401'
+ })
+ .get('/timeout', async () => {
+ await new Promise((resolve) => setTimeout(resolve, 2000))
+
+ return 'A'
+ })
+ .all('/all', () => 'hi')
+ .listen(8080, ({ hostname, port }) => {
+ console.log(`🦊 Elysia is running at http://${hostname}:${port}`)
+ })
diff --git a/.agents/skills/elysiajs/examples/cookie.ts b/.agents/skills/elysiajs/examples/cookie.ts
new file mode 100644
index 0000000..9a42720
--- /dev/null
+++ b/.agents/skills/elysiajs/examples/cookie.ts
@@ -0,0 +1,45 @@
+import { Elysia, t } from 'elysia'
+
+const app = new Elysia({
+ cookie: {
+ secrets: 'Fischl von Luftschloss Narfidort',
+ sign: ['name']
+ }
+})
+ .get(
+ '/council',
+ ({ cookie: { council } }) =>
+ (council.value = [
+ {
+ name: 'Rin',
+ affilation: 'Administration'
+ }
+ ]),
+ {
+ cookie: t.Cookie({
+ council: t.Array(
+ t.Object({
+ name: t.String(),
+ affilation: t.String()
+ })
+ )
+ })
+ }
+ )
+ .get('/create', ({ cookie: { name } }) => (name.value = 'Himari'))
+ .get(
+ '/update',
+ ({ cookie: { name } }) => {
+ name.value = 'seminar: Rio'
+ name.value = 'seminar: Himari'
+ name.maxAge = 86400
+
+ return name.value
+ },
+ {
+ cookie: t.Cookie({
+ name: t.Optional(t.String())
+ })
+ }
+ )
+ .listen(3000)
diff --git a/.agents/skills/elysiajs/examples/error.ts b/.agents/skills/elysiajs/examples/error.ts
new file mode 100644
index 0000000..2c2f126
--- /dev/null
+++ b/.agents/skills/elysiajs/examples/error.ts
@@ -0,0 +1,38 @@
+import { Elysia, t } from 'elysia'
+
+class CustomError extends Error {
+ constructor(public name: string) {
+ super(name)
+ }
+}
+
+new Elysia()
+ .error({
+ CUSTOM_ERROR: CustomError
+ })
+ // global handler
+ .onError(({ code, error, status }) => {
+ switch (code) {
+ case "CUSTOM_ERROR":
+ return status(401, { message: error.message })
+
+ case "NOT_FOUND":
+ return "Not found :("
+ }
+ })
+ .post('/', ({ body }) => body, {
+ body: t.Object({
+ username: t.String(),
+ password: t.String(),
+ nested: t.Optional(
+ t.Object({
+ hi: t.String()
+ })
+ )
+ }),
+ // local handler
+ error({ error }) {
+ console.log(error)
+ }
+ })
+ .listen(3000)
diff --git a/.agents/skills/elysiajs/examples/file.ts b/.agents/skills/elysiajs/examples/file.ts
new file mode 100644
index 0000000..504cad7
--- /dev/null
+++ b/.agents/skills/elysiajs/examples/file.ts
@@ -0,0 +1,10 @@
+import { Elysia, file } from 'elysia'
+
+/**
+ * Example of handle single static file
+ *
+ * @see https://github.com/elysiajs/elysia-static
+ */
+new Elysia()
+ .get('/tako', file('./example/takodachi.png'))
+ .listen(3000)
diff --git a/.agents/skills/elysiajs/examples/guard.ts b/.agents/skills/elysiajs/examples/guard.ts
new file mode 100644
index 0000000..2fe158f
--- /dev/null
+++ b/.agents/skills/elysiajs/examples/guard.ts
@@ -0,0 +1,34 @@
+import { Elysia, t } from 'elysia'
+
+new Elysia()
+ .state('name', 'salt')
+ .get('/', ({ store: { name } }) => `Hi ${name}`, {
+ query: t.Object({
+ name: t.String()
+ })
+ })
+ // If query 'name' is not preset, skip the whole handler
+ .guard(
+ {
+ query: t.Object({
+ name: t.String()
+ })
+ },
+ (app) =>
+ app
+ // Query type is inherited from guard
+ .get('/profile', ({ query }) => `Hi`)
+ // Store is inherited
+ .post('/name', ({ store: { name }, body, query }) => name, {
+ body: t.Object({
+ id: t.Number({
+ minimum: 5
+ }),
+ username: t.String(),
+ profile: t.Object({
+ name: t.String()
+ })
+ })
+ })
+ )
+ .listen(3000)
diff --git a/.agents/skills/elysiajs/examples/map-response.ts b/.agents/skills/elysiajs/examples/map-response.ts
new file mode 100644
index 0000000..8cd4be4
--- /dev/null
+++ b/.agents/skills/elysiajs/examples/map-response.ts
@@ -0,0 +1,15 @@
+import { Elysia } from 'elysia'
+
+const prettyJson = new Elysia()
+ .mapResponse(({ response }) => {
+ if (response instanceof Object)
+ return new Response(JSON.stringify(response, null, 4))
+ })
+ .as('scoped')
+
+new Elysia()
+ .use(prettyJson)
+ .get('/', () => ({
+ hello: 'world'
+ }))
+ .listen(3000)
diff --git a/.agents/skills/elysiajs/examples/redirect.ts b/.agents/skills/elysiajs/examples/redirect.ts
new file mode 100644
index 0000000..28171b0
--- /dev/null
+++ b/.agents/skills/elysiajs/examples/redirect.ts
@@ -0,0 +1,6 @@
+import { Elysia } from 'elysia'
+
+new Elysia()
+ .get('/', () => 'Hi')
+ .get('/redirect', ({ redirect }) => redirect('/'))
+ .listen(3000)
diff --git a/.agents/skills/elysiajs/examples/rename.ts b/.agents/skills/elysiajs/examples/rename.ts
new file mode 100644
index 0000000..361f06f
--- /dev/null
+++ b/.agents/skills/elysiajs/examples/rename.ts
@@ -0,0 +1,32 @@
+import { Elysia, t } from 'elysia'
+
+// ? Elysia#83 | Proposal: Standardized way of renaming third party plugin-scoped stuff
+// this would be a plugin provided by a third party
+const myPlugin = new Elysia()
+ .decorate('myProperty', 42)
+ .model('salt', t.String())
+
+new Elysia()
+ .use(
+ myPlugin
+ // map decorator, rename "myProperty" to "renamedProperty"
+ .decorate(({ myProperty, ...decorators }) => ({
+ renamedProperty: myProperty,
+ ...decorators
+ }))
+ // map model, rename "salt" to "pepper"
+ .model(({ salt, ...models }) => ({
+ ...models,
+ pepper: t.String()
+ }))
+ // Add prefix
+ .prefix('decorator', 'unstable')
+ )
+ .get(
+ '/mapped',
+ ({ unstableRenamedProperty }) => unstableRenamedProperty
+ )
+ .post('/pepper', ({ body }) => body, {
+ body: 'pepper',
+ // response: t.String()
+ })
diff --git a/.agents/skills/elysiajs/examples/schema.ts b/.agents/skills/elysiajs/examples/schema.ts
new file mode 100644
index 0000000..db79300
--- /dev/null
+++ b/.agents/skills/elysiajs/examples/schema.ts
@@ -0,0 +1,61 @@
+import { Elysia, t } from 'elysia'
+
+const app = new Elysia()
+ .model({
+ name: t.Object({
+ name: t.String()
+ }),
+ b: t.Object({
+ response: t.Number()
+ }),
+ authorization: t.Object({
+ authorization: t.String()
+ })
+ })
+ // Strictly validate response
+ .get('/', () => 'hi')
+ // Strictly validate body and response
+ .post('/', ({ body, query }) => body.id, {
+ body: t.Object({
+ id: t.Number(),
+ username: t.String(),
+ profile: t.Object({
+ name: t.String()
+ })
+ })
+ })
+ // Strictly validate query, params, and body
+ .get('/query/:id', ({ query: { name }, params }) => name, {
+ query: t.Object({
+ name: t.String()
+ }),
+ params: t.Object({
+ id: t.String()
+ }),
+ response: {
+ 200: t.String(),
+ 300: t.Object({
+ error: t.String()
+ })
+ }
+ })
+ .guard(
+ {
+ headers: 'authorization'
+ },
+ (app) =>
+ app
+ .derive(({ headers }) => ({
+ userId: headers.authorization
+ }))
+ .get('/', ({ userId }) => 'A')
+ .post('/id/:id', ({ query, body, params, userId }) => body, {
+ params: t.Object({
+ id: t.Number()
+ }),
+ transform({ params }) {
+ params.id = +params.id
+ }
+ })
+ )
+ .listen(3000)
diff --git a/.agents/skills/elysiajs/examples/state.ts b/.agents/skills/elysiajs/examples/state.ts
new file mode 100644
index 0000000..8bcc993
--- /dev/null
+++ b/.agents/skills/elysiajs/examples/state.ts
@@ -0,0 +1,6 @@
+import { Elysia } from 'elysia'
+
+new Elysia()
+ .state('counter', 0)
+ .get('/', ({ store }) => store.counter++)
+ .listen(3000)
diff --git a/.agents/skills/elysiajs/examples/upload-file.ts b/.agents/skills/elysiajs/examples/upload-file.ts
new file mode 100644
index 0000000..4af5a19
--- /dev/null
+++ b/.agents/skills/elysiajs/examples/upload-file.ts
@@ -0,0 +1,20 @@
+import { Elysia, t } from 'elysia'
+
+const app = new Elysia()
+ .post('/single', ({ body: { file } }) => file, {
+ body: t.Object({
+ file: t.File({
+ maxSize: '1m'
+ })
+ })
+ })
+ .post(
+ '/multiple',
+ ({ body: { files } }) => files.reduce((a, b) => a + b.size, 0),
+ {
+ body: t.Object({
+ files: t.Files()
+ })
+ }
+ )
+ .listen(3000)
diff --git a/.agents/skills/elysiajs/examples/websocket.ts b/.agents/skills/elysiajs/examples/websocket.ts
new file mode 100644
index 0000000..f97e47b
--- /dev/null
+++ b/.agents/skills/elysiajs/examples/websocket.ts
@@ -0,0 +1,25 @@
+import { Elysia } from 'elysia'
+
+const app = new Elysia()
+ .state('start', 'here')
+ .ws('/ws', {
+ open(ws) {
+ ws.subscribe('asdf')
+ console.log('Open Connection:', ws.id)
+ },
+ close(ws) {
+ console.log('Closed Connection:', ws.id)
+ },
+ message(ws, message) {
+ ws.publish('asdf', message)
+ ws.send(message)
+ }
+ })
+ .get('/publish/:publish', ({ params: { publish: text } }) => {
+ app.server!.publish('asdf', text)
+
+ return text
+ })
+ .listen(3000, (server) => {
+ console.log(`http://${server.hostname}:${server.port}`)
+ })
diff --git a/.agents/skills/elysiajs/integrations/ai-sdk.md b/.agents/skills/elysiajs/integrations/ai-sdk.md
new file mode 100644
index 0000000..99f5409
--- /dev/null
+++ b/.agents/skills/elysiajs/integrations/ai-sdk.md
@@ -0,0 +1,92 @@
+# AI SDK Integration
+
+## What It Is
+Seamless integration with Vercel AI SDK via response streaming.
+
+## Response Streaming
+Return `ReadableStream` or `Response` directly:
+```typescript
+import { streamText } from 'ai'
+import { openai } from '@ai-sdk/openai'
+
+new Elysia().get('/', () => {
+ const stream = streamText({
+ model: openai('gpt-5'),
+ system: 'You are Yae Miko from Genshin Impact',
+ prompt: 'Hi! How are you doing?'
+ })
+
+ return stream.textStream // ReadableStream
+ // or
+ return stream.toUIMessageStream() // UI Message Stream
+})
+```
+
+Elysia auto-handles stream.
+
+## Server-Sent Events
+Wrap `ReadableStream` with `sse`:
+```typescript
+import { sse } from 'elysia'
+
+.get('/', () => {
+ const stream = streamText({ /* ... */ })
+
+ return sse(stream.textStream)
+ // or
+ return sse(stream.toUIMessageStream())
+})
+```
+
+Each chunk → SSE.
+
+## As Response
+Return stream directly (no Eden type safety):
+```typescript
+.get('/', () => {
+ const stream = streamText({ /* ... */ })
+
+ return stream.toTextStreamResponse()
+ // or
+ return stream.toUIMessageStreamResponse() // Uses SSE
+})
+```
+
+## Manual Streaming
+Generator function for control:
+```typescript
+import { sse } from 'elysia'
+
+.get('/', async function* () {
+ const stream = streamText({ /* ... */ })
+
+ for await (const data of stream.textStream)
+ yield sse({ data, event: 'message' })
+
+ yield sse({ event: 'done' })
+})
+```
+
+## Fetch for Unsupported Models
+Direct fetch with streaming proxy:
+```typescript
+.get('/', () => {
+ return fetch('https://api.openai.com/v1/chat/completions', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: `Bearer ${process.env.OPENAI_API_KEY}`
+ },
+ body: JSON.stringify({
+ model: 'gpt-5',
+ stream: true,
+ messages: [
+ { role: 'system', content: 'You are Yae Miko' },
+ { role: 'user', content: 'Hi! How are you doing?' }
+ ]
+ })
+ })
+})
+```
+
+Elysia auto-proxies fetch response with streaming.
diff --git a/.agents/skills/elysiajs/integrations/astro.md b/.agents/skills/elysiajs/integrations/astro.md
new file mode 100644
index 0000000..41cd451
--- /dev/null
+++ b/.agents/skills/elysiajs/integrations/astro.md
@@ -0,0 +1,59 @@
+# Astro Integration - SKILLS.md
+
+## What It Is
+Run Elysia on Astro via Astro Endpoint.
+
+## Setup
+1. Set output to server:
+```javascript
+// astro.config.mjs
+export default defineConfig({
+ output: 'server'
+})
+```
+
+2. Create `pages/[...slugs].ts`
+3. Define Elysia server + export handlers:
+```typescript
+// pages/[...slugs].ts
+import { Elysia, t } from 'elysia'
+
+const app = new Elysia()
+ .get('/api', () => 'hi')
+ .post('/api', ({ body }) => body, {
+ body: t.Object({ name: t.String() })
+ })
+
+const handle = ({ request }: { request: Request }) => app.handle(request)
+
+export const GET = handle
+export const POST = handle
+```
+
+WinterCG compliance - works normally.
+
+Recommended: Run Astro on Bun (Elysia designed for Bun).
+
+## Prefix for Non-Root
+If placed in `pages/api/[...slugs].ts`, set prefix:
+```typescript
+// pages/api/[...slugs].ts
+const app = new Elysia({ prefix: '/api' })
+ .get('/', () => 'hi')
+
+const handle = ({ request }: { request: Request }) => app.handle(request)
+
+export const GET = handle
+export const POST = handle
+```
+
+Ensures routing works in any location.
+
+## Benefits
+Co-location of frontend + backend. End-to-end type safety with Eden.
+
+## pnpm
+Manual install:
+```bash
+pnpm add @sinclair/typebox openapi-types
+```
diff --git a/.agents/skills/elysiajs/integrations/better-auth.md b/.agents/skills/elysiajs/integrations/better-auth.md
new file mode 100644
index 0000000..0dfa3af
--- /dev/null
+++ b/.agents/skills/elysiajs/integrations/better-auth.md
@@ -0,0 +1,117 @@
+# Better Auth Integration
+Elysia + Better Auth integration guide
+
+## What It Is
+Framework-agnostic TypeScript auth/authz. Comprehensive features + plugin ecosystem.
+
+## Setup
+```typescript
+import { betterAuth } from 'better-auth'
+import { Pool } from 'pg'
+
+export const auth = betterAuth({
+ database: new Pool()
+})
+```
+
+## Handler Mounting
+```typescript
+import { auth } from './auth'
+
+new Elysia()
+ .mount(auth.handler) // http://localhost:3000/api/auth
+ .listen(3000)
+```
+
+### Custom Endpoint
+```typescript
+// Mount with prefix
+.mount('/auth', auth.handler) // http://localhost:3000/auth/api/auth
+
+// Customize basePath
+export const auth = betterAuth({
+ basePath: '/api' // http://localhost:3000/auth/api
+})
+```
+
+Cannot set `basePath` to empty or `/`.
+
+## OpenAPI Integration
+Extract docs from Better Auth:
+```typescript
+import { openAPI } from 'better-auth/plugins'
+
+let _schema: ReturnType
+const getSchema = async () => (_schema ??= auth.api.generateOpenAPISchema())
+
+export const OpenAPI = {
+ getPaths: (prefix = '/auth/api') =>
+ getSchema().then(({ paths }) => {
+ const reference: typeof paths = Object.create(null)
+
+ for (const path of Object.keys(paths)) {
+ const key = prefix + path
+ reference[key] = paths[path]
+
+ for (const method of Object.keys(paths[path])) {
+ const operation = (reference[key] as any)[method]
+ operation.tags = ['Better Auth']
+ }
+ }
+
+ return reference
+ }) as Promise,
+ components: getSchema().then(({ components }) => components) as Promise
+} as const
+```
+
+Apply to Elysia:
+```typescript
+new Elysia().use(openapi({
+ documentation: {
+ components: await OpenAPI.components,
+ paths: await OpenAPI.getPaths()
+ }
+}))
+```
+
+## CORS
+```typescript
+import { cors } from '@elysiajs/cors'
+
+new Elysia()
+ .use(cors({
+ origin: 'http://localhost:3001',
+ methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
+ credentials: true,
+ allowedHeaders: ['Content-Type', 'Authorization']
+ }))
+ .mount(auth.handler)
+```
+
+## Macro for Auth
+Use macro + resolve for session/user:
+```typescript
+const betterAuth = new Elysia({ name: 'better-auth' })
+ .mount(auth.handler)
+ .macro({
+ auth: {
+ async resolve({ status, request: { headers } }) {
+ const session = await auth.api.getSession({ headers })
+
+ if (!session) return status(401)
+
+ return {
+ user: session.user,
+ session: session.session
+ }
+ }
+ }
+ })
+
+new Elysia()
+ .use(betterAuth)
+ .get('/user', ({ user }) => user, { auth: true })
+```
+
+Access `user` and `session` in all routes.
diff --git a/.agents/skills/elysiajs/integrations/cloudflare-worker.md b/.agents/skills/elysiajs/integrations/cloudflare-worker.md
new file mode 100644
index 0000000..4245c1a
--- /dev/null
+++ b/.agents/skills/elysiajs/integrations/cloudflare-worker.md
@@ -0,0 +1,95 @@
+
+# Cloudflare Worker Integration
+
+## What It Is
+**Experimental** Cloudflare Worker adapter for Elysia.
+
+## Setup
+1. Install Wrangler:
+```bash
+wrangler init elysia-on-cloudflare
+```
+
+2. Apply adapter + compile:
+```typescript
+import { Elysia } from 'elysia'
+import { CloudflareAdapter } from 'elysia/adapter/cloudflare-worker'
+
+export default new Elysia({
+ adapter: CloudflareAdapter
+})
+ .get('/', () => 'Hello Cloudflare Worker!')
+ .compile() // Required
+```
+
+3. Set compatibility date (min `2025-06-01`):
+```json
+// wrangler.json
+{
+ "name": "elysia-on-cloudflare",
+ "main": "src/index.ts",
+ "compatibility_date": "2025-06-01"
+}
+```
+
+4. Dev server:
+```bash
+wrangler dev
+# http://localhost:8787
+```
+
+No `nodejs_compat` flag needed.
+
+## Limitations
+1. `Elysia.file` + Static Plugin don't work (no `fs` module)
+2. OpenAPI Type Gen doesn't work (no `fs` module)
+3. Cannot define Response before server start
+4. Cannot inline values:
+```typescript
+// ❌ Throws error
+.get('/', 'Hello Elysia')
+
+// ✅ Works
+.get('/', () => 'Hello Elysia')
+```
+
+## Static Files
+Use Cloudflare's built-in static serving:
+```json
+// wrangler.json
+{
+ "assets": { "directory": "public" }
+}
+```
+
+Structure:
+```
+├─ public
+│ ├─ kyuukurarin.mp4
+│ └─ static/mika.webp
+```
+
+Access:
+- `http://localhost:8787/kyuukurarin.mp4`
+- `http://localhost:8787/static/mika.webp`
+
+## Binding
+Import env from `cloudflare:workers`:
+```typescript
+import { env } from 'cloudflare:workers'
+
+export default new Elysia({ adapter: CloudflareAdapter })
+ .get('/', () => `Hello ${await env.KV.get('my-key')}`)
+ .compile()
+```
+
+## AoT Compilation
+As of Elysia 1.4.7, AoT works with Cloudflare Worker. Drop `aot: false` flag.
+
+Cloudflare now supports Function compilation during startup.
+
+## pnpm
+Manual install:
+```bash
+pnpm add @sinclair/typebox openapi-types
+```
diff --git a/.agents/skills/elysiajs/integrations/deno.md b/.agents/skills/elysiajs/integrations/deno.md
new file mode 100644
index 0000000..28687d5
--- /dev/null
+++ b/.agents/skills/elysiajs/integrations/deno.md
@@ -0,0 +1,34 @@
+# Deno Integration
+Run Elysia on Deno
+
+## What It Is
+Run Elysia on Deno via Web Standard Request/Response.
+
+## Setup
+Wrap `Elysia.fetch` in `Deno.serve`:
+```typescript
+import { Elysia } from 'elysia'
+
+const app = new Elysia()
+ .get('/', () => 'Hello Elysia')
+ .listen(3000)
+
+Deno.serve(app.fetch)
+```
+
+Run:
+```bash
+deno serve --watch src/index.ts
+```
+
+## Port Config
+```typescript
+Deno.serve(app.fetch) // Default
+Deno.serve({ port: 8787 }, app.fetch) // Custom port
+```
+
+## pnpm
+[Inference] pnpm doesn't auto-install peer deps. Manual install required:
+```bash
+pnpm add @sinclair/typebox openapi-types
+```
diff --git a/.agents/skills/elysiajs/integrations/drizzle.md b/.agents/skills/elysiajs/integrations/drizzle.md
new file mode 100644
index 0000000..779db4e
--- /dev/null
+++ b/.agents/skills/elysiajs/integrations/drizzle.md
@@ -0,0 +1,258 @@
+# Drizzle Integration
+Elysia + Drizzle integration guide
+
+## What It Is
+Headless TypeScript ORM. Convert Drizzle schema → Elysia validation models via `drizzle-typebox`.
+
+## Flow
+```
+Drizzle → drizzle-typebox → Elysia validation → OpenAPI + Eden Treaty
+```
+
+## Installation
+```bash
+bun add drizzle-orm drizzle-typebox
+```
+
+### Pin TypeBox Version
+Prevent Symbol conflicts:
+```bash
+grep "@sinclair/typebox" node_modules/elysia/package.json
+```
+
+Add to `package.json`:
+```json
+{
+ "overrides": {
+ "@sinclair/typebox": "0.32.4"
+ }
+}
+```
+
+## Drizzle Schema
+```typescript
+// src/database/schema.ts
+import { pgTable, varchar, timestamp } from 'drizzle-orm/pg-core'
+import { createId } from '@paralleldrive/cuid2'
+
+export const user = pgTable('user', {
+ id: varchar('id').$defaultFn(() => createId()).primaryKey(),
+ username: varchar('username').notNull().unique(),
+ password: varchar('password').notNull(),
+ email: varchar('email').notNull().unique(),
+ salt: varchar('salt', { length: 64 }).notNull(),
+ createdAt: timestamp('created_at').defaultNow().notNull()
+})
+
+export const table = { user } as const
+export type Table = typeof table
+```
+
+## drizzle-typebox
+```typescript
+import { t } from 'elysia'
+import { createInsertSchema } from 'drizzle-typebox'
+import { table } from './database/schema'
+
+const _createUser = createInsertSchema(table.user, {
+ email: t.String({ format: 'email' }) // Replace with Elysia type
+})
+
+new Elysia()
+ .post('/sign-up', ({ body }) => {}, {
+ body: t.Omit(_createUser, ['id', 'salt', 'createdAt'])
+ })
+```
+
+## Type Instantiation Error
+**Error**: "Type instantiation is possibly infinite"
+
+**Cause**: Circular reference when nesting drizzle-typebox into Elysia schema.
+
+**Fix**: Explicitly define type between them:
+```typescript
+// ✅ Works
+const _createUser = createInsertSchema(table.user, {
+ email: t.String({ format: 'email' })
+})
+const createUser = t.Omit(_createUser, ['id', 'salt', 'createdAt'])
+
+// ❌ Infinite loop
+const createUser = t.Omit(
+ createInsertSchema(table.user, { email: t.String({ format: 'email' }) }),
+ ['id', 'salt', 'createdAt']
+)
+```
+
+Always declare variable for drizzle-typebox then reference it.
+
+## Utility Functions
+Copy as-is for simplified usage:
+```typescript
+// src/database/utils.ts
+/**
+ * @lastModified 2025-02-04
+ * @see https://elysiajs.com/recipe/drizzle.html#utility
+ */
+
+import { Kind, type TObject } from '@sinclair/typebox'
+import {
+ createInsertSchema,
+ createSelectSchema,
+ BuildSchema,
+} from 'drizzle-typebox'
+
+import { table } from './schema'
+import type { Table } from 'drizzle-orm'
+
+type Spread<
+ T extends TObject | Table,
+ Mode extends 'select' | 'insert' | undefined,
+> =
+ T extends TObject
+ ? {
+ [K in keyof Fields]: Fields[K]
+ }
+ : T extends Table
+ ? Mode extends 'select'
+ ? BuildSchema<
+ 'select',
+ T['_']['columns'],
+ undefined
+ >['properties']
+ : Mode extends 'insert'
+ ? BuildSchema<
+ 'insert',
+ T['_']['columns'],
+ undefined
+ >['properties']
+ : {}
+ : {}
+
+/**
+ * Spread a Drizzle schema into a plain object
+ */
+export const spread = <
+ T extends TObject | Table,
+ Mode extends 'select' | 'insert' | undefined,
+>(
+ schema: T,
+ mode?: Mode,
+): Spread => {
+ const newSchema: Record = {}
+ let table
+
+ switch (mode) {
+ case 'insert':
+ case 'select':
+ if (Kind in schema) {
+ table = schema
+ break
+ }
+
+ table =
+ mode === 'insert'
+ ? createInsertSchema(schema)
+ : createSelectSchema(schema)
+
+ break
+
+ default:
+ if (!(Kind in schema)) throw new Error('Expect a schema')
+ table = schema
+ }
+
+ for (const key of Object.keys(table.properties))
+ newSchema[key] = table.properties[key]
+
+ return newSchema as any
+}
+
+/**
+ * Spread a Drizzle Table into a plain object
+ *
+ * If `mode` is 'insert', the schema will be refined for insert
+ * If `mode` is 'select', the schema will be refined for select
+ * If `mode` is undefined, the schema will be spread as is, models will need to be refined manually
+ */
+export const spreads = <
+ T extends Record,
+ Mode extends 'select' | 'insert' | undefined,
+>(
+ models: T,
+ mode?: Mode,
+): {
+ [K in keyof T]: Spread
+} => {
+ const newSchema: Record = {}
+ const keys = Object.keys(models)
+
+ for (const key of keys) newSchema[key] = spread(models[key], mode)
+
+ return newSchema as any
+}
+```
+
+Usage:
+```typescript
+// ✅ Using spread
+const user = spread(table.user, 'insert')
+const createUser = t.Object({
+ id: user.id,
+ username: user.username,
+ password: user.password
+})
+
+// ⚠️ Using t.Pick
+const _createUser = createInsertSchema(table.user)
+const createUser = t.Pick(_createUser, ['id', 'username', 'password'])
+```
+
+## Table Singleton Pattern
+```typescript
+// src/database/model.ts
+import { table } from './schema'
+import { spreads } from './utils'
+
+export const db = {
+ insert: spreads({ user: table.user }, 'insert'),
+ select: spreads({ user: table.user }, 'select')
+} as const
+```
+
+Usage:
+```typescript
+// src/index.ts
+import { db } from './database/model'
+const { user } = db.insert
+
+new Elysia()
+ .post('/sign-up', ({ body }) => {}, {
+ body: t.Object({
+ id: user.username,
+ username: user.username,
+ password: user.password
+ })
+ })
+```
+
+## Refinement
+```typescript
+// src/database/model.ts
+import { createInsertSchema, createSelectSchema } from 'drizzle-typebox'
+
+export const db = {
+ insert: spreads({
+ user: createInsertSchema(table.user, {
+ email: t.String({ format: 'email' })
+ })
+ }, 'insert'),
+ select: spreads({
+ user: createSelectSchema(table.user, {
+ email: t.String({ format: 'email' })
+ })
+ }, 'select')
+} as const
+```
+
+`spread` skips refined schemas.
diff --git a/.agents/skills/elysiajs/integrations/expo.md b/.agents/skills/elysiajs/integrations/expo.md
new file mode 100644
index 0000000..fad1471
--- /dev/null
+++ b/.agents/skills/elysiajs/integrations/expo.md
@@ -0,0 +1,95 @@
+# Expo Integration
+Run Elysia on Expo (React Native)
+
+## What It Is
+Create API routes in Expo app (SDK 50+, App Router v3).
+
+## Setup
+1. Create `app/[...slugs]+api.ts`
+2. Define Elysia server
+3. Export `Elysia.fetch` as HTTP methods
+
+```typescript
+// app/[...slugs]+api.ts
+import { Elysia, t } from 'elysia'
+
+const app = new Elysia()
+ .get('/', 'hello Expo')
+ .post('/', ({ body }) => body, {
+ body: t.Object({ name: t.String() })
+ })
+
+export const GET = app.fetch
+export const POST = app.fetch
+```
+
+## Prefix for Non-Root
+If placed in `app/api/[...slugs]+api.ts`, set prefix:
+```typescript
+const app = new Elysia({ prefix: '/api' })
+ .get('/', 'Hello Expo')
+
+export const GET = app.fetch
+export const POST = app.fetch
+```
+
+Ensures routing works in any location.
+
+## Eden (End-to-End Type Safety)
+1. Export type:
+```typescript
+// app/[...slugs]+api.ts
+const app = new Elysia()
+ .get('/', 'Hello Nextjs')
+ .post('/user', ({ body }) => body, {
+ body: treaty.schema('User', { name: 'string' })
+ })
+
+export type app = typeof app
+
+export const GET = app.fetch
+export const POST = app.fetch
+```
+
+2. Create client:
+```typescript
+// lib/eden.ts
+import { treaty } from '@elysiajs/eden'
+import type { app } from '../app/[...slugs]+api'
+
+export const api = treaty('localhost:3000/api')
+```
+
+3. Use in components:
+```tsx
+// app/page.tsx
+import { api } from '../lib/eden'
+
+export default async function Page() {
+ const message = await api.get()
+ return Hello, {message}
+}
+```
+
+## Deployment
+- Deploy as normal Elysia app OR
+- Use experimental Expo server runtime
+
+With Expo runtime:
+```bash
+expo export
+# Creates dist/server/_expo/functions/[...slugs]+api.js
+```
+
+Edge function, not normal server (no port allocation).
+
+### Adapters
+- Express
+- Netlify
+- Vercel
+
+## pnpm
+Manual install:
+```bash
+pnpm add @sinclair/typebox openapi-types
+```
diff --git a/.agents/skills/elysiajs/integrations/nextjs.md b/.agents/skills/elysiajs/integrations/nextjs.md
new file mode 100644
index 0000000..ddbc849
--- /dev/null
+++ b/.agents/skills/elysiajs/integrations/nextjs.md
@@ -0,0 +1,103 @@
+
+# Next.js Integration
+
+## What It Is
+Run Elysia on Next.js App Router.
+
+## Setup
+1. Create `app/api/[[...slugs]]/route.ts`
+2. Define Elysia + export handlers:
+```typescript
+// app/api/[[...slugs]]/route.ts
+import { Elysia, t } from 'elysia'
+
+const app = new Elysia({ prefix: '/api' })
+ .get('/', 'Hello Nextjs')
+ .post('/', ({ body }) => body, {
+ body: t.Object({ name: t.String() })
+ })
+
+export const GET = app.fetch
+export const POST = app.fetch
+```
+
+WinterCG compliance - works as normal Next.js API route.
+
+## Prefix for Non-Root
+If placed in `app/user/[[...slugs]]/route.ts`, set prefix:
+```typescript
+const app = new Elysia({ prefix: '/user' })
+ .get('/', 'Hello Nextjs')
+
+export const GET = app.fetch
+export const POST = app.fetch
+```
+
+## Eden (End-to-End Type Safety)
+Isomorphic fetch pattern:
+- Server: Direct calls (no network)
+- Client: Network calls
+
+1. Export type:
+```typescript
+// app/api/[[...slugs]]/route.ts
+export const app = new Elysia({ prefix: '/api' })
+ .get('/', 'Hello Nextjs')
+ .post('/user', ({ body }) => body, {
+ body: treaty.schema('User', { name: 'string' })
+ })
+
+export type app = typeof app
+
+export const GET = app.fetch
+export const POST = app.fetch
+```
+
+2. Create client:
+```typescript
+// lib/eden.ts
+import { treaty } from '@elysiajs/eden'
+import type { app } from '../app/api/[[...slugs]]/route'
+
+export const api =
+ typeof process !== 'undefined'
+ ? treaty(app).api
+ : treaty('localhost:3000').api
+```
+
+Use `typeof process` not `typeof window` (window undefined at build time → hydration error).
+
+3. Use in components:
+```tsx
+// app/page.tsx
+import { api } from '../lib/eden'
+
+export default async function Page() {
+ const message = await api.get()
+ return Hello, {message}
+}
+```
+
+Works with server/client components + ISR.
+
+## React Query
+```tsx
+import { useQuery } from '@tanstack/react-query'
+
+function App() {
+ const { data: response } = useQuery({
+ queryKey: ['get'],
+ queryFn: () => getTreaty().get()
+ })
+
+ return response?.data
+}
+```
+
+Works with all React Query features.
+
+## pnpm
+Manual install:
+```bash
+pnpm add @sinclair/typebox openapi-types
+```
diff --git a/.agents/skills/elysiajs/integrations/nodejs.md b/.agents/skills/elysiajs/integrations/nodejs.md
new file mode 100644
index 0000000..ce2edfa
--- /dev/null
+++ b/.agents/skills/elysiajs/integrations/nodejs.md
@@ -0,0 +1,64 @@
+# Node.js Integration
+Run Elysia on Node.js
+
+## What It Is
+Runtime adapter to run Elysia on Node.js.
+
+## Installation
+```bash
+bun add elysia @elysiajs/node
+```
+
+## Setup
+Apply node adapter:
+```typescript
+import { Elysia } from 'elysia'
+import { node } from '@elysiajs/node'
+
+const app = new Elysia({ adapter: node() })
+ .get('/', () => 'Hello Elysia')
+ .listen(3000)
+```
+
+## Additional Setup (Recommended)
+Install `tsx` for hot-reload:
+```bash
+bun add -d tsx @types/node typescript
+```
+
+Scripts in `package.json`:
+```json
+{
+ "scripts": {
+ "dev": "tsx watch src/index.ts",
+ "build": "tsc src/index.ts --outDir dist",
+ "start": "NODE_ENV=production node dist/index.js"
+ }
+}
+```
+
+- **dev**: Hot-reload dev mode
+- **build**: Production build
+- **start**: Production server
+
+Create `tsconfig.json`:
+```bash
+tsc --init
+```
+
+Update strict mode:
+```json
+{
+ "compilerOptions": {
+ "strict": true
+ }
+}
+```
+
+Provides hot-reload + JSX support similar to `bun dev`.
+
+## pnpm
+Manual install:
+```bash
+pnpm add @sinclair/typebox openapi-types
+```
diff --git a/.agents/skills/elysiajs/integrations/nuxt.md b/.agents/skills/elysiajs/integrations/nuxt.md
new file mode 100644
index 0000000..0b4d13d
--- /dev/null
+++ b/.agents/skills/elysiajs/integrations/nuxt.md
@@ -0,0 +1,67 @@
+# Nuxt Integration
+
+## What It Is
+Community plugin `nuxt-elysia` for Nuxt API routes with Eden Treaty.
+
+## Installation
+```bash
+bun add elysia @elysiajs/eden
+bun add -d nuxt-elysia
+```
+
+## Setup
+1. Add to Nuxt config:
+```typescript
+export default defineNuxtConfig({
+ modules: ['nuxt-elysia']
+})
+```
+
+2. Create `api.ts` at project root:
+```typescript
+// api.ts
+export default () => new Elysia()
+ .get('/hello', () => ({ message: 'Hello world!' }))
+```
+
+3. Use Eden Treaty:
+```vue
+
+
+
+
+```
+
+Auto-setup on Nuxt API route.
+
+## Prefix
+Default: `/_api`. Customize:
+```typescript
+export default defineNuxtConfig({
+ nuxtElysia: {
+ path: '/api'
+ }
+})
+```
+
+Mounts on `/api` instead of `/_api`.
+
+See [nuxt-elysia](https://github.com/tkesgar/nuxt-elysia) for more config.
+
+## pnpm
+Manual install:
+```bash
+pnpm add @sinclair/typebox openapi-types
+```
diff --git a/.agents/skills/elysiajs/integrations/prisma.md b/.agents/skills/elysiajs/integrations/prisma.md
new file mode 100644
index 0000000..f0684f1
--- /dev/null
+++ b/.agents/skills/elysiajs/integrations/prisma.md
@@ -0,0 +1,93 @@
+
+# Prisma Integration
+Elysia + Prisma integration guide
+
+## What It Is
+Type-safe ORM. Generate Elysia validation models from Prisma schema via `prismabox`.
+
+## Flow
+```
+Prisma → prismabox → Elysia validation → OpenAPI + Eden Treaty
+```
+
+## Installation
+```bash
+bun add @prisma/client prismabox && \
+bun add -d prisma
+```
+
+## Prisma Schema
+Add `prismabox` generator:
+```prisma
+// prisma/schema.prisma
+generator client {
+ provider = "prisma-client"
+ output = "../generated/prisma"
+}
+
+datasource db {
+ provider = "sqlite"
+ url = env("DATABASE_URL")
+}
+
+generator prismabox {
+ provider = "prismabox"
+ typeboxImportDependencyName = "elysia"
+ typeboxImportVariableName = "t"
+ inputModel = true
+ output = "../generated/prismabox"
+}
+
+model User {
+ id String @id @default(cuid())
+ email String @unique
+ name String?
+ posts Post[]
+}
+
+model Post {
+ id String @id @default(cuid())
+ title String
+ content String?
+ published Boolean @default(false)
+ author User @relation(fields: [authorId], references: [id])
+ authorId String
+}
+```
+
+Generates:
+- `User` → `generated/prismabox/User.ts`
+- `Post` → `generated/prismabox/Post.ts`
+
+## Using Generated Models
+```typescript
+// src/index.ts
+import { Elysia, t } from 'elysia'
+import { PrismaClient } from '../generated/prisma'
+import { UserPlain, UserPlainInputCreate } from '../generated/prismabox/User'
+
+const prisma = new PrismaClient()
+
+new Elysia()
+ .put('/', async ({ body }) =>
+ prisma.user.create({ data: body }), {
+ body: UserPlainInputCreate,
+ response: UserPlain
+ }
+ )
+ .get('/id/:id', async ({ params: { id }, status }) => {
+ const user = await prisma.user.findUnique({ where: { id } })
+
+ if (!user) return status(404, 'User not found')
+
+ return user
+ }, {
+ response: {
+ 200: UserPlain,
+ 404: t.String()
+ }
+ })
+ .listen(3000)
+```
+
+Reuses DB schema in Elysia validation models.
diff --git a/.agents/skills/elysiajs/integrations/react-email.md b/.agents/skills/elysiajs/integrations/react-email.md
new file mode 100644
index 0000000..1cb636f
--- /dev/null
+++ b/.agents/skills/elysiajs/integrations/react-email.md
@@ -0,0 +1,134 @@
+# React Email Integration
+
+## What It Is
+Use React components to create emails. Direct JSX import via Bun.
+
+## Installation
+```bash
+bun add -d react-email
+bun add @react-email/components react react-dom
+```
+
+Script in `package.json`:
+```json
+{
+ "scripts": {
+ "email": "email dev --dir src/emails"
+ }
+}
+```
+
+Email templates → `src/emails` directory.
+
+### TypeScript
+Add to `tsconfig.json`:
+```json
+{
+ "compilerOptions": {
+ "jsx": "react"
+ }
+}
+```
+
+## Email Template
+```tsx
+// src/emails/otp.tsx
+import * as React from 'react'
+import { Tailwind, Section, Text } from '@react-email/components'
+
+export default function OTPEmail({ otp }: { otp: number }) {
+ return (
+
+
+
+
+ Verify your Email Address
+
+
+ Use the following code to verify your email address
+
+ {otp}
+
+ This code is valid for 10 minutes
+
+
+ Thank you for joining us
+
+
+
+
+ )
+}
+
+OTPEmail.PreviewProps = { otp: 123456 }
+```
+
+`@react-email/components` → email-client compatible (Gmail, Outlook). Tailwind support.
+
+`PreviewProps` → playground only.
+
+## Preview
+```bash
+bun email
+```
+
+Opens browser with preview.
+
+## Send Email
+Render with `react-dom/server`, submit via provider:
+
+### Nodemailer
+```typescript
+import { renderToStaticMarkup } from 'react-dom/server'
+import OTPEmail from './emails/otp'
+import nodemailer from 'nodemailer'
+
+const transporter = nodemailer.createTransport({
+ host: 'smtp.gehenna.sh',
+ port: 465,
+ auth: { user: 'makoto', pass: '12345678' }
+})
+
+.get('/otp', async ({ body }) => {
+ const otp = ~~(Math.random() * 900_000) + 100_000
+ const html = renderToStaticMarkup( )
+
+ await transporter.sendMail({
+ from: '[email protected]',
+ to: body,
+ subject: 'Verify your email address',
+ html
+ })
+
+ return { success: true }
+}, {
+ body: t.String({ format: 'email' })
+})
+```
+
+### Resend
+```typescript
+import OTPEmail from './emails/otp'
+import Resend from 'resend'
+
+const resend = new Resend('re_123456789')
+
+.get('/otp', ({ body }) => {
+ const otp = ~~(Math.random() * 900_000) + 100_000
+
+ await resend.emails.send({
+ from: '[email protected]',
+ to: body,
+ subject: 'Verify your email address',
+ html: // Direct JSX
+ })
+
+ return { success: true }
+})
+```
+
+Direct JSX import thanks to Bun.
+
+Other providers: AWS SES, SendGrid.
+
+See [React Email Integrations](https://react.email/docs/integrations/overview).
diff --git a/.agents/skills/elysiajs/integrations/sveltekit.md b/.agents/skills/elysiajs/integrations/sveltekit.md
new file mode 100644
index 0000000..4ad306a
--- /dev/null
+++ b/.agents/skills/elysiajs/integrations/sveltekit.md
@@ -0,0 +1,53 @@
+
+# SvelteKit Integration
+
+## What It Is
+Run Elysia on SvelteKit server routes.
+
+## Setup
+1. Create `src/routes/[...slugs]/+server.ts`
+2. Define Elysia server
+3. Export fallback handler:
+```typescript
+// src/routes/[...slugs]/+server.ts
+import { Elysia, t } from 'elysia'
+
+const app = new Elysia()
+ .get('/', 'hello SvelteKit')
+ .post('/', ({ body }) => body, {
+ body: t.Object({ name: t.String() })
+ })
+
+interface WithRequest {
+ request: Request
+}
+
+export const fallback = ({ request }: WithRequest) => app.handle(request)
+```
+
+Treat as normal SvelteKit server route.
+
+## Prefix for Non-Root
+If placed in `src/routes/api/[...slugs]/+server.ts`, set prefix:
+```typescript
+// src/routes/api/[...slugs]/+server.ts
+import { Elysia, t } from 'elysia'
+
+const app = new Elysia({ prefix: '/api' })
+ .get('/', () => 'hi')
+ .post('/', ({ body }) => body, {
+ body: t.Object({ name: t.String() })
+ })
+
+type RequestHandler = (v: { request: Request }) => Response | Promise
+
+export const fallback: RequestHandler = ({ request }) => app.handle(request)
+```
+
+Ensures routing works in any location.
+
+## pnpm
+Manual install:
+```bash
+pnpm add @sinclair/typebox openapi-types
+```
diff --git a/.agents/skills/elysiajs/integrations/tanstack-start.md b/.agents/skills/elysiajs/integrations/tanstack-start.md
new file mode 100644
index 0000000..2a1e642
--- /dev/null
+++ b/.agents/skills/elysiajs/integrations/tanstack-start.md
@@ -0,0 +1,87 @@
+# Tanstack Start Integration
+
+## What It Is
+Elysia runs inside Tanstack Start server routes.
+
+## Setup
+1. Create `src/routes/api.$.ts`
+2. Define Elysia server
+3. Export handlers in `server.handlers`:
+```typescript
+// src/routes/api.$.ts
+import { Elysia } from 'elysia'
+import { createFileRoute } from '@tanstack/react-router'
+import { createIsomorphicFn } from '@tanstack/react-start'
+
+const app = new Elysia({
+ prefix: '/api'
+}).get('/', 'Hello Elysia!')
+
+const handle = ({ request }: { request: Request }) => app.fetch(request)
+
+export const Route = createFileRoute('/api/$')({
+ server: {
+ handlers: {
+ GET: handle,
+ POST: handle
+ }
+ }
+})
+```
+
+Runs on `/api`. Add methods to `server.handlers` as needed.
+
+## Eden (End-to-End Type Safety)
+Isomorphic pattern with `createIsomorphicFn`:
+```typescript
+// src/routes/api.$.ts
+export const getTreaty = createIsomorphicFn()
+ .server(() => treaty(app).api)
+ .client(() => treaty('localhost:3000').api)
+```
+
+- Server: Direct call (no HTTP overhead)
+- Client: HTTP call
+
+## Loader Data
+Fetch before render:
+```tsx
+// src/routes/index.tsx
+import { createFileRoute } from '@tanstack/react-router'
+import { getTreaty } from './api.$'
+
+export const Route = createFileRoute('/a')({
+ component: App,
+ loader: () => getTreaty().get().then((res) => res.data)
+})
+
+function App() {
+ const data = Route.useLoaderData()
+ return data
+}
+```
+
+Executed server-side during SSR. No HTTP overhead. Type-safe.
+
+## React Query
+```tsx
+import { useQuery } from '@tanstack/react-query'
+import { getTreaty } from './api.$'
+
+function App() {
+ const { data: response } = useQuery({
+ queryKey: ['get'],
+ queryFn: () => getTreaty().get()
+ })
+
+ return response?.data
+}
+```
+
+Works with all React Query features.
+
+## pnpm
+Manual install:
+```bash
+pnpm add @sinclair/typebox openapi-types
+```
diff --git a/.agents/skills/elysiajs/integrations/vercel.md b/.agents/skills/elysiajs/integrations/vercel.md
new file mode 100644
index 0000000..555ec8e
--- /dev/null
+++ b/.agents/skills/elysiajs/integrations/vercel.md
@@ -0,0 +1,55 @@
+# Vercel Integration
+Deploy Elysia on Vercel
+
+## What It Is
+Zero-config deployment on Vercel (Bun or Node runtime).
+
+## Setup
+1. Create/import Elysia server in `src/index.ts`
+2. Export as default:
+```typescript
+import { Elysia, t } from 'elysia'
+
+export default new Elysia()
+ .get('/', () => 'Hello Vercel Function')
+ .post('/', ({ body }) => body, {
+ body: t.Object({ name: t.String() })
+ })
+```
+
+3. Develop locally:
+```bash
+vc dev
+```
+
+4. Deploy:
+```bash
+vc deploy
+```
+
+## Node.js Runtime
+Set in `package.json`:
+```json
+{
+ "name": "elysia-app",
+ "type": "module"
+}
+```
+
+## Bun Runtime
+Set in `vercel.json`:
+```json
+{
+ "$schema": "https://openapi.vercel.sh/vercel.json",
+ "bunVersion": "1.x"
+}
+```
+
+## pnpm
+Manual install:
+```bash
+pnpm add @sinclair/typebox openapi-types
+```
+
+## Troubleshooting
+Vercel has zero config for Elysia. For additional config, see [Vercel docs](https://vercel.com/docs/frameworks/backend/elysia).
diff --git a/.agents/skills/elysiajs/patterns/mvc.md b/.agents/skills/elysiajs/patterns/mvc.md
new file mode 100644
index 0000000..dc227c6
--- /dev/null
+++ b/.agents/skills/elysiajs/patterns/mvc.md
@@ -0,0 +1,380 @@
+# MVC pattern
+This file contains a guideline for using Elysia with MVC or Model View Controller patterns
+
+- Controller:
+ - Prefers Elysia as a controller for HTTP dependant
+ - For non HTTP dependent, prefers service instead unless explicitly asked
+ - Use `onError` to handle local custom errors
+ - Register Model to Elysia instance via `Elysia.models({ ...models })` and prefix model by namespace `Elysia.prefix('model', 'Namespace.')
+ - Prefers Reference Model by name provided by Elysia instead of using an actual `Model.name`
+- Service:
+ - Prefers class (or abstract class if possible)
+ - Prefers interface/type derive from `Model`
+ - Return `status` (`import { status } from 'elysia'`) for error
+ - Prefers `return Error` instead of `throw Error`
+- Models:
+ - Always export validation model and type of validation model
+ - Custom Error should be in contains in Model
+
+## Controller
+Due to type soundness of Elysia, it's not recommended to use a traditional controller class that is tightly coupled with Elysia's `Context` because:
+
+1. **Elysia type is complex** and heavily depends on plugin and multiple level of chaining.
+2. **Hard to type**, Elysia type could change at anytime, especially with decorators, and store
+3. **Loss of type integrity**, and inconsistency between types and runtime code.
+
+We recommended one of the following approach to implement a controller in Elysia.
+1. Use Elysia instance as a controller itself
+2. Create a controller that is not tied with HTTP request or Elysia.
+
+---
+
+### 1. Elysia instance as a controller
+> 1 Elysia instance = 1 controller
+
+Treat an Elysia instance as a controller, and define your routes directly on the Elysia instance.
+
+```typescript
+// Do
+import { Elysia } from 'elysia'
+import { Service } from './service'
+
+new Elysia()
+ .get('/', ({ stuff }) => {
+ Service.doStuff(stuff)
+ })
+```
+
+This approach allows Elysia to infer the `Context` type automatically, ensuring type integrity and consistency between types and runtime code.
+
+```typescript
+// Don't
+import { Elysia, t, type Context } from 'elysia'
+
+abstract class Controller {
+ static root(context: Context) {
+ return Service.doStuff(context.stuff)
+ }
+}
+
+new Elysia()
+ .get('/', Controller.root)
+```
+
+This approach makes it hard to type `Context` properly, and may lead to loss of type integrity.
+
+### 2. Controller without HTTP request
+If you want to create a controller class, we recommend creating a class that is not tied to HTTP request or Elysia at all.
+
+This approach allows you to decouple the controller from Elysia, making it easier to test, reuse, and even swap a framework while still follows the MVC pattern.
+
+```typescript
+import { Elysia } from 'elysia'
+
+abstract class Controller {
+ static doStuff(stuff: string) {
+ return Service.doStuff(stuff)
+ }
+}
+
+new Elysia()
+ .get('/', ({ stuff }) => Controller.doStuff(stuff))
+```
+
+Tying the controller to Elysia Context may lead to:
+1. Loss of type integrity
+2. Make it harder to test and reuse
+3. Lead to vendor lock-in
+
+We recommended to keep the controller decoupled from Elysia as much as possible.
+
+### Don't: Pass entire `Context` to a controller
+**Context is a highly dynamic type** that can be inferred from Elysia instance.
+
+Do not pass an entire `Context` to a controller, instead use object destructuring to extract what you need and pass it to the controller.
+
+```typescript
+import type { Context } from 'elysia'
+
+abstract class Controller {
+ constructor() {}
+
+ // Don't do this
+ static root(context: Context) {
+ return Service.doStuff(context.stuff)
+ }
+}
+```
+
+This approach makes it hard to type `Context` properly, and may lead to loss of type integrity.
+
+### Testing
+If you're using Elysia as a controller, you can test your controller using `handle` to directly call a function (and it's lifecycle)
+
+```typescript
+import { Elysia } from 'elysia'
+import { Service } from './service'
+
+import { describe, it, expect } from 'bun:test'
+
+const app = new Elysia()
+ .get('/', ({ stuff }) => {
+ Service.doStuff(stuff)
+
+ return 'ok'
+ })
+
+describe('Controller', () => {
+ it('should work', async () => {
+ const response = await app
+ .handle(new Request('http://localhost/'))
+ .then((x) => x.text())
+
+ expect(response).toBe('ok')
+ })
+})
+```
+
+You may find more information about testing in [Unit Test](/patterns/unit-test.html).
+
+## Service
+Service is a set of utility/helper functions decoupled as a business logic to use in a module/controller, in our case, an Elysia instance.
+
+Any technical logic that can be decoupled from controller may live inside a **Service**.
+
+There are 2 types of service in Elysia:
+1. Non-request dependent service
+2. Request dependent service
+
+### 1. Abstract away Non-request dependent service
+
+We recommend abstracting a service class/function away from Elysia.
+
+If the service or function isn't tied to an HTTP request or doesn't access a `Context`, it's recommended to implement it as a static class or function.
+
+```typescript
+import { Elysia, t } from 'elysia'
+
+abstract class Service {
+ static fibo(number: number): number {
+ if(number < 2)
+ return number
+
+ return Service.fibo(number - 1) + Service.fibo(number - 2)
+ }
+}
+
+new Elysia()
+ .get('/fibo', ({ body }) => {
+ return Service.fibo(body)
+ }, {
+ body: t.Numeric()
+ })
+```
+
+If your service doesn't need to store a property, you may use `abstract class` and `static` instead to avoid allocating class instance.
+
+### 2. Request dependent service as Elysia instance
+
+**If the service is a request-dependent service** or needs to process HTTP requests, we recommend abstracting it as an Elysia instance to ensure type integrity and inference:
+
+```typescript
+import { Elysia } from 'elysia'
+
+// Do
+const AuthService = new Elysia({ name: 'Auth.Service' })
+ .macro({
+ isSignIn: {
+ resolve({ cookie, status }) {
+ if (!cookie.session.value) return status(401)
+
+ return {
+ session: cookie.session.value,
+ }
+ }
+ }
+ })
+
+const UserController = new Elysia()
+ .use(AuthService)
+ .get('/profile', ({ Auth: { user } }) => user, {
+ isSignIn: true
+ })
+```
+
+### Do: Decorate only request dependent property
+
+It's recommended to `decorate` only request-dependent properties, such as `requestIP`, `requestTime`, or `session`.
+
+Overusing decorators may tie your code to Elysia, making it harder to test and reuse.
+
+```typescript
+import { Elysia } from 'elysia'
+
+new Elysia()
+ .decorate('requestIP', ({ request }) => request.headers.get('x-forwarded-for') || request.ip)
+ .decorate('requestTime', () => Date.now())
+ .decorate('session', ({ cookie }) => cookie.session.value)
+ .get('/', ({ requestIP, requestTime, session }) => {
+ return { requestIP, requestTime, session }
+ })
+```
+
+### Don't: Pass entire `Context` to a service
+**Context is a highly dynamic type** that can be inferred from Elysia instance.
+
+Do not pass an entire `Context` to a service, instead use object destructuring to extract what you need and pass it to the service.
+```typescript
+import type { Context } from 'elysia'
+
+class AuthService {
+ constructor() {}
+
+ // Don't do this
+ isSignIn({ status, cookie: { session } }: Context) {
+ if (session.value)
+ return status(401)
+ }
+}
+```
+
+As Elysia type is complex, and heavily depends on plugin and multiple level of chaining, it can be challenging to manually type as it's highly dynamic.
+
+## Model
+Model or [DTO (Data Transfer Object)](https://en.wikipedia.org/wiki/Data_transfer_object) is handle by [Elysia.t (Validation)](/essential/validation.html#elysia-type).
+
+Elysia has a validation system built-in which can infers type from your code and validate it at runtime.
+
+### Do: Use Elysia's validation system
+
+Elysia strength is prioritizing a single source of truth for both type and runtime validation.
+
+Instead of declaring an interface, reuse validation's model instead:
+```typescript twoslash
+// Do
+import { Elysia, t } from 'elysia'
+
+const customBody = t.Object({
+ username: t.String(),
+ password: t.String()
+})
+
+// Optional if you want to get the type of the model
+// Usually if we didn't use the type, as it's already inferred by Elysia
+type CustomBody = typeof customBody.static
+
+export { customBody }
+```
+
+We can get type of model by using `typeof` with `.static` property from the model.
+
+Then you can use the `CustomBody` type to infer the type of the request body.
+
+```typescript twoslash
+// Do
+new Elysia()
+ .post('/login', ({ body }) => {
+ return body
+ }, {
+ body: customBody
+ })
+```
+
+### Don't: Declare a class instance as a model
+
+Do not declare a class instance as a model:
+```typescript
+// Don't
+class CustomBody {
+ username: string
+ password: string
+
+ constructor(username: string, password: string) {
+ this.username = username
+ this.password = password
+ }
+}
+
+// Don't
+interface ICustomBody {
+ username: string
+ password: string
+}
+```
+
+### Don't: Declare type separate from the model
+Do not declare a type separate from the model, instead use `typeof` with `.static` property to get the type of the model.
+
+```typescript
+// Don't
+import { Elysia, t } from 'elysia'
+
+const customBody = t.Object({
+ username: t.String(),
+ password: t.String()
+})
+
+type CustomBody = {
+ username: string
+ password: string
+}
+
+// Do
+const customBody = t.Object({
+ username: t.String(),
+ password: t.String()
+})
+
+type CustomBody = typeof customBody.static
+```
+
+### Group
+You can group multiple models into a single object to make it more organized.
+
+```typescript
+import { Elysia, t } from 'elysia'
+
+export const AuthModel = {
+ sign: t.Object({
+ username: t.String(),
+ password: t.String()
+ })
+}
+
+const models = AuthModel.models
+```
+
+### Model Injection
+Though this is optional, if you are strictly following MVC pattern, you may want to inject like a service into a controller. We recommended using Elysia reference model
+
+Using Elysia's model reference
+```typescript twoslash
+import { Elysia, t } from 'elysia'
+
+const customBody = t.Object({
+ username: t.String(),
+ password: t.String()
+})
+
+const AuthModel = new Elysia()
+ .model({
+ sign: customBody
+ })
+
+const models = AuthModel.models
+
+const UserController = new Elysia({ prefix: '/auth' })
+ .use(AuthModel)
+ .prefix('model', 'auth.')
+ .post('/sign-in', async ({ body, cookie: { session } }) => {
+ return true
+ }, {
+ body: 'auth.Sign'
+ })
+```
+
+This approach provide several benefits:
+1. Allow us to name a model and provide auto-completion.
+2. Modify schema for later usage, or perform a [remap](/essential/handler.html#remap).
+3. Show up as "models" in OpenAPI compliance client, eg. OpenAPI.
+4. Improve TypeScript inference speed as model type will be cached during registration.
diff --git a/.agents/skills/elysiajs/plugins/bearer.md b/.agents/skills/elysiajs/plugins/bearer.md
new file mode 100644
index 0000000..df529e5
--- /dev/null
+++ b/.agents/skills/elysiajs/plugins/bearer.md
@@ -0,0 +1,30 @@
+# Bearer
+Plugin for Elysia for retrieving the Bearer token.
+
+## Installation
+```bash
+bun add @elysiajs/bearer
+```
+
+## Basic Usage
+```typescript twoslash
+import { Elysia } from 'elysia'
+import { bearer } from '@elysiajs/bearer'
+
+const app = new Elysia()
+ .use(bearer())
+ .get('/sign', ({ bearer }) => bearer, {
+ beforeHandle({ bearer, set, status }) {
+ if (!bearer) {
+ set.headers[
+ 'WWW-Authenticate'
+ ] = `Bearer realm='sign', error="invalid_request"`
+
+ return status(400, 'Unauthorized')
+ }
+ }
+ })
+ .listen(3000)
+```
+
+This plugin is for retrieving a Bearer token specified in RFC6750
diff --git a/.agents/skills/elysiajs/plugins/cors.md b/.agents/skills/elysiajs/plugins/cors.md
new file mode 100644
index 0000000..2d8db2a
--- /dev/null
+++ b/.agents/skills/elysiajs/plugins/cors.md
@@ -0,0 +1,141 @@
+# CORS
+
+Plugin for Elysia that adds support for customizing Cross-Origin Resource Sharing behavior.
+
+## Installation
+```bash
+bun add @elysiajs/cors
+```
+
+## Basic Usage
+```typescript twoslash
+import { Elysia } from 'elysia'
+import { cors } from '@elysiajs/cors'
+
+new Elysia().use(cors()).listen(3000)
+```
+
+This will set Elysia to accept requests from any origin.
+
+## Config
+
+Below is a config which is accepted by the plugin
+
+### origin
+
+@default `true`
+
+Indicates whether the response can be shared with the requesting code from the given origins.
+
+Value can be one of the following:
+
+- **string** - Name of origin which will directly assign to Access-Control-Allow-Origin header.
+- **boolean** - If set to true, Access-Control-Allow-Origin will be set to `*` (any origins)
+- **RegExp** - Pattern to match request's URL, allowed if matched.
+- **Function** - Custom logic to allow resource sharing, allow if `true` is returned.
+ - Expected to have the type of:
+ ```typescript
+ cors(context: Context) => boolean | void
+ ```
+- **Array** - iterate through all cases above in order, allowed if any of the values are `true`.
+
+---
+
+### methods
+
+@default `*`
+
+Allowed methods for cross-origin requests by assign `Access-Control-Allow-Methods` header.
+
+Value can be one of the following:
+- **undefined | null | ''** - Ignore all methods.
+- **\*** - Allows all methods.
+- **string** - Expects either a single method or a comma-delimited string
+ - (eg: `'GET, PUT, POST'`)
+- **string[]** - Allow multiple HTTP methods.
+ - eg: `['GET', 'PUT', 'POST']`
+
+---
+
+### allowedHeaders
+
+@default `*`
+
+Allowed headers for an incoming request by assign `Access-Control-Allow-Headers` header.
+
+Value can be one of the following:
+- **string** - Expects either a single header or a comma-delimited string
+ - eg: `'Content-Type, Authorization'`.
+- **string[]** - Allow multiple HTTP headers.
+ - eg: `['Content-Type', 'Authorization']`
+
+---
+
+### exposeHeaders
+
+@default `*`
+
+Response CORS with specified headers by sssign Access-Control-Expose-Headers header.
+
+Value can be one of the following:
+- **string** - Expects either a single header or a comma-delimited string.
+ - eg: `'Content-Type, X-Powered-By'`.
+- **string[]** - Allow multiple HTTP headers.
+ - eg: `['Content-Type', 'X-Powered-By']`
+
+---
+
+### credentials
+
+@default `true`
+
+The Access-Control-Allow-Credentials response header tells browsers whether to expose the response to the frontend JavaScript code when the request's credentials mode Request.credentials is `include`.
+
+Credentials are cookies, authorization headers, or TLS client certificates by assign `Access-Control-Allow-Credentials` header.
+
+---
+
+### maxAge
+
+@default `5`
+
+Indicates how long the results of a preflight request that is the information contained in the `Access-Control-Allow-Methods` and `Access-Control-Allow-Headers` headers) can be cached.
+
+Assign `Access-Control-Max-Age` header.
+
+---
+
+### preflight
+
+The preflight request is a request sent to check if the CORS protocol is understood and if a server is aware of using specific methods and headers.
+
+Response with **OPTIONS** request with 3 HTTP request headers:
+- **Access-Control-Request-Method**
+- **Access-Control-Request-Headers**
+- **Origin**
+
+This config indicates if the server should respond to preflight requests.
+
+---
+
+## Pattern
+
+Below you can find the common patterns to use the plugin.
+
+## Allow CORS by top-level domain
+
+```typescript twoslash
+import { Elysia } from 'elysia'
+import { cors } from '@elysiajs/cors'
+
+const app = new Elysia()
+ .use(
+ cors({
+ origin: /.*\.saltyaom\.com$/
+ })
+ )
+ .get('/', () => 'Hi')
+ .listen(3000)
+```
+
+This will allow requests from top-level domains with `saltyaom.com`
diff --git a/.agents/skills/elysiajs/plugins/cron.md b/.agents/skills/elysiajs/plugins/cron.md
new file mode 100644
index 0000000..3905ad5
--- /dev/null
+++ b/.agents/skills/elysiajs/plugins/cron.md
@@ -0,0 +1,265 @@
+# Cron Plugin
+
+This plugin adds support for running cronjob to Elysia server.
+
+## Installation
+
+```bash
+bun add @elysiajs/cron
+```
+
+## Basic Usage
+```typescript twoslash
+import { Elysia } from 'elysia'
+import { cron } from '@elysiajs/cron'
+
+new Elysia()
+ .use(
+ cron({
+ name: 'heartbeat',
+ pattern: '*/10 * * * * *',
+ run() {
+ console.log('Heartbeat')
+ }
+ })
+ )
+ .listen(3000)
+```
+
+The above code will log `heartbeat` every 10 seconds.
+
+## Config
+Below is a config which is accepted by the plugin
+
+### cron
+
+Create a cronjob for the Elysia server.
+
+```
+cron(config: CronConfig, callback: (Instance['store']) => void): this
+```
+
+`CronConfig` accepts the parameters specified below:
+
+---
+
+### CronConfig.name
+
+Job name to register to `store`.
+
+This will register the cron instance to `store` with a specified name, which can be used to reference in later processes eg. stop the job.
+
+---
+
+### CronConfig.pattern
+
+Time to run the job as specified by cron syntax.
+
+```
+┌────────────── second (optional)
+│ ┌──────────── minute
+│ │ ┌────────── hour
+│ │ │ ┌──────── day of the month
+│ │ │ │ ┌────── month
+│ │ │ │ │ ┌──── day of week
+│ │ │ │ │ │
+* * * * * *
+```
+
+---
+
+### CronConfig.timezone
+Time zone in Europe/Stockholm format
+
+---
+
+### CronConfig.startAt
+Schedule start time for the job
+
+---
+
+### CronConfig.stopAt
+Schedule stop time for the job
+
+---
+
+### CronConfig.maxRuns
+Maximum number of executions
+
+---
+
+### CronConfig.catch
+Continue execution even if an unhandled error is thrown by a triggered function.
+
+### CronConfig.interval
+The minimum interval between executions, in seconds.
+
+---
+
+## CronConfig.Pattern
+Below you can find the common patterns to use the plugin.
+
+---
+
+## Pattern
+
+Below you can find the common patterns to use the plugin.
+
+## Stop cronjob
+
+You can stop cronjob manually by accessing the cronjob name registered to `store`.
+
+```typescript
+import { Elysia } from 'elysia'
+import { cron } from '@elysiajs/cron'
+
+const app = new Elysia()
+ .use(
+ cron({
+ name: 'heartbeat',
+ pattern: '*/1 * * * * *',
+ run() {
+ console.log('Heartbeat')
+ }
+ })
+ )
+ .get(
+ '/stop',
+ ({
+ store: {
+ cron: { heartbeat }
+ }
+ }) => {
+ heartbeat.stop()
+
+ return 'Stop heartbeat'
+ }
+ )
+ .listen(3000)
+```
+
+---
+
+## Predefined patterns
+
+You can use predefined patterns from `@elysiajs/cron/schedule`
+
+```typescript
+import { Elysia } from 'elysia'
+import { cron, Patterns } from '@elysiajs/cron'
+
+const app = new Elysia()
+ .use(
+ cron({
+ name: 'heartbeat',
+ pattern: Patterns.everySecond(),
+ run() {
+ console.log('Heartbeat')
+ }
+ })
+ )
+ .get(
+ '/stop',
+ ({
+ store: {
+ cron: { heartbeat }
+ }
+ }) => {
+ heartbeat.stop()
+
+ return 'Stop heartbeat'
+ }
+ )
+ .listen(3000)
+```
+
+### Functions
+
+| Function | Description |
+| ---------------------------------------- | ----------------------------------------------------- |
+| `.everySeconds(2)` | Run the task every 2 seconds |
+| `.everyMinutes(5)` | Run the task every 5 minutes |
+| `.everyHours(3)` | Run the task every 3 hours |
+| `.everyHoursAt(3, 15)` | Run the task every 3 hours at 15 minutes |
+| `.everyDayAt('04:19')` | Run the task every day at 04:19 |
+| `.everyWeekOn(Patterns.MONDAY, '19:30')` | Run the task every Monday at 19:30 |
+| `.everyWeekdayAt('17:00')` | Run the task every day from Monday to Friday at 17:00 |
+| `.everyWeekendAt('11:00')` | Run the task on Saturday and Sunday at 11:00 |
+
+### Function aliases to constants
+
+| Function | Constant |
+| ----------------- | ---------------------------------- |
+| `.everySecond()` | EVERY_SECOND |
+| `.everyMinute()` | EVERY_MINUTE |
+| `.hourly()` | EVERY_HOUR |
+| `.daily()` | EVERY_DAY_AT_MIDNIGHT |
+| `.everyWeekday()` | EVERY_WEEKDAY |
+| `.everyWeekend()` | EVERY_WEEKEND |
+| `.weekly()` | EVERY_WEEK |
+| `.monthly()` | EVERY_1ST_DAY_OF_MONTH_AT_MIDNIGHT |
+| `.everyQuarter()` | EVERY_QUARTER |
+| `.yearly()` | EVERY_YEAR |
+
+### Constants
+
+| Constant | Pattern |
+| ---------------------------------------- | -------------------- |
+| `.EVERY_SECOND` | `* * * * * *` |
+| `.EVERY_5_SECONDS` | `*/5 * * * * *` |
+| `.EVERY_10_SECONDS` | `*/10 * * * * *` |
+| `.EVERY_30_SECONDS` | `*/30 * * * * *` |
+| `.EVERY_MINUTE` | `*/1 * * * *` |
+| `.EVERY_5_MINUTES` | `0 */5 * * * *` |
+| `.EVERY_10_MINUTES` | `0 */10 * * * *` |
+| `.EVERY_30_MINUTES` | `0 */30 * * * *` |
+| `.EVERY_HOUR` | `0 0-23/1 * * *` |
+| `.EVERY_2_HOURS` | `0 0-23/2 * * *` |
+| `.EVERY_3_HOURS` | `0 0-23/3 * * *` |
+| `.EVERY_4_HOURS` | `0 0-23/4 * * *` |
+| `.EVERY_5_HOURS` | `0 0-23/5 * * *` |
+| `.EVERY_6_HOURS` | `0 0-23/6 * * *` |
+| `.EVERY_7_HOURS` | `0 0-23/7 * * *` |
+| `.EVERY_8_HOURS` | `0 0-23/8 * * *` |
+| `.EVERY_9_HOURS` | `0 0-23/9 * * *` |
+| `.EVERY_10_HOURS` | `0 0-23/10 * * *` |
+| `.EVERY_11_HOURS` | `0 0-23/11 * * *` |
+| `.EVERY_12_HOURS` | `0 0-23/12 * * *` |
+| `.EVERY_DAY_AT_1AM` | `0 01 * * *` |
+| `.EVERY_DAY_AT_2AM` | `0 02 * * *` |
+| `.EVERY_DAY_AT_3AM` | `0 03 * * *` |
+| `.EVERY_DAY_AT_4AM` | `0 04 * * *` |
+| `.EVERY_DAY_AT_5AM` | `0 05 * * *` |
+| `.EVERY_DAY_AT_6AM` | `0 06 * * *` |
+| `.EVERY_DAY_AT_7AM` | `0 07 * * *` |
+| `.EVERY_DAY_AT_8AM` | `0 08 * * *` |
+| `.EVERY_DAY_AT_9AM` | `0 09 * * *` |
+| `.EVERY_DAY_AT_10AM` | `0 10 * * *` |
+| `.EVERY_DAY_AT_11AM` | `0 11 * * *` |
+| `.EVERY_DAY_AT_NOON` | `0 12 * * *` |
+| `.EVERY_DAY_AT_1PM` | `0 13 * * *` |
+| `.EVERY_DAY_AT_2PM` | `0 14 * * *` |
+| `.EVERY_DAY_AT_3PM` | `0 15 * * *` |
+| `.EVERY_DAY_AT_4PM` | `0 16 * * *` |
+| `.EVERY_DAY_AT_5PM` | `0 17 * * *` |
+| `.EVERY_DAY_AT_6PM` | `0 18 * * *` |
+| `.EVERY_DAY_AT_7PM` | `0 19 * * *` |
+| `.EVERY_DAY_AT_8PM` | `0 20 * * *` |
+| `.EVERY_DAY_AT_9PM` | `0 21 * * *` |
+| `.EVERY_DAY_AT_10PM` | `0 22 * * *` |
+| `.EVERY_DAY_AT_11PM` | `0 23 * * *` |
+| `.EVERY_DAY_AT_MIDNIGHT` | `0 0 * * *` |
+| `.EVERY_WEEK` | `0 0 * * 0` |
+| `.EVERY_WEEKDAY` | `0 0 * * 1-5` |
+| `.EVERY_WEEKEND` | `0 0 * * 6,0` |
+| `.EVERY_1ST_DAY_OF_MONTH_AT_MIDNIGHT` | `0 0 1 * *` |
+| `.EVERY_1ST_DAY_OF_MONTH_AT_NOON` | `0 12 1 * *` |
+| `.EVERY_2ND_HOUR` | `0 */2 * * *` |
+| `.EVERY_2ND_HOUR_FROM_1AM_THROUGH_11PM` | `0 1-23/2 * * *` |
+| `.EVERY_2ND_MONTH` | `0 0 1 */2 *` |
+| `.EVERY_QUARTER` | `0 0 1 */3 *` |
+| `.EVERY_6_MONTHS` | `0 0 1 */6 *` |
+| `.EVERY_YEAR` | `0 0 1 1 *` |
+| `.EVERY_30_MINUTES_BETWEEN_9AM_AND_5PM` | `0 */30 9-17 * * *` |
+| `.EVERY_30_MINUTES_BETWEEN_9AM_AND_6PM` | `0 */30 9-18 * * *` |
+| `.EVERY_30_MINUTES_BETWEEN_10AM_AND_7PM` | `0 */30 10-19 * * *` |
diff --git a/.agents/skills/elysiajs/plugins/graphql-apollo.md b/.agents/skills/elysiajs/plugins/graphql-apollo.md
new file mode 100644
index 0000000..655f258
--- /dev/null
+++ b/.agents/skills/elysiajs/plugins/graphql-apollo.md
@@ -0,0 +1,90 @@
+# GraphQL Apollo
+
+Plugin for Elysia to use GraphQL Apollo.
+
+## Installation
+```bash
+bun add graphql @elysiajs/apollo @apollo/server
+```
+
+## Basic Usage
+
+```typescript
+import { Elysia } from 'elysia'
+import { apollo, gql } from '@elysiajs/apollo'
+
+const app = new Elysia()
+ .use(
+ apollo({
+ typeDefs: gql`
+ type Book {
+ title: String
+ author: String
+ }
+
+ type Query {
+ books: [Book]
+ }
+ `,
+ resolvers: {
+ Query: {
+ books: () => {
+ return [
+ {
+ title: 'Elysia',
+ author: 'saltyAom'
+ }
+ ]
+ }
+ }
+ }
+ })
+ )
+ .listen(3000)
+```
+
+Accessing `/graphql` should show Apollo GraphQL playground work with.
+
+## Context
+
+Because Elysia is based on Web Standard Request and Response which is different from Node's `HttpRequest` and `HttpResponse` that Express uses, results in `req, res` being undefined in context.
+
+Because of this, Elysia replaces both with `context` like route parameters.
+
+```typescript
+const app = new Elysia()
+ .use(
+ apollo({
+ typeDefs,
+ resolvers,
+ context: async ({ request }) => {
+ const authorization = request.headers.get('Authorization')
+
+ return {
+ authorization
+ }
+ }
+ })
+ )
+ .listen(3000)
+```
+
+## Config
+
+This plugin extends Apollo's [ServerRegistration](https://www.apollographql.com/docs/apollo-server/api/apollo-server/#options) (which is `ApolloServer`'s' constructor parameter).
+
+Below are the extended parameters for configuring Apollo Server with Elysia.
+
+### path
+
+@default `"/graphql"`
+
+Path to expose Apollo Server.
+
+---
+
+### enablePlayground
+
+@default `process.env.ENV !== 'production'`
+
+Determine whether should Apollo should provide Apollo Playground.
diff --git a/.agents/skills/elysiajs/plugins/graphql-yoga.md b/.agents/skills/elysiajs/plugins/graphql-yoga.md
new file mode 100644
index 0000000..3203d02
--- /dev/null
+++ b/.agents/skills/elysiajs/plugins/graphql-yoga.md
@@ -0,0 +1,87 @@
+# GraphQL Yoga
+
+This plugin integrates GraphQL yoga with Elysia
+
+## Installation
+```bash
+bun add @elysiajs/graphql-yoga
+```
+
+## Basic Usage
+```typescript
+import { Elysia } from 'elysia'
+import { yoga } from '@elysiajs/graphql-yoga'
+
+const app = new Elysia()
+ .use(
+ yoga({
+ typeDefs: /* GraphQL */ `
+ type Query {
+ hi: String
+ }
+ `,
+ resolvers: {
+ Query: {
+ hi: () => 'Hello from Elysia'
+ }
+ }
+ })
+ )
+ .listen(3000)
+```
+
+Accessing `/graphql` in the browser (GET request) would show you a GraphiQL instance for the GraphQL-enabled Elysia server.
+
+optional: you can install a custom version of optional peer dependencies as well:
+
+```bash
+bun add graphql graphql-yoga
+```
+
+## Resolver
+
+Elysia uses Mobius to infer type from **typeDefs** field automatically, allowing you to get full type-safety and auto-complete when typing **resolver** types.
+
+## Context
+
+You can add custom context to the resolver function by adding **context**
+
+```ts
+import { Elysia } from 'elysia'
+import { yoga } from '@elysiajs/graphql-yoga'
+
+const app = new Elysia()
+ .use(
+ yoga({
+ typeDefs: /* GraphQL */ `
+ type Query {
+ hi: String
+ }
+ `,
+ context: {
+ name: 'Mobius'
+ },
+ // If context is a function on this doesn't present
+ // for some reason it won't infer context type
+ useContext(_) {},
+ resolvers: {
+ Query: {
+ hi: async (parent, args, context) => context.name
+ }
+ }
+ })
+ )
+ .listen(3000)
+```
+
+## Config
+
+This plugin extends [GraphQL Yoga's createYoga options, please refer to the GraphQL Yoga documentation](https://the-guild.dev/graphql/yoga-server/docs) with inlining `schema` config to root.
+
+Below is a config which is accepted by the plugin
+
+### path
+
+@default `/graphql`
+
+Endpoint to expose GraphQL handler
diff --git a/.agents/skills/elysiajs/plugins/html.md b/.agents/skills/elysiajs/plugins/html.md
new file mode 100644
index 0000000..777a536
--- /dev/null
+++ b/.agents/skills/elysiajs/plugins/html.md
@@ -0,0 +1,188 @@
+# HTML
+
+Allows you to use JSX and HTML with proper headers and support.
+
+## Installation
+
+```bash
+bun add @elysiajs/html
+```
+
+## Basic Usage
+```tsx twoslash
+import React from 'react'
+import { Elysia } from 'elysia'
+import { html, Html } from '@elysiajs/html'
+
+new Elysia()
+ .use(html())
+ .get(
+ '/html',
+ () => `
+
+
+ Hello World
+
+
+ Hello World
+
+ `
+ )
+ .get('/jsx', () => (
+
+
+ Hello World
+
+
+ Hello World
+
+
+ ))
+ .listen(3000)
+```
+
+This plugin will automatically add `Content-Type: text/html; charset=utf8` header to the response, add ``, and convert it into a Response object.
+
+## JSX
+Elysia can use JSX
+
+1. Replace your file that needs to use JSX to end with affix **"x"**:
+- .js -> .jsx
+- .ts -> .tsx
+
+2. Register the TypeScript type by append the following to **tsconfig.json**:
+```jsonc
+// tsconfig.json
+{
+ "compilerOptions": {
+ "jsx": "react",
+ "jsxFactory": "Html.createElement",
+ "jsxFragmentFactory": "Html.Fragment"
+ }
+}
+```
+
+3. Starts using JSX in your file
+```tsx twoslash
+import React from 'react'
+import { Elysia } from 'elysia'
+import { html, Html } from '@elysiajs/html'
+
+new Elysia()
+ .use(html())
+ .get('/', () => (
+
+
+ Hello World
+
+
+ Hello World
+
+
+ ))
+ .listen(3000)
+```
+
+If the error `Cannot find name 'Html'. Did you mean 'html'?` occurs, this import must be added to the JSX template:
+
+```tsx
+import { Html } from '@elysiajs/html'
+```
+
+It is important that it is written in uppercase.
+
+## XSS
+
+Elysia HTML is based use of the Kita HTML plugin to detect possible XSS attacks in compile time.
+
+You can use a dedicated `safe` attribute to sanitize user value to prevent XSS vulnerability.
+
+```tsx
+import { Elysia, t } from 'elysia'
+import { html, Html } from '@elysiajs/html'
+
+new Elysia()
+ .use(html())
+ .post(
+ '/',
+ ({ body }) => (
+
+
+ Hello World
+
+
+ {body}
+
+
+ ),
+ {
+ body: t.String()
+ }
+ )
+ .listen(3000)
+```
+
+However, when are building a large-scale app, it's best to have a type reminder to detect possible XSS vulnerabilities in your codebase.
+
+To add a type-safe reminder, please install:
+
+```sh
+bun add @kitajs/ts-html-plugin
+```
+
+Then appends the following **tsconfig.json**
+
+```jsonc
+// tsconfig.json
+{
+ "compilerOptions": {
+ "jsx": "react",
+ "jsxFactory": "Html.createElement",
+ "jsxFragmentFactory": "Html.Fragment",
+ "plugins": [{ "name": "@kitajs/ts-html-plugin" }]
+ }
+}
+```
+
+## Config
+Below is a config which is accepted by the plugin
+
+### contentType
+
+- Type: `string`
+- Default: `'text/html; charset=utf8'`
+
+The content-type of the response.
+
+### autoDetect
+
+- Type: `boolean`
+- Default: `true`
+
+Whether to automatically detect HTML content and set the content-type.
+
+### autoDoctype
+
+- Type: `boolean | 'full'`
+- Default: `true`
+
+Whether to automatically add `` to a response starting with ``, if not found.
+
+Use `full` to also automatically add doctypes on responses returned without this plugin
+
+```ts
+// without the plugin
+app.get('/', () => '')
+
+// With the plugin
+app.get('/', ({ html }) => html(''))
+```
+
+### isHtml
+
+- Type: `(value: string) => boolean`
+- Default: `isHtml` (exported function)
+
+The function is used to detect if a string is a html or not. Default implementation if length is greater than 7, starts with `<` and ends with `>`.
+
+Keep in mind there's no real way to validate HTML, so the default implementation is a best guess.
diff --git a/.agents/skills/elysiajs/plugins/jwt.md b/.agents/skills/elysiajs/plugins/jwt.md
new file mode 100644
index 0000000..b5767bf
--- /dev/null
+++ b/.agents/skills/elysiajs/plugins/jwt.md
@@ -0,0 +1,197 @@
+# JWT Plugin
+This plugin adds support for using JWT in Elysia handlers.
+
+## Installation
+```bash
+bun add @elysiajs/jwt
+```
+
+## Basic Usage
+```typescript [cookie]
+import { Elysia } from 'elysia'
+import { jwt } from '@elysiajs/jwt'
+
+const app = new Elysia()
+ .use(
+ jwt({
+ name: 'jwt',
+ secret: 'Fischl von Luftschloss Narfidort'
+ })
+ )
+ .get('/sign/:name', async ({ jwt, params: { name }, cookie: { auth } }) => {
+ const value = await jwt.sign({ name })
+
+ auth.set({
+ value,
+ httpOnly: true,
+ maxAge: 7 * 86400,
+ path: '/profile',
+ })
+
+ return `Sign in as ${value}`
+ })
+ .get('/profile', async ({ jwt, status, cookie: { auth } }) => {
+ const profile = await jwt.verify(auth.value)
+
+ if (!profile)
+ return status(401, 'Unauthorized')
+
+ return `Hello ${profile.name}`
+ })
+ .listen(3000)
+```
+
+## Config
+This plugin extends config from [jose](https://github.com/panva/jose).
+
+Below is a config that is accepted by the plugin.
+
+### name
+Name to register `jwt` function as.
+
+For example, `jwt` function will be registered with a custom name.
+```typescript
+new Elysia()
+ .use(
+ jwt({
+ name: 'myJWTNamespace',
+ secret: process.env.JWT_SECRETS!
+ })
+ )
+ .get('/sign/:name', ({ myJWTNamespace, params }) => {
+ return myJWTNamespace.sign(params)
+ })
+```
+
+Because some might need to use multiple `jwt` with different configs in a single server, explicitly registering the JWT function with a different name is needed.
+
+### secret
+The private key to sign JWT payload with.
+
+### schema
+Type strict validation for JWT payload.
+
+### alg
+@default `HS256`
+
+Signing Algorithm to sign JWT payload with.
+
+Possible properties for jose are:
+HS256
+HS384
+HS512
+PS256
+PS384
+PS512
+RS256
+RS384
+RS512
+ES256
+ES256K
+ES384
+ES512
+EdDSA
+
+### iss
+The issuer claim identifies the principal that issued the JWT as per [RFC7519](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.1)
+
+TLDR; is usually (the domain) name of the signer.
+
+### sub
+The subject claim identifies the principal that is the subject of the JWT.
+
+The claims in a JWT are normally statements about the subject as per [RFC7519](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.2)
+
+### aud
+The audience claim identifies the recipients that the JWT is intended for.
+
+Each principal intended to process the JWT MUST identify itself with a value in the audience claim as per [RFC7519](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.3)
+
+### jti
+JWT ID claim provides a unique identifier for the JWT as per [RFC7519](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.7)
+
+### nbf
+The "not before" claim identifies the time before which the JWT must not be accepted for processing as per [RFC7519](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.5)
+
+### exp
+The expiration time claim identifies the expiration time on or after which the JWT MUST NOT be accepted for processing as per [RFC7519](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.4)
+
+### iat
+The "issued at" claim identifies the time at which the JWT was issued.
+
+This claim can be used to determine the age of the JWT as per [RFC7519](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.6)
+
+### b64
+This JWS Extension Header Parameter modifies the JWS Payload representation and the JWS Signing input computation as per [RFC7797](https://www.rfc-editor.org/rfc/rfc7797).
+
+### kid
+A hint indicating which key was used to secure the JWS.
+
+This parameter allows originators to explicitly signal a change of key to recipients as per [RFC7515](https://www.rfc-editor.org/rfc/rfc7515#section-4.1.4)
+
+### x5t
+(X.509 certificate SHA-1 thumbprint) header parameter is a base64url-encoded SHA-1 digest of the DER encoding of the X.509 certificate [RFC5280](https://www.rfc-editor.org/rfc/rfc5280) corresponding to the key used to digitally sign the JWS as per [RFC7515](https://www.rfc-editor.org/rfc/rfc7515#section-4.1.7)
+
+### x5c
+(X.509 certificate chain) header parameter contains the X.509 public key certificate or certificate chain [RFC5280](https://www.rfc-editor.org/rfc/rfc5280) corresponding to the key used to digitally sign the JWS as per [RFC7515](https://www.rfc-editor.org/rfc/rfc7515#section-4.1.6)
+
+### x5u
+(X.509 URL) header parameter is a URI [RFC3986](https://www.rfc-editor.org/rfc/rfc3986) that refers to a resource for the X.509 public key certificate or certificate chain [RFC5280] corresponding to the key used to digitally sign the JWS as per [RFC7515](https://www.rfc-editor.org/rfc/rfc7515#section-4.1.5)
+
+### jwk
+The "jku" (JWK Set URL) Header Parameter is a URI [RFC3986] that refers to a resource for a set of JSON-encoded public keys, one of which corresponds to the key used to digitally sign the JWS.
+
+The keys MUST be encoded as a JWK Set [JWK] as per [RFC7515](https://www.rfc-editor.org/rfc/rfc7515#section-4.1.2)
+
+### typ
+The `typ` (type) Header Parameter is used by JWS applications to declare the media type [IANA.MediaTypes] of this complete JWS.
+
+This is intended for use by the application when more than one kind of object could be present in an application data structure that can contain a JWS as per [RFC7515](https://www.rfc-editor.org/rfc/rfc7515#section-4.1.9)
+
+### ctr
+Content-Type parameter is used by JWS applications to declare the media type [IANA.MediaTypes] of the secured content (the payload).
+
+This is intended for use by the application when more than one kind of object could be present in the JWS Payload as per [RFC7515](https://www.rfc-editor.org/rfc/rfc7515#section-4.1.9)
+
+## Handler
+Below are the value added to the handler.
+
+### jwt.sign
+A dynamic object of collection related to use with JWT registered by the JWT plugin.
+
+Type:
+```typescript
+sign: (payload: JWTPayloadSpec): Promise
+```
+
+`JWTPayloadSpec` accepts the same value as [JWT config](#config)
+
+### jwt.verify
+Verify payload with the provided JWT config
+
+Type:
+```typescript
+verify(payload: string) => Promise
+```
+
+`JWTPayloadSpec` accepts the same value as [JWT config](#config)
+
+## Pattern
+Below you can find the common patterns to use the plugin.
+
+## Set JWT expiration date
+By default, the config is passed to `setCookie` and inherits its value.
+
+```typescript
+const app = new Elysia()
+ .use(
+ jwt({
+ name: 'jwt',
+ secret: 'kunikuzushi',
+ exp: '7d'
+ })
+ )
+ .get('/sign/:name', async ({ jwt, params }) => jwt.sign(params))
+```
+
+This will sign JWT with an expiration date of the next 7 days.
diff --git a/.agents/skills/elysiajs/plugins/openapi.md b/.agents/skills/elysiajs/plugins/openapi.md
new file mode 100644
index 0000000..c69150d
--- /dev/null
+++ b/.agents/skills/elysiajs/plugins/openapi.md
@@ -0,0 +1,246 @@
+# OpenAPI Plugin
+
+## Installation
+```bash
+bun add @elysiajs/openapi
+```
+
+## Basic Usage
+```typescript
+import { openapi } from '@elysiajs/openapi'
+
+new Elysia()
+ .use(openapi())
+ .get('/', () => 'hello')
+```
+
+Docs at `/openapi`, spec at `/openapi/json`.
+
+## Detail Object
+Extends OpenAPI Operation Object:
+```typescript
+.get('/', () => 'hello', {
+ detail: {
+ title: 'Hello',
+ description: 'An example route',
+ summary: 'Short summary',
+ deprecated: false,
+ hide: true, // Hide from docs
+ tags: ['App']
+ }
+})
+```
+
+### Documentation Config
+```typescript
+openapi({
+ documentation: {
+ info: {
+ title: 'API',
+ version: '1.0.0'
+ },
+ tags: [
+ { name: 'App', description: 'General' }
+ ],
+ components: {
+ securitySchemes: {
+ bearerAuth: { type: 'http', scheme: 'bearer' }
+ }
+ }
+ }
+})
+```
+
+### Standard Schema Mapping
+```typescript
+mapJsonSchema: {
+ zod: z.toJSONSchema, // Zod 4
+ valibot: toJsonSchema,
+ effect: JSONSchema.make
+}
+```
+
+Zod 3: `zodToJsonSchema` from `zod-to-json-schema`
+
+## OpenAPI Type Gen
+Generate docs from types:
+```typescript
+import { fromTypes } from '@elysiajs/openapi'
+
+export const app = new Elysia()
+ .use(openapi({
+ references: fromTypes()
+ }))
+```
+
+### Production
+Recommended to generate `.d.ts` file for production when using OpenAPI Type Gen
+```typescript
+references: fromTypes(
+ process.env.NODE_ENV === 'production'
+ ? 'dist/index.d.ts'
+ : 'src/index.ts'
+)
+```
+
+### Options
+```typescript
+fromTypes('src/index.ts', {
+ projectRoot: path.join('..', import.meta.dir),
+ tsconfigPath: 'tsconfig.dts.json'
+})
+```
+
+### Caveat: Explicit Types
+Use `Prettify` helper to inline when type is not showing:
+```typescript
+type Prettify = { [K in keyof T]: T[K] } & {}
+
+function getUser(): Prettify { }
+```
+
+## Schema Description
+```typescript
+body: t.Object({
+ username: t.String(),
+ password: t.String({
+ minLength: 8,
+ description: 'Password (8+ chars)'
+ })
+}, {
+ description: 'Expected username and password'
+}),
+detail: {
+ summary: 'Sign in user',
+ tags: ['auth']
+}
+```
+
+## Response Headers
+```typescript
+import { withHeader } from '@elysiajs/openapi'
+
+response: withHeader(
+ t.Literal('Hi'),
+ { 'x-powered-by': t.Literal('Elysia') }
+)
+```
+
+Annotation only - doesn't enforce. Set headers manually.
+
+## Tags
+Define + assign:
+```typescript
+.use(openapi({
+ documentation: {
+ tags: [
+ { name: 'App', description: 'General' },
+ { name: 'Auth', description: 'Auth' }
+ ]
+ }
+}))
+.get('/', () => 'hello', {
+ detail: { tags: ['App'] }
+})
+```
+
+### Instance Tags
+```typescript
+new Elysia({ tags: ['user'] })
+ .get('/user', 'user')
+```
+
+## Reference Models
+Auto-generates schemas:
+```typescript
+.model({
+ User: t.Object({
+ id: t.Number(),
+ username: t.String()
+ })
+})
+.get('/user', () => ({ id: 1, username: 'x' }), {
+ response: { 200: 'User' },
+ detail: { tags: ['User'] }
+})
+```
+
+## Guard
+Apply to instance/group:
+```typescript
+.guard({
+ detail: {
+ description: 'Requires auth'
+ }
+})
+.get('/user', 'user')
+```
+
+## Security
+```typescript
+.use(openapi({
+ documentation: {
+ components: {
+ securitySchemes: {
+ bearerAuth: {
+ type: 'http',
+ scheme: 'bearer',
+ bearerFormat: 'JWT'
+ }
+ }
+ }
+ }
+}))
+
+new Elysia({
+ prefix: '/address',
+ detail: {
+ security: [{ bearerAuth: [] }]
+ }
+})
+```
+
+Secures all routes under prefix.
+
+## Config
+Below is a config which is accepted by the `openapi({})`
+
+### enabled
+@default true
+Enable/Disable the plugin
+
+### documentation
+OpenAPI documentation information
+@see https://spec.openapis.org/oas/v3.0.3.html
+
+### exclude
+Configuration to exclude paths or methods from documentation
+
+### exclude.methods
+List of methods to exclude from documentation
+
+### exclude.paths
+List of paths to exclude from documentation
+
+### exclude.staticFile
+@default true
+
+Exclude static file routes from documentation
+
+### exclude.tags
+List of tags to exclude from documentation
+
+### mapJsonSchema
+A custom mapping function from Standard schema to OpenAPI schema
+
+### path
+@default '/openapi'
+The endpoint to expose OpenAPI documentation frontend
+
+### provider
+@default 'scalar'
+
+OpenAPI documentation frontend between:
+- Scalar
+- SwaggerUI
+- null: disable frontend
diff --git a/.agents/skills/elysiajs/plugins/opentelemetry.md b/.agents/skills/elysiajs/plugins/opentelemetry.md
new file mode 100644
index 0000000..0ca95c3
--- /dev/null
+++ b/.agents/skills/elysiajs/plugins/opentelemetry.md
@@ -0,0 +1,167 @@
+# OpenTelemetry Plugin - SKILLS.md
+
+## Installation
+```bash
+bun add @elysiajs/opentelemetry
+```
+
+## Basic Usage
+```typescript
+import { opentelemetry } from '@elysiajs/opentelemetry'
+import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-node'
+import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto'
+
+new Elysia()
+ .use(opentelemetry({
+ spanProcessors: [
+ new BatchSpanProcessor(new OTLPTraceExporter())
+ ]
+ }))
+```
+
+Auto-collects spans from OpenTelemetry-compatible libraries. Parent/child spans applied automatically.
+
+## Config
+Extends OpenTelemetry SDK params:
+
+- `autoDetectResources` (true) - Auto-detect from env
+- `contextManager` (AsyncHooksContextManager) - Custom context
+- `textMapPropagator` (CompositePropagator) - W3C Trace + Baggage
+- `metricReader` - For MeterProvider
+- `views` - Histogram bucket config
+- `instrumentations` (getNodeAutoInstrumentations()) - Metapackage or individual
+- `resource` - Custom resource
+- `resourceDetectors` ([envDetector, processDetector, hostDetector]) - Auto-detect needs `autoDetectResources: true`
+- `sampler` - Custom sampler (default: sample all)
+- `serviceName` - Namespace identifier
+- `spanProcessors` - Array for tracer provider
+- `traceExporter` - Auto-setup OTLP/http/protobuf with BatchSpanProcessor if not set
+- `spanLimits` - Tracing params
+
+### Resource Detectors via Env
+```bash
+export OTEL_NODE_RESOURCE_DETECTORS="env,host"
+# Options: env, host, os, process, serviceinstance, all, none
+```
+
+## Export to Backends
+Example - Axiom:
+```typescript
+.use(opentelemetry({
+ spanProcessors: [
+ new BatchSpanProcessor(
+ new OTLPTraceExporter({
+ url: 'https://api.axiom.co/v1/traces',
+ headers: {
+ Authorization: `Bearer ${Bun.env.AXIOM_TOKEN}`,
+ 'X-Axiom-Dataset': Bun.env.AXIOM_DATASET
+ }
+ })
+ )
+ ]
+}))
+```
+
+## OpenTelemetry SDK
+Use SDK normally - runs under Elysia's request span, auto-appears in trace.
+
+## Record Utility
+Equivalent to `startActiveSpan` - auto-closes + captures exceptions:
+```typescript
+import { record } from '@elysiajs/opentelemetry'
+
+.get('', () => {
+ return record('database.query', () => {
+ return db.query('SELECT * FROM users')
+ })
+})
+```
+
+Label for code shown in trace.
+
+## Function Naming
+Elysia reads function names as span names:
+```typescript
+// ⚠️ Anonymous span
+.derive(async ({ cookie: { session } }) => {
+ return { user: await getProfile(session) }
+})
+
+// ✅ Named span: "getProfile"
+.derive(async function getProfile({ cookie: { session } }) {
+ return { user: await getProfile(session) }
+})
+```
+
+## getCurrentSpan
+Get current span outside handler (via AsyncLocalStorage):
+```typescript
+import { getCurrentSpan } from '@elysiajs/opentelemetry'
+
+function utility() {
+ const span = getCurrentSpan()
+ span.setAttributes({ 'custom.attribute': 'value' })
+}
+```
+
+## setAttributes
+Sugar for `getCurrentSpan().setAttributes`:
+```typescript
+import { setAttributes } from '@elysiajs/opentelemetry'
+
+function utility() {
+ setAttributes({ 'custom.attribute': 'value' })
+}
+```
+
+## Instrumentations (Advanced)
+SDK must run before importing instrumented module.
+
+### Setup
+1. Separate file:
+```typescript
+// src/instrumentation.ts
+import { opentelemetry } from '@elysiajs/opentelemetry'
+import { PgInstrumentation } from '@opentelemetry/instrumentation-pg'
+
+export const instrumentation = opentelemetry({
+ instrumentations: [new PgInstrumentation()]
+})
+```
+
+2. Apply:
+```typescript
+// src/index.ts
+import { instrumentation } from './instrumentation'
+new Elysia().use(instrumentation).listen(3000)
+```
+
+3. Preload:
+```toml
+# bunfig.toml
+preload = ["./src/instrumentation.ts"]
+```
+
+### Production Deployment (Advanced)
+OpenTelemetry monkey-patches `node_modules`. Exclude instrumented libs from bundling:
+```bash
+bun build --compile --external pg --outfile server src/index.ts
+```
+
+Package.json:
+```json
+{
+ "dependencies": { "pg": "^8.15.6" },
+ "devDependencies": {
+ "@elysiajs/opentelemetry": "^1.2.0",
+ "@opentelemetry/instrumentation-pg": "^0.52.0"
+ }
+}
+```
+
+Production install:
+```bash
+bun install --production
+```
+
+Keeps `node_modules` with instrumented libs at runtime.
diff --git a/.agents/skills/elysiajs/plugins/server-timing.md b/.agents/skills/elysiajs/plugins/server-timing.md
new file mode 100644
index 0000000..0021424
--- /dev/null
+++ b/.agents/skills/elysiajs/plugins/server-timing.md
@@ -0,0 +1,71 @@
+# Server Timing Plugin
+This plugin adds support for auditing performance bottlenecks with Server Timing API
+
+## Installation
+```bash
+bun add @elysiajs/server-timing
+```
+
+## Basic Usage
+```typescript twoslash
+import { Elysia } from 'elysia'
+import { serverTiming } from '@elysiajs/server-timing'
+
+new Elysia()
+ .use(serverTiming())
+ .get('/', () => 'hello')
+ .listen(3000)
+```
+
+Server Timing then will append header 'Server-Timing' with log duration, function name, and detail for each life-cycle function.
+
+To inspect, open browser developer tools > Network > [Request made through Elysia server] > Timing.
+
+Now you can effortlessly audit the performance bottleneck of your server.
+
+## Config
+Below is a config which is accepted by the plugin
+
+### enabled
+@default `NODE_ENV !== 'production'`
+
+Determine whether or not Server Timing should be enabled
+
+### allow
+@default `undefined`
+
+A condition whether server timing should be log
+
+### trace
+@default `undefined`
+
+Allow Server Timing to log specified life-cycle events:
+
+Trace accepts objects of the following:
+- request: capture duration from request
+- parse: capture duration from parse
+- transform: capture duration from transform
+- beforeHandle: capture duration from beforeHandle
+- handle: capture duration from the handle
+- afterHandle: capture duration from afterHandle
+- total: capture total duration from start to finish
+
+## Pattern
+Below you can find the common patterns to use the plugin.
+
+## Allow Condition
+You may disable Server Timing on specific routes via `allow` property
+
+```ts twoslash
+import { Elysia } from 'elysia'
+import { serverTiming } from '@elysiajs/server-timing'
+
+new Elysia()
+ .use(
+ serverTiming({
+ allow: ({ request }) => {
+ return new URL(request.url).pathname !== '/no-trace'
+ }
+ })
+ )
+```
diff --git a/.agents/skills/elysiajs/plugins/static.md b/.agents/skills/elysiajs/plugins/static.md
new file mode 100644
index 0000000..82fa1da
--- /dev/null
+++ b/.agents/skills/elysiajs/plugins/static.md
@@ -0,0 +1,84 @@
+# Static Plugin
+This plugin can serve static files/folders for Elysia Server
+
+## Installation
+```bash
+bun add @elysiajs/static
+```
+
+## Basic Usage
+```typescript twoslash
+import { Elysia } from 'elysia'
+import { staticPlugin } from '@elysiajs/static'
+
+new Elysia()
+ .use(staticPlugin())
+ .listen(3000)
+```
+
+By default, the static plugin default folder is `public`, and registered with `/public` prefix.
+
+Suppose your project structure is:
+```
+| - src
+ | - index.ts
+| - public
+ | - takodachi.png
+ | - nested
+ | - takodachi.png
+```
+
+The available path will become:
+- /public/takodachi.png
+- /public/nested/takodachi.png
+
+## Config
+Below is a config which is accepted by the plugin
+
+### assets
+@default `"public"`
+
+Path to the folder to expose as static
+
+### prefix
+@default `"/public"`
+
+Path prefix to register public files
+
+### ignorePatterns
+@default `[]`
+
+List of files to ignore from serving as static files
+
+### staticLimit
+@default `1024`
+
+By default, the static plugin will register paths to the Router with a static name, if the limits are exceeded, paths will be lazily added to the Router to reduce memory usage.
+Tradeoff memory with performance.
+
+### alwaysStatic
+@default `false`
+
+If set to true, static files path will be registered to Router skipping the `staticLimits`.
+
+### headers
+@default `{}`
+
+Set response headers of files
+
+### indexHTML
+@default `false`
+
+If set to true, the `index.html` file from the static directory will be served for any request that is matching neither a route nor any existing static file.
+
+## Pattern
+Below you can find the common patterns to use the plugin.
+
+## Single file
+Suppose you want to return just a single file, you can use `file` instead of using the static plugin
+```typescript
+import { Elysia, file } from 'elysia'
+
+new Elysia()
+ .get('/file', file('public/takodachi.png'))
+```
diff --git a/.agents/skills/elysiajs/references/bun-fullstack-dev-server.md b/.agents/skills/elysiajs/references/bun-fullstack-dev-server.md
new file mode 100644
index 0000000..70d721b
--- /dev/null
+++ b/.agents/skills/elysiajs/references/bun-fullstack-dev-server.md
@@ -0,0 +1,129 @@
+# Fullstack Dev Server
+
+## What It Is
+Bun 1.3 Fullstack Dev Server with HMR. React without bundler (no Vite/Webpack).
+
+Example: [elysia-fullstack-example](https://github.com/saltyaom/elysia-fullstack-example)
+
+## Setup
+1. Install + use Elysia Static:
+```typescript
+import { Elysia } from 'elysia'
+import { staticPlugin } from '@elysiajs/static'
+
+new Elysia()
+ .use(await staticPlugin()) // await required for HMR hooks
+ .listen(3000)
+```
+
+2. Create `public/index.html` + `public/index.tsx`:
+```html
+
+
+
+
+
+ Elysia React App
+
+
+
+
+
+
+
+```
+
+```tsx
+// public/index.tsx
+import { useState } from 'react'
+import { createRoot } from 'react-dom/client'
+
+function App() {
+ const [count, setCount] = useState(0)
+ const increase = () => setCount((c) => c + 1)
+
+ return (
+
+ {count}
+ Increase
+
+ )
+}
+
+const root = createRoot(document.getElementById('root')!)
+root.render( )
+```
+
+3. Enable JSX in `tsconfig.json`:
+```json
+{
+ "compilerOptions": {
+ "jsx": "react-jsx"
+ }
+}
+```
+
+4. Navigate to `http://localhost:3000/public`.
+
+Frontend + backend in single project. No bundler. Works with HMR, Tailwind, Tanstack Query, Eden Treaty, path alias.
+
+## Custom Prefix
+```typescript
+.use(await staticPlugin({ prefix: '/' }))
+```
+
+Serves at `/` instead of `/public`.
+
+## Tailwind CSS
+1. Install:
+```bash
+bun add tailwindcss@4
+bun add -d bun-plugin-tailwind
+```
+
+2. Create `bunfig.toml`:
+```toml
+[serve.static]
+plugins = ["bun-plugin-tailwind"]
+```
+
+3. Create `public/global.css`:
+```css
+@tailwind base;
+```
+
+4. Add to HTML or TS:
+```html
+
+```
+Or:
+```tsx
+import './global.css'
+```
+
+## Path Alias
+1. Add to `tsconfig.json`:
+```json
+{
+ "compilerOptions": {
+ "baseUrl": ".",
+ "paths": {
+ "@public/*": ["public/*"]
+ }
+ }
+}
+```
+
+2. Use:
+```tsx
+import '@public/global.css'
+```
+
+Works out of box.
+
+## Production Build
+```bash
+bun build --compile --target bun --outfile server src/index.ts
+```
+
+Creates single executable `server`. Include `public` folder when running.
diff --git a/.agents/skills/elysiajs/references/cookie.md b/.agents/skills/elysiajs/references/cookie.md
new file mode 100644
index 0000000..9e1aa1c
--- /dev/null
+++ b/.agents/skills/elysiajs/references/cookie.md
@@ -0,0 +1,187 @@
+# Cookie
+
+## What It Is
+Reactive mutable signal for cookie interaction. Auto-encodes/decodes objects.
+
+## Basic Usage
+No get/set - direct value access:
+```typescript
+import { Elysia } from 'elysia'
+
+new Elysia()
+ .get('/', ({ cookie: { name } }) => {
+ // Get
+ name.value
+
+ // Set
+ name.value = "New Value"
+ })
+```
+
+Auto-encodes/decodes objects. Just works.
+
+## Reactivity
+Signal-like approach. Single source of truth. Auto-sets headers, syncs values.
+
+Cookie jar = Proxy object. Extract value always `Cookie`, never `undefined`. Access via `.value`.
+
+Iterate over cookie jar → only existing cookies.
+
+## Cookie Attributes
+
+### Direct Property Assignment
+```typescript
+.get('/', ({ cookie: { name } }) => {
+ // Get
+ name.domain
+
+ // Set
+ name.domain = 'millennium.sh'
+ name.httpOnly = true
+})
+```
+
+### set - Reset All Properties
+```typescript
+.get('/', ({ cookie: { name } }) => {
+ name.set({
+ domain: 'millennium.sh',
+ httpOnly: true
+ })
+})
+```
+
+Overwrites all properties.
+
+### add - Update Specific Properties
+Like `set` but only overwrites defined properties.
+
+## Remove Cookie
+```typescript
+.get('/', ({ cookie, cookie: { name } }) => {
+ name.remove()
+ // or
+ delete cookie.name
+})
+```
+
+## Cookie Schema
+Strict validation + type inference with `t.Cookie`:
+```typescript
+import { Elysia, t } from 'elysia'
+
+new Elysia()
+ .get('/', ({ cookie: { name } }) => {
+ name.value = {
+ id: 617,
+ name: 'Summoning 101'
+ }
+ }, {
+ cookie: t.Cookie({
+ name: t.Object({
+ id: t.Numeric(),
+ name: t.String()
+ })
+ })
+ })
+```
+
+### Nullable Cookie
+```typescript
+cookie: t.Cookie({
+ name: t.Optional(
+ t.Object({
+ id: t.Numeric(),
+ name: t.String()
+ })
+ )
+})
+```
+
+## Cookie Signature
+Cryptographic hash for verification. Prevents malicious modification.
+
+```typescript
+new Elysia()
+ .get('/', ({ cookie: { profile } }) => {
+ profile.value = { id: 617, name: 'Summoning 101' }
+ }, {
+ cookie: t.Cookie({
+ profile: t.Object({
+ id: t.Numeric(),
+ name: t.String()
+ })
+ }, {
+ secrets: 'Fischl von Luftschloss Narfidort',
+ sign: ['profile']
+ })
+ })
+```
+
+Auto-signs/unsigns.
+
+### Global Config
+```typescript
+new Elysia({
+ cookie: {
+ secrets: 'Fischl von Luftschloss Narfidort',
+ sign: ['profile']
+ }
+})
+```
+
+## Cookie Rotation
+Auto-handles secret rotation. Old signature verification + new signature signing.
+
+```typescript
+new Elysia({
+ cookie: {
+ secrets: ['Vengeance will be mine', 'Fischl von Luftschloss Narfidort']
+ }
+})
+```
+
+Array = key rotation (retire old, replace with new).
+
+## Config
+
+### secrets
+Secret key for signing/unsigning. Array = key rotation.
+
+### domain
+Domain Set-Cookie attribute. Default: none (current domain only).
+
+### encode
+Function to encode value. Default: `encodeURIComponent`.
+
+### expires
+Date for Expires attribute. Default: none (non-persistent, deleted on browser exit).
+
+If both `expires` and `maxAge` set, `maxAge` takes precedence (spec-compliant clients).
+
+### httpOnly (false)
+HttpOnly attribute. If true, JS can't access via `document.cookie`.
+
+### maxAge (undefined)
+Seconds for Max-Age attribute. Rounded down to integer.
+
+If both `expires` and `maxAge` set, `maxAge` takes precedence (spec-compliant clients).
+
+### path
+Path attribute. Default: handler path.
+
+### priority
+Priority attribute: `low` | `medium` | `high`. Not fully standardized.
+
+### sameSite
+SameSite attribute:
+- `true` = Strict
+- `false` = not set
+- `'lax'` = Lax
+- `'none'` = None (explicit cross-site)
+- `'strict'` = Strict
+
+Not fully standardized.
+
+### secure
+Secure attribute. If true, only HTTPS. Clients won't send over HTTP.
diff --git a/.agents/skills/elysiajs/references/deployment.md b/.agents/skills/elysiajs/references/deployment.md
new file mode 100644
index 0000000..3c4cca8
--- /dev/null
+++ b/.agents/skills/elysiajs/references/deployment.md
@@ -0,0 +1,413 @@
+# Deployment
+
+## Production Build
+
+### Compile to Binary (Recommended)
+```bash
+bun build \
+ --compile \
+ --minify-whitespace \
+ --minify-syntax \
+ --target bun \
+ --outfile server \
+ src/index.ts
+```
+
+**Benefits:**
+- No runtime needed on deployment server
+- Smaller memory footprint (2-3x reduction)
+- Faster startup
+- Single portable executable
+
+**Run the binary:**
+```bash
+./server
+```
+
+### Compile to JavaScript
+```bash
+bun build \
+ --minify-whitespace \
+ --minify-syntax \
+ --outfile ./dist/index.js \
+ src/index.ts
+```
+
+**Run:**
+```bash
+NODE_ENV=production bun ./dist/index.js
+```
+
+## Docker
+
+### Basic Dockerfile
+```dockerfile
+FROM oven/bun:1 AS build
+
+WORKDIR /app
+
+# Cache dependencies
+COPY package.json bun.lock ./
+RUN bun install
+
+COPY ./src ./src
+
+ENV NODE_ENV=production
+
+RUN bun build \
+ --compile \
+ --minify-whitespace \
+ --minify-syntax \
+ --outfile server \
+ src/index.ts
+
+FROM gcr.io/distroless/base
+
+WORKDIR /app
+
+COPY --from=build /app/server server
+
+ENV NODE_ENV=production
+
+CMD ["./server"]
+
+EXPOSE 3000
+```
+
+### Build and Run
+```bash
+docker build -t my-elysia-app .
+docker run -p 3000:3000 my-elysia-app
+```
+
+### With Environment Variables
+```dockerfile
+FROM gcr.io/distroless/base
+
+WORKDIR /app
+
+COPY --from=build /app/server server
+
+ENV NODE_ENV=production
+ENV PORT=3000
+ENV DATABASE_URL=""
+ENV JWT_SECRET=""
+
+CMD ["./server"]
+
+EXPOSE 3000
+```
+
+## Cluster Mode (Multiple CPU Cores)
+
+```typescript
+// src/index.ts
+import cluster from 'node:cluster'
+import os from 'node:os'
+import process from 'node:process'
+
+if (cluster.isPrimary) {
+ for (let i = 0; i < os.availableParallelism(); i++) {
+ cluster.fork()
+ }
+} else {
+ await import('./server')
+ console.log(`Worker ${process.pid} started`)
+}
+```
+
+```typescript
+// src/server.ts
+import { Elysia } from 'elysia'
+
+new Elysia()
+ .get('/', () => 'Hello World!')
+ .listen(3000)
+```
+
+## Environment Variables
+
+### .env File
+```env
+NODE_ENV=production
+PORT=3000
+DATABASE_URL=postgresql://user:password@localhost:5432/db
+JWT_SECRET=your-secret-key
+CORS_ORIGIN=https://example.com
+```
+
+### Load in App
+```typescript
+import { Elysia } from 'elysia'
+
+const app = new Elysia()
+ .get('/env', () => ({
+ env: process.env.NODE_ENV,
+ port: process.env.PORT
+ }))
+ .listen(parseInt(process.env.PORT || '3000'))
+```
+
+## Platform-Specific Deployments
+
+### Railway
+```typescript
+// Railway assigns random PORT via env variable
+new Elysia()
+ .get('/', () => 'Hello Railway')
+ .listen(process.env.PORT ?? 3000)
+```
+
+### Vercel
+```typescript
+// src/index.ts
+import { Elysia } from 'elysia'
+
+export default new Elysia()
+ .get('/', () => 'Hello Vercel')
+
+export const GET = app.fetch
+export const POST = app.fetch
+```
+
+```json
+// vercel.json
+{
+ "$schema": "https://openapi.vercel.sh/vercel.json",
+ "bunVersion": "1.x"
+}
+```
+
+### Cloudflare Workers
+```typescript
+import { Elysia } from 'elysia'
+import { CloudflareAdapter } from 'elysia/adapter/cloudflare-worker'
+
+export default new Elysia({
+ adapter: CloudflareAdapter
+})
+ .get('/', () => 'Hello Cloudflare!')
+ .compile()
+```
+
+```toml
+# wrangler.toml
+name = "elysia-app"
+main = "src/index.ts"
+compatibility_date = "2025-06-01"
+```
+
+### Node.js Adapter
+```typescript
+import { Elysia } from 'elysia'
+import { node } from '@elysiajs/node'
+
+const app = new Elysia({ adapter: node() })
+ .get('/', () => 'Hello Node.js')
+ .listen(3000)
+```
+
+## Performance Optimization
+
+### Enable AoT Compilation
+```typescript
+new Elysia({
+ aot: true // Ahead-of-time compilation
+})
+```
+
+### Use Native Static Response
+```typescript
+new Elysia({
+ nativeStaticResponse: true
+})
+ .get('/version', 1) // Optimized for Bun.serve.static
+```
+
+### Precompile Routes
+```typescript
+new Elysia({
+ precompile: true // Compile all routes ahead of time
+})
+```
+
+## Health Checks
+
+```typescript
+new Elysia()
+ .get('/health', () => ({
+ status: 'ok',
+ timestamp: Date.now()
+ }))
+ .get('/ready', ({ db }) => {
+ // Check database connection
+ const isDbReady = checkDbConnection()
+
+ if (!isDbReady) {
+ return status(503, { status: 'not ready' })
+ }
+
+ return { status: 'ready' }
+ })
+```
+
+## Graceful Shutdown
+
+```typescript
+import { Elysia } from 'elysia'
+
+const app = new Elysia()
+ .get('/', () => 'Hello')
+ .listen(3000)
+
+process.on('SIGTERM', () => {
+ console.log('SIGTERM received, shutting down gracefully')
+ app.stop()
+ process.exit(0)
+})
+
+process.on('SIGINT', () => {
+ console.log('SIGINT received, shutting down gracefully')
+ app.stop()
+ process.exit(0)
+})
+```
+
+## Monitoring
+
+### OpenTelemetry
+```typescript
+import { opentelemetry } from '@elysiajs/opentelemetry'
+
+new Elysia()
+ .use(opentelemetry({
+ serviceName: 'my-service',
+ endpoint: 'http://localhost:4318'
+ }))
+```
+
+### Custom Logging
+```typescript
+.onRequest(({ request }) => {
+ console.log(`[${new Date().toISOString()}] ${request.method} ${request.url}`)
+})
+.onAfterResponse(({ request, set }) => {
+ console.log(`[${new Date().toISOString()}] ${request.method} ${request.url} - ${set.status}`)
+})
+```
+
+## SSL/TLS (HTTPS)
+
+```typescript
+import { Elysia, file } from 'elysia'
+
+new Elysia({
+ serve: {
+ tls: {
+ cert: file('cert.pem'),
+ key: file('key.pem')
+ }
+ }
+})
+ .get('/', () => 'Hello HTTPS')
+ .listen(3000)
+```
+
+## Best Practices
+
+1. **Always compile to binary for production**
+ - Reduces memory usage
+ - Smaller deployment size
+ - No runtime needed
+
+2. **Use environment variables**
+ - Never hardcode secrets
+ - Use different configs per environment
+
+3. **Enable health checks**
+ - Essential for load balancers
+ - K8s/Docker orchestration
+
+4. **Implement graceful shutdown**
+ - Handle SIGTERM/SIGINT
+ - Close connections properly
+
+5. **Use cluster mode**
+ - Utilize all CPU cores
+ - Better performance under load
+
+6. **Monitor your app**
+ - Use OpenTelemetry
+ - Log requests/responses
+ - Track errors
+
+## Example Production Setup
+
+```typescript
+// src/server.ts
+import { Elysia } from 'elysia'
+import { cors } from '@elysiajs/cors'
+import { opentelemetry } from '@elysiajs/opentelemetry'
+
+export const app = new Elysia({
+ aot: true,
+ nativeStaticResponse: true
+})
+ .use(cors({
+ origin: process.env.CORS_ORIGIN || 'http://localhost:3000'
+ }))
+ .use(opentelemetry({
+ serviceName: 'my-service'
+ }))
+ .get('/health', () => ({ status: 'ok' }))
+ .get('/', () => 'Hello Production')
+ .listen(parseInt(process.env.PORT || '3000'))
+
+// Graceful shutdown
+process.on('SIGTERM', () => {
+ app.stop()
+ process.exit(0)
+})
+```
+
+```typescript
+// src/index.ts (cluster)
+import cluster from 'node:cluster'
+import os from 'node:os'
+
+if (cluster.isPrimary) {
+ for (let i = 0; i < os.availableParallelism(); i++) {
+ cluster.fork()
+ }
+} else {
+ await import('./server')
+}
+```
+
+```dockerfile
+# Dockerfile
+FROM oven/bun:1 AS build
+
+WORKDIR /app
+
+COPY package.json bun.lock ./
+RUN bun install
+
+COPY ./src ./src
+
+ENV NODE_ENV=production
+
+RUN bun build --compile --outfile server src/index.ts
+
+FROM gcr.io/distroless/base
+
+WORKDIR /app
+
+COPY --from=build /app/server server
+
+ENV NODE_ENV=production
+
+CMD ["./server"]
+
+EXPOSE 3000
+```
diff --git a/.agents/skills/elysiajs/references/eden.md b/.agents/skills/elysiajs/references/eden.md
new file mode 100644
index 0000000..7d9165d
--- /dev/null
+++ b/.agents/skills/elysiajs/references/eden.md
@@ -0,0 +1,158 @@
+# Eden Treaty
+e2e type safe RPC client for share type from backend to frontend.
+
+## What It Is
+Type-safe object representation for Elysia server. Auto-completion + error handling.
+
+## Installation
+```bash
+bun add @elysiajs/eden
+bun add -d elysia
+```
+
+Export Elysia server type:
+```typescript
+const app = new Elysia()
+ .get('/', () => 'Hi Elysia')
+ .get('/id/:id', ({ params: { id } }) => id)
+ .post('/mirror', ({ body }) => body, {
+ body: t.Object({
+ id: t.Number(),
+ name: t.String()
+ })
+ })
+ .listen(3000)
+
+export type App = typeof app
+```
+
+Consume on client side:
+```typescript
+import { treaty } from '@elysiajs/eden'
+import type { App } from './server'
+
+const client = treaty('localhost:3000')
+
+// response: Hi Elysia
+const { data: index } = await client.get()
+
+// response: 1895
+const { data: id } = await client.id({ id: 1895 }).get()
+
+// response: { id: 1895, name: 'Skadi' }
+const { data: nendoroid } = await client.mirror.post({
+ id: 1895,
+ name: 'Skadi'
+})
+```
+
+## Common Errors & Fixes
+- **Strict mode**: Enable in tsconfig
+- **Version mismatch**: `npm why elysia` - must match server/client
+- **TypeScript**: Min 5.0
+- **Method chaining**: Required on server
+- **Bun types**: `bun add -d @types/bun` if using Bun APIs
+- **Path alias**: Must resolve same on frontend/backend
+
+### Monorepo Path Alias
+Must resolve to same file on frontend/backend
+
+```json
+// tsconfig.json at root
+{
+ "compilerOptions": {
+ "baseUrl": ".",
+ "paths": {
+ "@frontend/*": ["./apps/frontend/src/*"],
+ "@backend/*": ["./apps/backend/src/*"]
+ }
+ }
+}
+```
+
+## Syntax Mapping
+| Path | Method | Treaty |
+|----------------|--------|-------------------------------|
+| / | GET | `.get()` |
+| /hi | GET | `.hi.get()` |
+| /deep/nested | POST | `.deep.nested.post()` |
+| /item/:name | GET | `.item({ name: 'x' }).get()` |
+
+## Parameters
+
+### With body (POST/PUT/PATCH/DELETE):
+```typescript
+.user.post(
+ { name: 'Elysia' }, // body
+ { headers: {}, query: {}, fetch: {} } // optional
+)
+```
+
+### No body (GET/HEAD):
+```typescript
+.hello.get({ headers: {}, query: {}, fetch: {} })
+```
+
+### Empty body with query/headers:
+```typescript
+.user.post(null, { query: { name: 'Ely' } })
+```
+
+### Fetch options:
+```typescript
+.hello.get({ fetch: { signal: controller.signal } })
+```
+
+### File upload:
+```typescript
+// Accepts: File | File[] | FileList | Blob
+.image.post({
+ title: 'Title',
+ image: fileInput.files!
+})
+```
+
+## Response
+```typescript
+const { data, error, response, status, headers } = await api.user.post({ name: 'x' })
+
+if (error) {
+ switch (error.status) {
+ case 400: throw error.value
+ default: throw error.value
+ }
+}
+// data unwrapped after error handling
+return data
+```
+
+status >= 300 → `data = null`, `error` has value
+
+## Stream/SSE
+Interpreted as `AsyncGenerator`:
+```typescript
+const { data, error } = await treaty(app).ok.get()
+if (error) throw error
+
+for await (const chunk of data) console.log(chunk)
+```
+
+## Utility Types
+```typescript
+import { Treaty } from '@elysiajs/eden'
+
+type UserData = Treaty.Data
+type UserError = Treaty.Error
+```
+
+## WebSocket
+```typescript
+const chat = api.chat.subscribe()
+
+chat.subscribe((message) => console.log('got', message))
+chat.on('open', () => chat.send('hello'))
+
+// Native access: chat.raw
+```
+
+`.subscribe()` accepts same params as `get`/`head`
diff --git a/.agents/skills/elysiajs/references/lifecycle.md b/.agents/skills/elysiajs/references/lifecycle.md
new file mode 100644
index 0000000..645584e
--- /dev/null
+++ b/.agents/skills/elysiajs/references/lifecycle.md
@@ -0,0 +1,198 @@
+# Lifecycle
+
+Instead of a sequential process, Elysia's request handling is divided into multiple stages called lifecycle events.
+
+It's designed to separate the process into distinct phases based on their responsibility without interfering with each others.
+
+### List of events in order
+
+1. **request** - early, global
+2. **parse** - body parsing
+3. **transform** / **derive** - mutate context pre validation
+4. **beforeHandle** / **resolve** - auth/guard logic
+5. **handler** - your business code
+6. **afterHandle** - tweak response, set headers
+7. **mapResponse** - turn anything into a proper `Response`
+8. **onError** - centralized error handling
+9. **onAfterResponse** - post response/cleanup tasks
+
+## Request (`onRequest`)
+
+Runs first for every incoming request.
+
+- Ideal for **caching, rate limiting, CORS, adding global headers**.
+- If the hook returns a value, the whole lifecycle stops and that value becomes the response.
+
+```ts
+new Elysia().onRequest(({ ip, set }) => {
+ if (blocked(ip)) return (set.status = 429)
+})
+```
+
+---
+
+## Parse (`onParse`)
+
+_Body parsing stage._
+
+- Handles `text/plain`, `application/json`, `multipart/form-data`, `application/x www-form-urlencoded` by default.
+- Use to add **custom parsers** or support extra `Content Type`s.
+
+```ts
+new Elysia().onParse(({ request, contentType }) => {
+ if (contentType === 'application/custom') return request.text()
+})
+```
+
+---
+
+## Transform (`onTransform`)
+
+_Runs **just before validation**; can mutate the request context._
+
+- Perfect for **type coercion**, trimming strings, or adding temporary fields that validation will use.
+
+```ts
+new Elysia().onTransform(({ params }) => {
+ params.id = Number(params.id)
+})
+```
+
+---
+
+## Derive
+
+_Runs along with `onTransform` **but before validation**; adds per request values to the context._
+
+- Useful for extracting info from headers, cookies, query, etc., that you want to reuse in handlers.
+
+```ts
+new Elysia().derive(({ headers }) => ({
+ bearer: headers.authorization?.replace(/^Bearer /, '')
+}))
+```
+
+---
+
+## Before Handle (`onBeforeHandle`)
+
+_Executed after validation, right before the route handler._
+
+- Great for **auth checks, permission gating, custom pre validation logic**.
+- Returning a value skips the handler.
+
+```ts
+new Elysia().get('/', () => 'hi', {
+ beforeHandle({ cookie, status }) {
+ if (!cookie.session) return status(401)
+ }
+})
+```
+
+---
+
+## Resolve
+
+_Like `derive` but runs **after validation** along "Before Handle" (so you can rely on validated data)._
+
+- Usually placed inside a `guard` because it isn't available as a local hook.
+
+```ts
+new Elysia().guard(
+ { headers: t.Object({ authorization: t.String() }) },
+ (app) =>
+ app
+ .resolve(({ headers }) => ({
+ bearer: headers.authorization.split(' ')[1]
+ }))
+ .get('/', ({ bearer }) => bearer)
+)
+```
+
+---
+
+## After Handle (`onAfterHandle`)
+
+_Runs after the handler finishes._
+
+- Can **modify response headers**, wrap the result in a `Response`, or transform the payload.
+- Returning a value **replaces** the handler’s result, but the next `afterHandle` hooks still run.
+
+```ts
+new Elysia().get('/', () => 'Hello ', {
+ afterHandle({ response, set }) {
+ if (isHtml(response)) {
+ set.headers['content-type'] = 'text/html; charset=utf-8'
+ return new Response(response)
+ }
+ }
+})
+```
+
+---
+
+## Map Response (`mapResponse`)
+
+_Runs right after all `afterHandle` hooks; maps **any** value to a Web standard `Response`._
+
+- Ideal for **compression, custom content type mapping, streaming**.
+
+```ts
+new Elysia().mapResponse(({ responseValue, set }) => {
+ const body =
+ typeof responseValue === 'object'
+ ? JSON.stringify(responseValue)
+ : String(responseValue ?? '')
+
+ set.headers['content-encoding'] = 'gzip'
+ return new Response(Bun.gzipSync(new TextEncoder().encode(body)), {
+ headers: {
+ 'Content-Type':
+ typeof responseValue === 'object'
+ ? 'application/json'
+ : 'text/plain'
+ }
+ })
+})
+```
+
+---
+
+## On Error (`onError`)
+
+_Caught whenever an error bubbles up from any lifecycle stage._
+
+- Use to **customize error messages**, **handle 404**, **log**, or **retry**.
+- Must be registered **before** the routes it should protect.
+
+```ts
+new Elysia().onError(({ code, status }) => {
+ if (code === 'NOT_FOUND') return status(404, 'â“ Not found')
+ return new Response('Oops', { status: 500 })
+})
+```
+
+---
+
+## After Response (`onAfterResponse`)
+
+_Runs **after** the response has been sent to the client._
+
+- Perfect for **logging, metrics, cleanup**.
+
+```ts
+new Elysia().onAfterResponse(() =>
+ console.log('✅ response sent at', Date.now())
+)
+```
+
+---
+
+## Hook Types
+
+| Type | Scope | How to add |
+| -------------------- | --------------------------------- | --------------------------------------------------------- |
+| **Local Hook** | Single route | Inside route options (`afterHandle`, `beforeHandle`, …) |
+| **Interceptor Hook** | Whole instance (and later routes) | `.onXxx(cb)` or `.use(plugin)` |
+
+> **Remember:** Hooks only affect routes **defined after** they are registered, except `onRequest` which is global because it runs before route matching.
diff --git a/.agents/skills/elysiajs/references/macro.md b/.agents/skills/elysiajs/references/macro.md
new file mode 100644
index 0000000..f89ee75
--- /dev/null
+++ b/.agents/skills/elysiajs/references/macro.md
@@ -0,0 +1,83 @@
+# Macro
+
+Composable Elysia function for controlling lifecycle/schema/context with full type safety. Available in hook after definition control by key-value label.
+
+## Basic Pattern
+```typescript
+.macro({
+ hi: (word: string) => ({
+ beforeHandle() { console.log(word) }
+ })
+})
+.get('/', () => 'hi', { hi: 'Elysia' })
+```
+
+## Property Shorthand
+Object → function accepting boolean:
+```typescript
+.macro({
+ // These equivalent:
+ isAuth: { resolve: () => ({ user: 'saltyaom' }) },
+ isAuth(enabled: boolean) { if(enabled) return { resolve() {...} } }
+})
+```
+
+## Error Handling
+Return `status`, don't throw:
+```typescript
+.macro({
+ auth: {
+ resolve({ headers }) {
+ if(!headers.authorization) return status(401, 'Unauthorized')
+ return { user: 'SaltyAom' }
+ }
+ }
+})
+```
+
+## Resolve - Add Context Props
+```typescript
+.macro({
+ user: (enabled: true) => ({
+ resolve: () => ({ user: 'Pardofelis' })
+ })
+})
+.get('/', ({ user }) => user, { user: true })
+```
+
+### Named Macro for Type Inference
+TypeScript limitation workaround:
+```typescript
+.macro('user', { resolve: () => ({ user: 'lilith' }) })
+.macro('user2', { user: true, resolve: ({ user }) => {} })
+```
+
+## Schema
+Auto-validates, infers types, stacks with other schemas:
+```typescript
+.macro({
+ withFriends: {
+ body: t.Object({ friends: t.Tuple([...]) })
+ }
+})
+```
+
+Use named single macro for lifecycle type inference within same macro.
+
+## Extension
+Stack macros:
+```typescript
+.macro({
+ sartre: { body: t.Object({...}) },
+ fouco: { body: t.Object({...}) },
+ lilith: { fouco: true, sartre: true, body: t.Object({...}) }
+})
+```
+
+## Deduplication
+Auto-dedupes by property value. Custom seed:
+```typescript
+.macro({ sartre: (role: string) => ({ seed: role, ... }) })
+```
+
+Max stack: 16 (prevents infinite loops)
diff --git a/.agents/skills/elysiajs/references/plugin.md b/.agents/skills/elysiajs/references/plugin.md
new file mode 100644
index 0000000..cd10e64
--- /dev/null
+++ b/.agents/skills/elysiajs/references/plugin.md
@@ -0,0 +1,207 @@
+# Plugins
+
+## Plugin = Decoupled Elysia Instance
+
+```ts
+const plugin = new Elysia()
+ .decorate('plugin', 'hi')
+ .get('/plugin', ({ plugin }) => plugin)
+
+const app = new Elysia()
+ .use(plugin) // inherit properties
+ .get('/', ({ plugin }) => plugin)
+```
+
+**Inherits**: state, decorate
+**Does NOT inherit**: lifecycle (isolated by default)
+
+## Dependency
+
+Each instance runs independently like microservice. **Must explicitly declare dependencies**.
+
+```ts
+const auth = new Elysia()
+ .decorate('Auth', Auth)
+
+// ❌ Missing dependency
+const main = new Elysia()
+ .get('/', ({ Auth }) => Auth.getProfile())
+
+// ✅ Declare dependency
+const main = new Elysia()
+ .use(auth) // required for Auth
+ .get('/', ({ Auth }) => Auth.getProfile())
+```
+
+## Deduplication
+
+**Every plugin re-executes by default**. Use `name` + optional `seed` to deduplicate:
+
+```ts
+const ip = new Elysia({ name: 'ip' }) // unique identifier
+ .derive({ as: 'global' }, ({ server, request }) => ({
+ ip: server?.requestIP(request)
+ }))
+
+const router1 = new Elysia().use(ip)
+const router2 = new Elysia().use(ip)
+const server = new Elysia().use(router1).use(router2)
+// `ip` only executes once due to deduplication
+```
+
+## Global vs Explicit Dependency
+
+**Global plugin** (rare, apply everywhere):
+- Doesn't add types - cors, compress, helmet
+- Global lifecycle no instance controls - tracing, logging
+- Examples: OpenAPI docs, OpenTelemetry, logging
+
+**Explicit dependency** (default, recommended):
+- Adds types - macro, state, model
+- Business logic instances interact with - Auth, DB
+- Examples: state management, ORM, auth, features
+
+## Scope
+
+**Lifecycle isolated by default**. Must specify scope to export.
+
+```ts
+// ❌ NOT inherited by app
+const profile = new Elysia()
+ .onBeforeHandle(({ cookie }) => throwIfNotSignIn(cookie))
+ .get('/profile', () => 'Hi')
+
+const app = new Elysia()
+ .use(profile)
+ .patch('/rename', ({ body }) => updateProfile(body)) // No sign-in check
+
+// ✅ Exported to app
+const profile = new Elysia()
+ .onBeforeHandle({ as: 'global' }, ({ cookie }) => throwIfNotSignIn(cookie))
+ .get('/profile', () => 'Hi')
+```
+
+## Scope Levels
+
+1. **local** (default) - current + descendants only
+2. **scoped** - parent + current + descendants
+3. **global** - all instances (all parents, current, descendants)
+
+Example with `.onBeforeHandle({ as: 'local' }, ...)`:
+
+| type | child | current | parent | main |
+|------|-------|---------|--------|------|
+| local | ✅ | ✅ | ❌ | ❌ |
+| scoped | ✅ | ✅ | ✅ | ❌ |
+| global | ✅ | ✅ | ✅ | ✅ |
+
+## Config
+
+```ts
+// Instance factory with config
+const version = (v = 1) => new Elysia()
+ .get('/version', v)
+
+const app = new Elysia()
+ .use(version(1))
+```
+
+## Functional Callback (not recommended)
+
+```ts
+// Harder to handle scope/encapsulation
+const plugin = (app: Elysia) => app
+ .state('counter', 0)
+ .get('/plugin', () => 'Hi')
+
+// Prefer new instance (better type inference, no perf diff)
+```
+
+## Guard (Apply to Multiple Routes)
+
+```ts
+.guard(
+ { body: t.Object({ username: t.String(), password: t.String() }) },
+ (app) =>
+ app.post('/sign-up', ({ body }) => signUp(body))
+ .post('/sign-in', ({ body }) => signIn(body))
+)
+```
+
+**Grouped guard** (merge group + guard):
+
+```ts
+.group(
+ '/v1',
+ { body: t.Literal('Rikuhachima Aru') }, // guard here
+ (app) => app.post('/student', ({ body }) => body)
+)
+```
+
+## Scope Casting
+
+**3 methods to apply hook to parent**:
+
+1. **Inline as** (single hook):
+```ts
+.derive({ as: 'scoped' }, () => ({ hi: 'ok' }))
+```
+
+2. **Guard as** (multiple hooks, no derive/resolve):
+```ts
+.guard({
+ as: 'scoped',
+ response: t.String(),
+ beforeHandle() { console.log('ok') }
+})
+```
+
+3. **Instance as** (all hooks + schema):
+```ts
+const plugin = new Elysia()
+ .derive(() => ({ hi: 'ok' }))
+ .get('/child', ({ hi }) => hi)
+ .as('scoped') // lift scope up
+```
+
+`.as()` lifts scope: local → scoped → global
+
+## Lazy Load
+
+**Deferred module** (async plugin, non-blocking startup):
+
+```ts
+// plugin.ts
+export const loadStatic = async (app: Elysia) => {
+ const files = await loadAllFiles()
+ files.forEach((asset) => app.get(asset, file(asset)))
+ return app
+}
+
+// main.ts
+const app = new Elysia().use(loadStatic)
+```
+
+**Lazy-load module** (dynamic import):
+
+```ts
+const app = new Elysia()
+ .use(import('./plugin')) // loaded after startup
+```
+
+**Testing** (wait for modules):
+
+```ts
+await app.modules // ensure all deferred/lazy modules loaded
+```
+
+## Notes
+[Inference] Based on docs patterns:
+- Use inline values for static resources (performance optimization)
+- Group routes by prefix for organization
+- Extend context minimally (separation of concerns)
+- Use `status()` over `set.status` for type safety
+- Prefer `resolve()` over `derive()` when type integrity matters
+- Plugins isolated by default (must declare scope explicitly)
+- Use `name` for deduplication when plugin used multiple times
+- Prefer explicit dependency over global (better modularity/tracking)
diff --git a/.agents/skills/elysiajs/references/route.md b/.agents/skills/elysiajs/references/route.md
new file mode 100644
index 0000000..c767283
--- /dev/null
+++ b/.agents/skills/elysiajs/references/route.md
@@ -0,0 +1,331 @@
+# ElysiaJS: Routing, Handlers & Context
+
+## Routing
+
+### Path Types
+
+```ts
+new Elysia()
+ .get('/static', 'static path') // exact match
+ .get('/id/:id', 'dynamic path') // captures segment
+ .get('/id/*', 'wildcard path') // captures rest
+```
+
+**Path Priority**: static > dynamic > wildcard
+
+### Dynamic Paths
+
+```ts
+new Elysia()
+ .get('/id/:id', ({ params: { id } }) => id)
+ .get('/id/:id/:name', ({ params: { id, name } }) => id + ' ' + name)
+```
+
+**Optional params**: `.get('/id/:id?', ...)`
+
+### HTTP Verbs
+
+- `.get()` - retrieve data
+- `.post()` - submit/create
+- `.put()` - replace
+- `.patch()` - partial update
+- `.delete()` - remove
+- `.all()` - any method
+- `.route(method, path, handler)` - custom verb
+
+### Grouping Routes
+
+```ts
+new Elysia()
+ .group('/user', { body: t.Literal('auth') }, (app) =>
+ app.post('/sign-in', ...)
+ .post('/sign-up', ...)
+)
+
+// Or use prefix in constructor
+new Elysia({ prefix: '/user' })
+ .post('/sign-in', ...)
+```
+
+## Handlers
+
+### Handler = function accepting HTTP request, returning response
+
+```ts
+// Inline value (compiled ahead, optimized)
+.get('/', 'Hello Elysia')
+.get('/video', file('video.mp4'))
+
+// Function handler
+.get('/', () => 'hello')
+.get('/', ({ params, query, body }) => {...})
+```
+
+### Context Properties
+
+- `body` - HTTP message/form/file
+- `query` - query string as object
+- `params` - path parameters
+- `headers` - HTTP headers
+- `cookie` - mutable signal for cookies
+- `store` - global mutable state
+- `request` - Web Standard Request
+- `server` - Bun server instance
+- `path` - request pathname
+
+### Context Utilities
+
+```ts
+import { redirect, form } from 'elysia'
+
+new Elysia().get('/', ({ status, set, form }) => {
+ // Status code (type-safe)
+ status(418, "I'm a teapot")
+
+ // Set response props
+ set.headers['x-custom'] = 'value'
+ set.status = 418 // legacy, no type inference
+
+ // Redirect
+ return redirect('https://...', 302)
+
+ // Cookies (mutable signal, no get/set)
+ cookie.name.value // get
+ cookie.name.value = 'new' // set
+
+ // FormData response
+ return form({ name: 'Party', images: [file('a.jpg')] })
+
+ // Single file
+ return file('document.pdf')
+})
+```
+
+### Streaming
+
+```ts
+new Elysia()
+ .get('/stream', function* () {
+ yield 1
+ yield 2
+ yield 3
+ })
+ // Server-Sent Events
+ .get('/sse', function* () {
+ yield sse('hello')
+ yield sse({ event: 'msg', data: {...} })
+ })
+```
+
+**Note**: Headers only settable before first yield
+
+**Conditional stream**: returning without yield converts to normal response
+
+## Context Extension
+
+[Inference] Extend when property is:
+
+- Global mutable (use `state`)
+- Request/response related (use `decorate`)
+- Derived from existing props (use `derive`/`resolve`)
+
+### state() - Global Mutable
+
+```ts
+new Elysia()
+ `.state('version', 1)
+ .get('/', ({ store: { version } }) => version)
+ // Multiple
+ .state({ counter: 0, visits: 0 })
+
+ // Remap (create new from existing)
+ .state(({ version, ...store }) => ({
+ ...store,
+ apiVersion: version
+ }))
+````
+
+**Gotcha**: Use reference not value
+
+```ts
+new Elysia()
+ // ✅ Correct
+ .get('/', ({ store }) => store.counter++)
+
+ // ❌ Wrong - loses reference
+ .get('/', ({ store: { counter } }) => counter++)
+```
+
+### decorate() - Additional Context Props
+
+```ts
+new Elysia()
+ .decorate('logger', new Logger())
+ .get('/', ({ logger }) => logger.log('hi'))
+
+ // Multiple
+ .decorate({ logger: new Logger(), db: connection })
+```
+
+**When**: constant/readonly values, classes with internal state, singletons
+
+### derive() - Create from Existing (Transform Lifecycle)
+
+```ts
+new Elysia()
+ .derive(({ headers }) => ({
+ bearer: headers.authorization?.startsWith('Bearer ')
+ ? headers.authorization.slice(7)
+ : null
+ }))
+ .get('/', ({ bearer }) => bearer)
+```
+
+**Timing**: runs at transform (before validation)
+**Type safety**: request props typed as `unknown`
+
+### resolve() - Type-Safe Derive (beforeHandle Lifecycle)
+
+```ts
+new Elysia()
+ .guard({
+ headers: t.Object({
+ bearer: t.String({ pattern: '^Bearer .+$' })
+ })
+ })
+ .resolve(({ headers }) => ({
+ bearer: headers.bearer.slice(7) // typed correctly
+ }))
+```
+
+**Timing**: runs at beforeHandle (after validation)
+**Type safety**: request props fully typed
+
+### Error from derive/resolve
+
+```ts
+new Elysia()
+ .derive(({ headers, status }) => {
+ if (!headers.authorization) return status(400)
+ return { bearer: ... }
+ })
+```
+
+Returns early if error returned
+
+## Patterns
+
+### Affix (Bulk Remap)
+
+```ts
+const plugin = new Elysia({ name: 'setup' }).decorate({
+ argon: 'a',
+ boron: 'b'
+})
+
+new Elysia()
+ .use(plugin)
+ .prefix('decorator', 'setup') // setupArgon, setupBoron
+ .prefix('all', 'setup') // remap everything
+```
+
+### Assignment Patterns
+
+1. **key-value**: `.state('key', value)`
+2. **object**: `.state({ k1: v1, k2: v2 })`
+3. **remap**: `.state(({old}) => ({new}))`
+
+## Testing
+
+```ts
+const app = new Elysia().get('/', 'hi')
+
+// Programmatic test
+app.handle(new Request('http://localhost/'))
+```
+
+## To Throw or Return
+
+Most of an error handling in Elysia can be done by throwing an error and will be handle in `onError`.
+
+But for `status` it can be a little bit confusing, since it can be used both as a return value or throw an error.
+
+It could either be **return** or **throw** based on your specific needs.
+
+- If an `status` is **throw**, it will be caught by `onError` middleware.
+- If an `status` is **return**, it will be **NOT** caught by `onError` middleware.
+
+See the following code:
+
+```typescript
+import { Elysia, file } from 'elysia'
+
+new Elysia()
+ .onError(({ code, error, path }) => {
+ if (code === 418) return 'caught'
+ })
+ .get('/throw', ({ status }) => {
+ // This will be caught by onError
+ throw status(418)
+ })
+ .get('/return', ({ status }) => {
+ // This will NOT be caught by onError
+ return status(418)
+ })
+```
+
+## To Throw or Return
+
+Elysia provide a `status` function for returning HTTP status code, prefers over `set.status`.
+
+`status` can be import from Elysia but preferably extract from route handler Context for type safety.
+
+```ts
+import { Elysia, status } from 'elysia'
+
+function doThing() {
+ if (Math.random() > 0.33) return status(418, "I'm a teapot")
+}
+
+new Elysia().get('/', ({ status }) => {
+ if (Math.random() > 0.33) return status(418)
+
+ return 'ok'
+})
+```
+
+Error Handling in Elysia can be done by throwing an error and will be handle in `onError`.
+
+Status could either be **return** or **throw** based on your specific needs.
+
+- If an `status` is **throw**, it will be caught by `onError` middleware.
+- If an `status` is **return**, it will be **NOT** caught by `onError` middleware.
+
+See the following code:
+
+```typescript
+import { Elysia, file } from 'elysia'
+
+new Elysia()
+ .onError(({ code, error, path }) => {
+ if (code === 418) return 'caught'
+ })
+ .get('/throw', ({ status }) => {
+ // This will be caught by onError
+ throw status(418)
+ })
+ .get('/return', ({ status }) => {
+ // This will NOT be caught by onError
+ return status(418)
+ })
+```
+
+## Notes
+
+[Inference] Based on docs patterns:
+
+- Use inline values for static resources (performance optimization)
+- Group routes by prefix for organization
+- Extend context minimally (separation of concerns)
+- Use `status()` over `set.status` for type safety
+- Prefer `resolve()` over `derive()` when type integrity matters
diff --git a/.agents/skills/elysiajs/references/testing.md b/.agents/skills/elysiajs/references/testing.md
new file mode 100644
index 0000000..ffcdff3
--- /dev/null
+++ b/.agents/skills/elysiajs/references/testing.md
@@ -0,0 +1,385 @@
+# Unit Testing
+
+## Basic Test Setup
+
+### Installation
+```bash
+bun add -d @elysiajs/eden
+```
+
+### Basic Test
+```typescript
+// test/app.test.ts
+import { describe, expect, it } from 'bun:test'
+import { Elysia } from 'elysia'
+
+describe('Elysia App', () => {
+ it('should return hello world', async () => {
+ const app = new Elysia()
+ .get('/', () => 'Hello World')
+
+ const res = await app.handle(
+ new Request('http://localhost/')
+ )
+
+ expect(res.status).toBe(200)
+ expect(await res.text()).toBe('Hello World')
+ })
+})
+```
+
+## Testing Routes
+
+### GET Request
+```typescript
+it('should get user by id', async () => {
+ const app = new Elysia()
+ .get('/user/:id', ({ params: { id } }) => ({
+ id,
+ name: 'John Doe'
+ }))
+
+ const res = await app.handle(
+ new Request('http://localhost/user/123')
+ )
+
+ const data = await res.json()
+
+ expect(res.status).toBe(200)
+ expect(data).toEqual({
+ id: '123',
+ name: 'John Doe'
+ })
+})
+```
+
+### POST Request
+```typescript
+it('should create user', async () => {
+ const app = new Elysia()
+ .post('/user', ({ body }) => body)
+
+ const res = await app.handle(
+ new Request('http://localhost/user', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({
+ name: 'Jane Doe',
+ email: 'jane@example.com'
+ })
+ })
+ )
+
+ const data = await res.json()
+
+ expect(res.status).toBe(200)
+ expect(data.name).toBe('Jane Doe')
+})
+```
+
+## Testing Module/Plugin
+
+### Module Structure
+```
+src/
+├── modules/
+│ └── auth/
+│ ├── index.ts # Elysia instance
+│ ├── service.ts
+│ └── model.ts
+└── index.ts
+```
+
+### Auth Module
+```typescript
+// src/modules/auth/index.ts
+import { Elysia, t } from 'elysia'
+
+export const authModule = new Elysia({ prefix: '/auth' })
+ .post('/login', ({ body, cookie: { session } }) => {
+ if (body.username === 'admin' && body.password === 'password') {
+ session.value = 'valid-session'
+ return { success: true }
+ }
+ return { success: false }
+ }, {
+ body: t.Object({
+ username: t.String(),
+ password: t.String()
+ })
+ })
+ .get('/profile', ({ cookie: { session }, status }) => {
+ if (!session.value) {
+ return status(401, { error: 'Unauthorized' })
+ }
+ return { username: 'admin' }
+ })
+```
+
+### Auth Module Test
+```typescript
+// test/auth.test.ts
+import { describe, expect, it } from 'bun:test'
+import { authModule } from '../src/modules/auth'
+
+describe('Auth Module', () => {
+ it('should login successfully', async () => {
+ const res = await authModule.handle(
+ new Request('http://localhost/auth/login', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({
+ username: 'admin',
+ password: 'password'
+ })
+ })
+ )
+
+ const data = await res.json()
+ expect(res.status).toBe(200)
+ expect(data.success).toBe(true)
+ })
+
+ it('should reject invalid credentials', async () => {
+ const res = await authModule.handle(
+ new Request('http://localhost/auth/login', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({
+ username: 'wrong',
+ password: 'wrong'
+ })
+ })
+ )
+
+ const data = await res.json()
+ expect(data.success).toBe(false)
+ })
+
+ it('should return 401 for unauthenticated profile request', async () => {
+ const res = await authModule.handle(
+ new Request('http://localhost/auth/profile')
+ )
+
+ expect(res.status).toBe(401)
+ })
+})
+```
+
+## Eden Treaty Testing
+
+### Setup
+```typescript
+import { treaty } from '@elysiajs/eden'
+import { app } from '../src/modules/auth'
+
+const api = treaty(app)
+```
+
+### Eden Tests
+```typescript
+describe('Auth Module with Eden', () => {
+ it('should login with Eden', async () => {
+ const { data, error } = await api.auth.login.post({
+ username: 'admin',
+ password: 'password'
+ })
+
+ expect(error).toBeNull()
+ expect(data?.success).toBe(true)
+ })
+
+ it('should get profile with Eden', async () => {
+ // First login
+ await api.auth.login.post({
+ username: 'admin',
+ password: 'password'
+ })
+
+ // Then get profile
+ const { data, error } = await api.auth.profile.get()
+
+ expect(error).toBeNull()
+ expect(data?.username).toBe('admin')
+ })
+})
+```
+
+## Mocking Dependencies
+
+### With Decorators
+```typescript
+// app.ts
+export const app = new Elysia()
+ .decorate('db', realDatabase)
+ .get('/users', ({ db }) => db.getUsers())
+
+// test
+import { app } from '../src/app'
+
+describe('App with mocked DB', () => {
+ it('should use mock database', async () => {
+ const mockDb = {
+ getUsers: () => [{ id: 1, name: 'Test User' }]
+ }
+
+ const testApp = app.decorate('db', mockDb)
+
+ const res = await testApp.handle(
+ new Request('http://localhost/users')
+ )
+
+ const data = await res.json()
+ expect(data).toEqual([{ id: 1, name: 'Test User' }])
+ })
+})
+```
+
+## Testing with Headers
+
+```typescript
+it('should require authorization', async () => {
+ const app = new Elysia()
+ .get('/protected', ({ headers, status }) => {
+ if (!headers.authorization) {
+ return status(401)
+ }
+ return { data: 'secret' }
+ })
+
+ const res = await app.handle(
+ new Request('http://localhost/protected', {
+ headers: {
+ 'Authorization': 'Bearer token123'
+ }
+ })
+ )
+
+ expect(res.status).toBe(200)
+})
+```
+
+## Testing Validation
+
+```typescript
+import { Elysia, t } from 'elysia'
+
+it('should validate request body', async () => {
+ const app = new Elysia()
+ .post('/user', ({ body }) => body, {
+ body: t.Object({
+ name: t.String(),
+ age: t.Number({ minimum: 0 })
+ })
+ })
+
+ // Valid request
+ const validRes = await app.handle(
+ new Request('http://localhost/user', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ name: 'John',
+ age: 25
+ })
+ })
+ )
+ expect(validRes.status).toBe(200)
+
+ // Invalid request (negative age)
+ const invalidRes = await app.handle(
+ new Request('http://localhost/user', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ name: 'John',
+ age: -5
+ })
+ })
+ )
+ expect(invalidRes.status).toBe(400)
+})
+```
+
+## Testing WebSocket
+
+```typescript
+it('should handle websocket connection', (done) => {
+ const app = new Elysia()
+ .ws('/chat', {
+ message(ws, message) {
+ ws.send('Echo: ' + message)
+ }
+ })
+
+ const ws = new WebSocket('ws://localhost:3000/chat')
+
+ ws.onopen = () => {
+ ws.send('Hello')
+ }
+
+ ws.onmessage = (event) => {
+ expect(event.data).toBe('Echo: Hello')
+ ws.close()
+ done()
+ }
+})
+```
+
+## Complete Example
+
+```typescript
+// src/modules/auth/index.ts
+import { Elysia, t } from 'elysia'
+
+export const authModule = new Elysia({ prefix: '/auth' })
+ .post('/login', ({ body, cookie: { session } }) => {
+ if (body.username === 'admin' && body.password === 'password') {
+ session.value = 'valid-session'
+ return { success: true }
+ }
+ return { success: false }
+ }, {
+ body: t.Object({
+ username: t.String(),
+ password: t.String()
+ })
+ })
+ .get('/profile', ({ cookie: { session }, status }) => {
+ if (!session.value) {
+ return status(401)
+ }
+ return { username: 'admin' }
+ })
+
+// test/auth.test.ts
+import { describe, expect, it } from 'bun:test'
+import { treaty } from '@elysiajs/eden'
+import { authModule } from '../src/modules/auth'
+
+const api = treaty(authModule)
+
+describe('Auth Module', () => {
+ it('should login successfully', async () => {
+ const { data, error } = await api.auth.login.post({
+ username: 'admin',
+ password: 'password'
+ })
+
+ expect(error).toBeNull()
+ expect(data?.success).toBe(true)
+ })
+
+ it('should return 401 for unauthorized access', async () => {
+ const { error } = await api.auth.profile.get()
+
+ expect(error?.status).toBe(401)
+ })
+})
+```
diff --git a/.agents/skills/elysiajs/references/validation.md b/.agents/skills/elysiajs/references/validation.md
new file mode 100644
index 0000000..ba723e0
--- /dev/null
+++ b/.agents/skills/elysiajs/references/validation.md
@@ -0,0 +1,491 @@
+# Validation Schema - SKILLS.md
+
+## What It Is
+Runtime validation + type inference + OpenAPI schema from single source. TypeBox-based with Standard Schema support.
+
+## Basic Usage
+```typescript
+import { Elysia, t } from 'elysia'
+
+new Elysia()
+ .get('/id/:id', ({ params: { id } }) => id, {
+ params: t.Object({ id: t.Number({ minimum: 1 }) }),
+ response: {
+ 200: t.Number(),
+ 404: t.Literal('Not Found')
+ }
+ })
+```
+
+## Schema Types
+Third parameter of HTTP method:
+- **body** - HTTP message
+- **query** - URL query params
+- **params** - Path params
+- **headers** - Request headers
+- **cookie** - Request cookies
+- **response** - Response (per status)
+
+## Standard Schema Support
+Use Zod, Valibot, ArkType, Effect, Yup, Joi:
+```typescript
+import { z } from 'zod'
+import * as v from 'valibot'
+
+.get('/', ({ params, query }) => params.id, {
+ params: z.object({ id: z.coerce.number() }),
+ query: v.object({ name: v.literal('Lilith') })
+})
+```
+
+Mix validators in same handler.
+
+## Body
+```typescript
+body: t.Object({ name: t.String() })
+```
+
+GET/HEAD: body-parser disabled by default (RFC2616).
+
+### File Upload
+```typescript
+body: t.Object({
+ file: t.File({ format: 'image/*' }),
+ multipleFiles: t.Files()
+})
+// Auto-assumes multipart/form-data
+```
+
+### File (Standard Schema)
+```typescript
+import { fileType } from 'elysia'
+
+body: z.object({
+ file: z.file().refine((file) => fileType(file, 'image/jpeg'))
+})
+```
+
+Use `fileType` for security (validates magic number, not just MIME).
+
+## Query
+```typescript
+query: t.Object({ name: t.String() })
+// /?name=Elysia
+```
+
+Auto-coerces to specified type.
+
+### Arrays
+```typescript
+query: t.Object({ name: t.Array(t.String()) })
+```
+
+Formats supported:
+- **nuqs**: `?name=a,b,c` (comma delimiter)
+- **HTML form**: `?name=a&name=b&name=c` (multiple keys)
+
+## Params
+```typescript
+params: t.Object({ id: t.Number() })
+// /id/1
+```
+
+Auto-inferred as string if schema not provided.
+
+## Headers
+```typescript
+headers: t.Object({ authorization: t.String() })
+```
+
+`additionalProperties: true` by default. Always lowercase keys.
+
+## Cookie
+```typescript
+cookie: t.Cookie({
+ name: t.String()
+}, {
+ secure: true,
+ httpOnly: true
+})
+```
+
+Or use `t.Object`. `additionalProperties: true` by default.
+
+## Response
+```typescript
+response: t.Object({ name: t.String() })
+```
+
+### Per Status
+```typescript
+response: {
+ 200: t.Object({ name: t.String() }),
+ 400: t.Object({ error: t.String() })
+}
+```
+
+## Error Handling
+
+### Inline Error Property
+```typescript
+body: t.Object({
+ x: t.Number({ error: 'x must be number' })
+})
+```
+
+Or function:
+```typescript
+x: t.Number({
+ error({ errors, type, validation, value }) {
+ return 'Expected x to be number'
+ }
+})
+```
+
+### onError Hook
+```typescript
+.onError(({ code, error }) => {
+ if (code === 'VALIDATION')
+ return error.message // or error.all[0].message
+})
+```
+
+`error.all` - list all error causes. `error.all.find(x => x.path === '/name')` - find specific field.
+
+## Reference Models
+Name + reuse models:
+```typescript
+.model({
+ sign: t.Object({
+ username: t.String(),
+ password: t.String()
+ })
+})
+.post('/sign-in', ({ body }) => body, {
+ body: 'sign',
+ response: 'sign'
+})
+```
+
+Extract to plugin:
+```typescript
+// auth.model.ts
+export const authModel = new Elysia().model({ sign: t.Object({...}) })
+
+// main.ts
+new Elysia().use(authModel).post('/', ..., { body: 'sign' })
+```
+
+### Naming Convention
+Prevent duplicates with namespaces:
+```typescript
+.model({
+ 'auth.admin': t.Object({...}),
+ 'auth.user': t.Object({...})
+})
+```
+
+Or use `prefix` / `suffix` to rename models in current instance
+```typescript
+.model({ sign: t.Object({...}) })
+.prefix('model', 'auth')
+.post('/', () => '', {
+ body: 'auth.User'
+})
+```
+
+Models with `prefix` will be capitalized.
+
+## TypeScript Types
+```typescript
+const MyType = t.Object({ hello: t.Literal('Elysia') })
+type MyType = typeof MyType.static
+```
+
+Single schema → runtime validation + coercion + TypeScript type + OpenAPI.
+
+## Guard
+Apply schema to multiple handlers. Affects all handlers after definition.
+
+### Basic Usage
+```typescript
+import { Elysia, t } from 'elysia'
+
+new Elysia()
+ .get('/none', ({ query }) => 'hi')
+ .guard({
+ query: t.Object({
+ name: t.String()
+ })
+ })
+ .get('/query', ({ query }) => query)
+ .listen(3000)
+```
+
+Ensures `query.name` string required for all handlers after guard.
+
+### Behavior
+| Path | Response |
+|---------------|----------|
+| /none | hi |
+| /none?name=a | hi |
+| /query | error |
+| /query?name=a | a |
+
+### Precedence
+- Multiple global schemas: latest wins
+- Global vs local: local wins
+
+### Schema Types
+
+1. override (default)
+Latest schema overrides collided schema.
+```typescript
+.guard({ query: t.Object({ name: t.String() }) })
+.guard({ query: t.Object({ id: t.Number() }) })
+// Only id required, name overridden
+```
+
+2. standalone
+Both schemas run independently. Both validated.
+```typescript
+.guard({ query: t.Object({ name: t.String() }) }, { type: 'standalone' })
+.guard({ query: t.Object({ id: t.Number() }) }, { type: 'standalone' })
+// Both name AND id required
+```
+
+# Typebox Validation (Elysia.t)
+
+Elysia.t = TypeBox with server-side pre-configuration + HTTP-specific types
+
+**TypeBox API mirrors TypeScript syntax** but provides runtime validation
+
+## Basic Types
+
+| TypeBox | TypeScript | Example Value |
+|---------|------------|---------------|
+| `t.String()` | `string` | `"hello"` |
+| `t.Number()` | `number` | `42` |
+| `t.Boolean()` | `boolean` | `true` |
+| `t.Array(t.Number())` | `number[]` | `[1, 2, 3]` |
+| `t.Object({ x: t.Number() })` | `{ x: number }` | `{ x: 10 }` |
+| `t.Null()` | `null` | `null` |
+| `t.Literal(42)` | `42` | `42` |
+
+## Attributes (JSON Schema 7)
+
+```ts
+// Email format
+t.String({ format: 'email' })
+
+// Number constraints
+t.Number({ minimum: 10, maximum: 100 })
+
+// Array constraints
+t.Array(t.Number(), {
+ minItems: 1, // min items
+ maxItems: 5 // max items
+})
+
+// Object - allow extra properties
+t.Object(
+ { x: t.Number() },
+ { additionalProperties: true } // default: false
+)
+```
+
+## Common Patterns
+
+### Union (Multiple Types)
+```ts
+t.Union([t.String(), t.Number()])
+// type: string | number
+// values: "Hello" or 123
+```
+
+### Optional (Field Optional)
+```ts
+t.Object({
+ x: t.Number(),
+ y: t.Optional(t.Number()) // can be undefined
+})
+// type: { x: number, y?: number }
+// value: { x: 123 } or { x: 123, y: 456 }
+```
+
+### Partial (All Fields Optional)
+```ts
+t.Partial(t.Object({
+ x: t.Number(),
+ y: t.Number()
+}))
+// type: { x?: number, y?: number }
+// value: {} or { y: 123 } or { x: 1, y: 2 }
+```
+
+## Elysia-Specific Types
+
+### UnionEnum (One of Values)
+```ts
+t.UnionEnum(['rapi', 'anis', 1, true, false])
+```
+
+### File (Single File Upload)
+```ts
+t.File({
+ type: 'image', // or ['image', 'video']
+ minSize: '1k', // 1024 bytes
+ maxSize: '5m' // 5242880 bytes
+})
+```
+
+**File unit suffixes**:
+- `m` = MegaByte (1048576 bytes)
+- `k` = KiloByte (1024 bytes)
+
+### Files (Multiple Files)
+```ts
+t.Files() // extends File + array
+```
+
+### Cookie (Cookie Jar)
+```ts
+t.Cookie({
+ name: t.String()
+}, {
+ secrets: 'secret-key' // or ['key1', 'key2'] for rotation
+})
+```
+
+### Nullable (Allow null)
+```ts
+t.Nullable(t.String())
+// type: string | null
+```
+
+### MaybeEmpty (Allow null + undefined)
+```ts
+t.MaybeEmpty(t.String())
+// type: string | null | undefined
+```
+
+### Form (FormData Validation)
+```ts
+t.Form({
+ someValue: t.File()
+})
+// Syntax sugar for t.Object with FormData support
+```
+
+### UInt8Array (Buffer → Uint8Array)
+```ts
+t.UInt8Array()
+// For binary file uploads with arrayBuffer parser
+```
+
+### ArrayBuffer (Buffer → ArrayBuffer)
+```ts
+t.ArrayBuffer()
+// For binary file uploads with arrayBuffer parser
+```
+
+### ObjectString (String → Object)
+```ts
+t.ObjectString()
+// Accepts: '{"x":1}' → parses to { x: 1 }
+// Use in: query string, headers, FormData
+```
+
+### BooleanString (String → Boolean)
+```ts
+t.BooleanString()
+// Accepts: 'true'/'false' → parses to boolean
+// Use in: query string, headers, FormData
+```
+
+### Numeric (String/Number → Number)
+```ts
+t.Numeric()
+// Accepts: '123' or 123 → transforms to 123
+// Use in: path params, query string
+```
+
+## Elysia Behavior Differences from TypeBox
+
+### 1. Optional Behavior
+
+In Elysia, `t.Optional` makes **entire route parameter** optional (not object field):
+
+```ts
+.get('/optional', ({ query }) => query, {
+ query: t.Optional( // makes query itself optional
+ t.Object({ name: t.String() })
+ )
+})
+```
+
+**Different from TypeBox**: TypeBox uses Optional for object fields only
+
+### 2. Number → Numeric Auto-Conversion
+
+**Route schema only** (not nested objects):
+
+```ts
+.get('/:id', ({ id }) => id, {
+ params: t.Object({
+ id: t.Number() // ✅ Auto-converts to t.Numeric()
+ }),
+ body: t.Object({
+ id: t.Number() // ❌ NOT converted (stays t.Number())
+ })
+})
+
+// Outside route schema
+t.Number() // ❌ NOT converted
+```
+
+**Why**: HTTP headers/query/params always strings. Auto-conversion parses numeric strings.
+
+### 3. Boolean → BooleanString Auto-Conversion
+
+Same as Number → Numeric:
+
+```ts
+.get('/:active', ({ active }) => active, {
+ params: t.Object({
+ active: t.Boolean() // ✅ Auto-converts to t.BooleanString()
+ }),
+ body: t.Object({
+ active: t.Boolean() // ❌ NOT converted
+ })
+})
+```
+
+## Usage Pattern
+
+```ts
+import { Elysia, t } from 'elysia'
+
+new Elysia()
+ .post('/', ({ body }) => `Hello ${body}`, {
+ body: t.String() // validates body is string
+ })
+ .listen(3000)
+```
+
+**Validation flow**:
+1. Request arrives
+2. Schema validates against HTTP body/params/query/headers
+3. If valid → handler executes
+4. If invalid → Error Life Cycle
+
+## Notes
+
+[Inference] Based on docs:
+- TypeBox mirrors TypeScript but adds runtime validation
+- Elysia.t extends TypeBox with HTTP-specific types
+- Auto-conversion (Number→Numeric, Boolean→BooleanString) only for route schemas
+- Use `t.Optional` for optional route params (different from TypeBox behavior)
+- File validation supports unit suffixes ('1k', '5m')
+- ObjectString/BooleanString for parsing strings in query/headers
+- Cookie supports key rotation with array of secrets
diff --git a/.agents/skills/elysiajs/references/websocket.md b/.agents/skills/elysiajs/references/websocket.md
new file mode 100644
index 0000000..b2c86a8
--- /dev/null
+++ b/.agents/skills/elysiajs/references/websocket.md
@@ -0,0 +1,250 @@
+# WebSocket
+
+## Basic WebSocket
+
+```typescript
+import { Elysia } from 'elysia'
+
+new Elysia()
+ .ws('/chat', {
+ message(ws, message) {
+ ws.send(message) // Echo back
+ }
+ })
+ .listen(3000)
+```
+
+## With Validation
+
+```typescript
+import { Elysia, t } from 'elysia'
+
+.ws('/chat', {
+ body: t.Object({
+ message: t.String(),
+ username: t.String()
+ }),
+ response: t.Object({
+ message: t.String(),
+ timestamp: t.Number()
+ }),
+ message(ws, body) {
+ ws.send({
+ message: body.message,
+ timestamp: Date.now()
+ })
+ }
+})
+```
+
+## Lifecycle Events
+
+```typescript
+.ws('/chat', {
+ open(ws) {
+ console.log('Client connected')
+ },
+ message(ws, message) {
+ console.log('Received:', message)
+ ws.send('Echo: ' + message)
+ },
+ close(ws) {
+ console.log('Client disconnected')
+ },
+ error(ws, error) {
+ console.error('Error:', error)
+ }
+})
+```
+
+## Broadcasting
+
+```typescript
+const connections = new Set()
+
+.ws('/chat', {
+ open(ws) {
+ connections.add(ws)
+ },
+ message(ws, message) {
+ // Broadcast to all connected clients
+ for (const client of connections) {
+ client.send(message)
+ }
+ },
+ close(ws) {
+ connections.delete(ws)
+ }
+})
+```
+
+## With Authentication
+
+```typescript
+.ws('/chat', {
+ beforeHandle({ headers, status }) {
+ const token = headers.authorization?.replace('Bearer ', '')
+ if (!verifyToken(token)) {
+ return status(401)
+ }
+ },
+ message(ws, message) {
+ ws.send(message)
+ }
+})
+```
+
+## Room-Based Chat
+
+```typescript
+const rooms = new Map>()
+
+.ws('/chat/:room', {
+ open(ws) {
+ const room = ws.data.params.room
+ if (!rooms.has(room)) {
+ rooms.set(room, new Set())
+ }
+ rooms.get(room)!.add(ws)
+ },
+ message(ws, message) {
+ const room = ws.data.params.room
+ const clients = rooms.get(room)
+
+ if (clients) {
+ for (const client of clients) {
+ client.send(message)
+ }
+ }
+ },
+ close(ws) {
+ const room = ws.data.params.room
+ const clients = rooms.get(room)
+
+ if (clients) {
+ clients.delete(ws)
+ if (clients.size === 0) {
+ rooms.delete(room)
+ }
+ }
+ }
+})
+```
+
+## With State/Context
+
+```typescript
+.ws('/chat', {
+ open(ws) {
+ ws.data.userId = generateUserId()
+ ws.data.joinedAt = Date.now()
+ },
+ message(ws, message) {
+ const response = {
+ userId: ws.data.userId,
+ message,
+ timestamp: Date.now()
+ }
+ ws.send(response)
+ }
+})
+```
+
+## Client Usage (Browser)
+
+```typescript
+const ws = new WebSocket('ws://localhost:3000/chat')
+
+ws.onopen = () => {
+ console.log('Connected')
+ ws.send('Hello Server!')
+}
+
+ws.onmessage = (event) => {
+ console.log('Received:', event.data)
+}
+
+ws.onerror = (error) => {
+ console.error('Error:', error)
+}
+
+ws.onclose = () => {
+ console.log('Disconnected')
+}
+```
+
+## Eden Treaty WebSocket
+
+```typescript
+// Server
+export const app = new Elysia()
+ .ws('/chat', {
+ message(ws, message) {
+ ws.send(message)
+ }
+ })
+
+export type App = typeof app
+
+// Client
+import { treaty } from '@elysiajs/eden'
+import type { App } from './server'
+
+const api = treaty('localhost:3000')
+const chat = api.chat.subscribe()
+
+chat.subscribe((message) => {
+ console.log('Received:', message)
+})
+
+chat.send('Hello!')
+```
+
+## Headers in WebSocket
+
+```typescript
+.ws('/chat', {
+ header: t.Object({
+ authorization: t.String()
+ }),
+ beforeHandle({ headers, status }) {
+ const token = headers.authorization?.replace('Bearer ', '')
+ if (!token) return status(401)
+ },
+ message(ws, message) {
+ ws.send(message)
+ }
+})
+```
+
+## Query Parameters
+
+```typescript
+.ws('/chat', {
+ query: t.Object({
+ username: t.String()
+ }),
+ message(ws, message) {
+ const username = ws.data.query.username
+ ws.send(`${username}: ${message}`)
+ }
+})
+
+// Client
+const ws = new WebSocket('ws://localhost:3000/chat?username=john')
+```
+
+## Compression
+
+```typescript
+new Elysia({
+ websocket: {
+ perMessageDeflate: true
+ }
+})
+ .ws('/chat', {
+ message(ws, message) {
+ ws.send(message)
+ }
+ })
+```
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 22663f5..1f3a4cd 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -15,17 +15,17 @@ permissions:
contents: read
jobs:
- lint-format:
- name: Lint & Format (Biome)
+ ci:
+ name: CI
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@v7
+ uses: actions/checkout@v4
- name: Setup Bun
- uses: oven-sh/setup-bun@v2
+ uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
- bun-version: 1.3.2
+ bun-version: latest
- name: Cache Bun
uses: actions/cache@v6
@@ -40,61 +40,11 @@ jobs:
- name: Install dependencies
run: bun install --frozen-lockfile
- - name: Run Biome (Lint + Format)
- run: bun run check:biome
+ - name: Build
+ run: bun run build
- typecheck:
- name: TypeScript Type Check
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v7
-
- - name: Setup Bun
- uses: oven-sh/setup-bun@v2
- with:
- bun-version: 1.3.2
-
- - name: Cache Bun
- uses: actions/cache@v6
- with:
- path: |
- ~/.bun/install/cache
- **/node_modules
- key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }}
- restore-keys: |
- ${{ runner.os }}-bun-
-
- - name: Install dependencies
- run: bun install --frozen-lockfile
-
- - name: Run TypeScript Type Check
- run: bun run check:type
-
- test:
- name: Test (xss-scan + bun test)
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v7
-
- - name: Setup Bun
- uses: oven-sh/setup-bun@v2
- with:
- bun-version: 1.3.2
-
- - name: Cache Bun
- uses: actions/cache@v6
- with:
- path: |
- ~/.bun/install/cache
- **/node_modules
- key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }}
- restore-keys: |
- ${{ runner.os }}-bun-
-
- - name: Install dependencies
- run: bun install --frozen-lockfile
+ - name: Check (typecheck + biome)
+ run: bun run check
- - name: Run Tests
- run: bun test ./pkgs/rssbook/src/tests
+ - name: Test
+ run: bun run test
diff --git a/.github/workflows/feed-check.yml b/.github/workflows/feed-check.yml
new file mode 100644
index 0000000..52c5d63
--- /dev/null
+++ b/.github/workflows/feed-check.yml
@@ -0,0 +1,80 @@
+name: Feed Source Check
+
+on:
+ pull_request:
+ paths:
+ - "pkgs/rssbook/src/routers/feeds/**"
+
+permissions:
+ contents: read
+
+jobs:
+ check-feed-tests:
+ name: Verify feed sources have tests
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
+ with:
+ bun-version: latest
+
+ - name: Install dependencies
+ run: bun install --frozen-lockfile
+
+ - name: Check source files have corresponding tests
+ id: check
+ run: |
+ changed_sources=$(git diff --name-only --diff-filter=ACMR ${{ github.event.pull_request.base.sha }} HEAD -- \
+ 'pkgs/rssbook/src/routers/feeds/' \
+ | grep -E '^pkgs/rssbook/src/routers/feeds/[^/]+/[^/]+/index\.ts$' \
+ | grep -v '/_example/' \
+ || true)
+
+ if [ -z "$changed_sources" ]; then
+ echo "No feed source files changed."
+ echo "has_changes=false" >> "$GITHUB_OUTPUT"
+ exit 0
+ fi
+
+ echo "has_changes=true" >> "$GITHUB_OUTPUT"
+ echo "Changed feed sources:"
+ echo "$changed_sources"
+
+ missing_tests=""
+ test_files=""
+
+ for src in $changed_sources; do
+ test_file="${src%index.ts}index.test.ts"
+ if [ ! -f "$test_file" ]; then
+ missing_tests="$missing_tests\n Missing: $test_file (for $src)"
+ else
+ test_files="$test_files $test_file"
+ fi
+ done
+
+ if [ -n "$missing_tests" ]; then
+ echo "::error::The following feed sources are missing test files:"
+ echo -e "$missing_tests"
+ exit 1
+ fi
+
+ echo "test_files=$test_files" >> "$GITHUB_OUTPUT"
+ echo "All feed sources have corresponding test files."
+
+ - name: Run feed tests
+ if: steps.check.outputs.has_changes == 'true'
+ run: |
+ test_files="${{ steps.check.outputs.test_files }}"
+ echo "Running tests for:$test_files"
+ bun test $test_files
+
+ - name: Typecheck
+ if: steps.check.outputs.has_changes == 'true'
+ run: bun run check:type
+
+ - name: Biome check
+ if: steps.check.outputs.has_changes == 'true'
+ run: bun run check:biome
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 45db8cf..72370b7 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -19,7 +19,7 @@ jobs:
uses: actions/checkout@v7
- name: Setup Bun
- uses: oven-sh/setup-bun@v2
+ uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: 1.3.2
@@ -56,6 +56,6 @@ jobs:
- name: Publish to npm
working-directory: pkgs/rssbook
- env:
- FORCE_JAVASCRIPT_TYPES: "true"
run: npm publish --provenance --access public
+ env:
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
diff --git a/AGENTS.md b/AGENTS.md
index 29e7c5b..0494cb2 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -1,307 +1,70 @@
-# CLAUDE.md
+# AGENTS.md
-## Project Overview
+## Project Snapshot
-RSSBook 是一个 RSS Feed 生成器、工具集和博客平台。它支持多种运行时环境(Bun、Node.js、Deno、Cloudflare Workers、Netlify、Vercel),使用 ElysiaJS 框架构建。
+RSSBook is a Bun-first RSS feed generator, feed utility toolkit, and lightweight blog/book platform.
-## Common Development Commands
+The main package lives in `pkgs/rssbook` and is built with ElysiaJS.
+Runtime adapters live under `platform/` for Cloudflare Workers, Deno, Netlify, Node.js, and Vercel.
-### 开发和构建
+Use Bun for JavaScript and TypeScript work unless there is a strong reason not to.
-```bash
-# 开发模式(Bun)
-bun dev
-
-# 开发模式(Node.js)
-npx tsx src/node.ts
+## Agent Rules
-# 开发模式(Cloudflare Workers)
-bun run dev:cf
+- Use Context7 MCP for current documentation whenever working with a library, framework, SDK, API, CLI tool, or cloud service.
+- Add JSDoc only when it clarifies non-obvious public APIs, complex behavior, or important constraints.
+- Prefer existing local patterns over new abstractions.
+- Avoid broad refactors while fixing or adding a feed.
+- Do not use `any`, `as`, or `@ts-ignore` unless the alternative is materially worse. Prefer `satisfies` for feed data.
-# 生产构建(Bun)
-bun run build
-
-# 生产构建(Node.js)
-bun run build:node
-```
+## Common Commands
-### 代码检查和测试
+Run commands from the repository root unless noted.
```bash
-# 运行所有检查(TypeScript + Biome 格式化)
-bun run check
-
-# 仅 TypeScript 检查
-bun run check:ts
-
-# 仅 Biome 格式化检查
-bun run check:biome
+# Development
+bun run dev
-# 运行单元测试
-bun test ./src/tests
+# Production build for pkgs/rssbook
+bun run build
-# 运行所有路由测试
-bun test ./src/routers
+# Typecheck + Biome check
+bun run check
-# 运行测试并生成覆盖率报告
-bun test --coverage ./src/tests
+# Apply Biome fixes
+bun run fix
-# 运行 XSS 安全扫描和测试
+# Main test suite: XSS scan + non-feed tests
bun run test
-```
-### 路由和源管理
+# Coverage for non-feed tests
+bun run test:cov
-```bash
-# 创建新的源(Feed)
-bun run source:new
-
-# 测试单个源
-bun run source:test
-
-# 测试所有路由
+# Test all feed routes
bun run source:test:all
-```
-
-## Architecture Overview
-
-### 核心架构层次
-
-RSSBook 采用分层架构,从上到下为:
-
-1. **入口层** (`src/index.ts`, `src/node.ts`, `src/cloudflare.ts` 等)
- - 针对不同运行时的入口文件
- - 调用 `RSSBookApp()` 初始化应用
-
-2. **应用层** (`src/RSSBookApp.ts`)
- - 创建 Elysia 应用实例
- - 组织插件和路由
- - 配置顺序:错误处理 → 日志 → OpenAPI → 初始化 → 书籍 → 路由 → 静态资源
-
-3. **插件层** (`src/plugins/`)
- - `initPlugin`: 初始化 RSSBook 实例,注入到 Elysia 上下文
- - `errorHandlerPlugin`: 全局错误处理
- - `loggerPlugin`: 请求日志
- - `openAPIPlugin`: 生成 OpenAPI 文档
- - `renderPlugin`: 注入渲染函数
- - `injectPlugin`: 注入工具函数(cache、date、ofetch 等)
- - `assetsPlugin`: 静态资源服务
-
-4. **路由层** (`src/routers/`)
- - `feeds/`: 按分类组织的 Feed 路由(programming、news、blog 等)
- - `utils/`: 工具路由(fetch、filter、sort、union、intersection 等)
-
-5. **数据处理层** (`src/utils/`)
- - Feed 解析:`parse()` - 解析 RSS/Atom/JSON Feed
- - Feed 操作:`filter()`、`sort()`、`union()`、`intersection()`
- - 缓存:`Cache` 类(支持 LRU、内存、KV 等)
- - 工具函数:日期格式化、HTML 处理、语言检测等
-
-6. **主题层** (`src/books/`)
- - 渲染 Feed 为 HTML 页面
- - 支持自定义主题
- - 分页、搜索、分类过滤
-
-### 关键类和概念
-
-#### Source 类 (`src/utils/source.ts`)
-
-定义单个数据源(如 GitHub、Twitter 等)。每个 Source 包含:
-
-- **配置**:slug、title、description、domain、config 参数
-- **Feed 定义**:通过 `.feed(config, handler)` 添加具体的 Feed 路由
-- **路由生成**:自动生成 `/feeds/{category}/{slug}/{feedPath}` 格式的路由
-
-```typescript
-export default new Source({
- slug: "github",
- title: "Github",
- description: "Github is a code hosting platform...",
- domain: "github.com",
- config: {
- GITHUB_TOKEN: { description: "...", required: true }
- }
-}).feed(
- { title: "User Events", description: "...", language: "en" },
- (app) => app.get("/events/:username", handler)
-);
-```
-
-#### Category 类 (`src/utils/category.ts`)
-
-将多个 Source 组织成一个分类。每个分类对应 `src/routers/feeds/{categoryName}/index.ts`。
-
-```typescript
-export default new Category("programming", "Resources about coding...").use({
- github,
- gitlab,
- // 其他源...
-});
-```
-
-#### Data 类型 (`src/types/data.ts`)
-
-所有 Feed 处理的标准数据格式:
-```typescript
-interface Data {
- title: string;
- link: string;
- description?: string;
- item?: DataItem[];
- // 其他元数据...
-}
-
-interface DataItem {
- title: string;
- link: string;
- description?: string;
- author?: Author[];
- category?: Category[];
- date?: string;
- id?: string;
- // ...
-}
-```
-
-### 请求流程
-
-1. 用户请求 `/feeds/{category}/{slug}/{feedPath}?type=rss`
-2. 路由匹配到对应的 Source 和 Feed handler
-3. Handler 通过 `ofetch` 获取数据,使用 `cache` 缓存
-4. 返回 `Data` 对象
-5. `renderPlugin` 将 Data 转换为 RSS/Atom/JSON Feed 格式
-6. 如果 `type=html`,使用主题渲染为 HTML 页面
-
-## Creating New Feeds
-
-### 基本步骤
-
-1. **创建源文件** `src/routers/feeds/{category}/{slug}/index.ts`
-
-```typescript
-import { Source } from "@/utils";
-
-export default new Source({
- slug: "my-source",
- title: "My Source",
- description: "Description of the source",
- domain: "example.com",
- config: {
- // 可选的配置参数
- }
-}).feed(
- {
- title: "Feed Title",
- description: "Feed description",
- language: "en",
- maintainer: { name: "Your Name" }
- },
- (app) => app.get("/path/:param", async (context) => {
- // 实现 Feed 逻辑
- return data satisfies Data;
- })
-);
-```
-
-2. **在分类中注册** `src/routers/feeds/{category}/index.ts`
-
-```typescript
-import mySource from "./my-source";
-
-export default new Category("category-name", "Description").use({
- mySource,
- // 其他源...
-});
-```
-
-### Handler 上下文对象
-
-Handler 接收的上下文包含:
-
-- **props**:
- - `meta`: 源和 Feed 的元数据(domain、config、title 等)
- - `params`: 路由参数
- - `query`: 查询参数
-
-- **函数**:
- - `cache`: 缓存对象,使用 `cache.tryGet(key, asyncFn)` 避免重复请求
- - `date`: 日期格式化函数
- - `ofetch`: 增强的 fetch 函数
- - `parse`: 解析 Feed 内容
- - `render`: 渲染 Data 为 Feed 格式
-
-### 数据处理工具
-
-- `filter(data, options)`: 按关键词、分类、日期范围过滤
-- `sort(data, options)`: 按日期、标题排序
-- `union(data1, data2, ...)`: 合并多个 Feed
-- `intersection(data1, data2)`: 取交集
-- `parse(xmlString)`: 解析 RSS/Atom Feed
-
-## Code Style and Standards
-
-### 类型安全
-
-- 尽可能避免使用 `as`、`any`、`@ts-ignore`
-- 使用 `satisfies` 关键字验证类型正确性
-- 为 Feed handler 返回值使用 `satisfies Data`
-
-### 代码格式
-
-- 使用 `bun run check` 进行格式化检查(Biome)
-- 使用 JSDOC 注释提高代码可读性
-- 所有 Feed 路由必须是 GET 请求
-
-### 命名规范
-
-- Source slug:小写字母和短横线(如 `my-source`)
-- 配置参数:大写字母和下划线(如 `GITHUB_TOKEN`)
-- 分类名称:小写字母(如 `programming`)
-
-## Multi-Runtime Support
-
-项目支持多个运行时环境,每个有独立的入口文件:
-
-- `src/index.ts`: Bun(推荐开发和部署)
-- `src/node.ts`: Node.js
-- `src/deno.ts`: Deno
-- `src/cloudflare.ts`: Cloudflare Workers
-- `src/netlify.ts`: Netlify
-- `src/vercel.json`: Vercel 配置
-
-所有入口文件都基于 `src/RSSBookApp.ts` 修改,可根据运行时特性配置缓存、存储等。
-
-## Caching Strategy
-
-项目使用 `Cache` 类管理缓存,支持多种后端:
-
-- `Cache.LRU_Cache`: 内存 LRU 缓存(默认)
-- Cloudflare Workers KV
-- 其他 unstorage 支持的存储
-
-在 handler 中使用:
+# Create a new feed source
+bun run source:new
-```typescript
-const data = await cache.tryGet(cacheKey, async () => {
- // 异步获取数据
- return result;
-});
+# Test a selected feed source
+bun run source:test
```
-## Testing
+## Repository Layout
-- 单元测试位于 `src/tests/` 和 `src/routers/` 目录
-- 使用 Bun 的内置测试框架
-- 运行 `bun test` 执行所有测试
-- 运行 `bun run source:test` 测试特定源
+- `pkgs/rssbook/src/RSSBookApp.ts` creates the main Elysia app and wires plugins/routes.
+- `pkgs/rssbook/src/index.ts` is the Bun entry point.
+- `pkgs/rssbook/src/plugins/` contains error handling, logging, OpenAPI, rendering, dependency injection, initialization, and assets plugins.
+- `pkgs/rssbook/src/routers/feeds/` contains source feeds grouped by category.
+- `pkgs/rssbook/src/routers/utils/` contains utility routes such as fetch, filter, sort, union, intersection, and transform.
+- `pkgs/rssbook/src/utils/` contains feed parsing/rendering utilities, cache, date helpers, HTML helpers, `Source`, and `Category`.
+- `pkgs/rssbook/src/types/` contains shared data, source, route, meta, and theme types.
+- Tests live next to their related source files, for example `src/utils/*.test.ts`, `src/utils/feeds/*.test.ts`, `src/types/*.test.ts`, and `src/routers/**/*.test.ts`.
+- Runtime packages live in `platform/*`.
-## Important Files
+## Code Style
-- `src/RSSBookApp.ts`: 应用主入口,定义插件顺序
-- `src/routers/index.ts`: 路由组织
-- `src/plugins/init.ts`: RSSBook 实例初始化
-- `src/types/data.ts`: Feed 数据类型定义
-- `src/utils/source.ts`: Source 类定义
-- `src/utils/category.ts`: Category 类定义
-- `src/books/index.ts`: 个人主页/书籍功能
+- Follow Biome formatting.
+- Prefer descriptive names over comments. Add short comments only for non-obvious logic.
+- Do not introduce new dependencies without checking existing utilities first.
+- Keep public API and runtime compatibility in mind; `pkgs/rssbook` is consumed by runtime packages under `platform/`.
diff --git a/HOSTS b/HOSTS
index 1806bb4..6d9fb17 100644
--- a/HOSTS
+++ b/HOSTS
@@ -1,2 +1,2 @@
url,description,online
-rssbook.htu.me,Official RSSBook Website,true
\ No newline at end of file
+https://rssbook.htu.me,Official RSSBook Website,true
diff --git a/README.EN.md b/README.EN.md
index b59df09..cedecf0 100644
--- a/README.EN.md
+++ b/README.EN.md
@@ -183,7 +183,7 @@ The entry file for Deno is in the `platform-deno` package: `platform/deno/index.
Deno is another runtime following Node.js.
-[](https://console.deno.com/new?clone=https://github.com/HackHTU/RSSBook)
+[](https://console.deno.com/new?clone=https://github.com/HackHTU/RSSBook&path=platform/deno)
##### CloudFlare Workers
@@ -193,7 +193,7 @@ The entry file for CloudFlare Workers is in the `platform-cloudflare` package: `
For production, you can directly click the button below to deploy to CloudFlare Workers, but this method does not support **custom configuration**.
-[](https://deploy.workers.cloudflare.com/?url=https://github.com/HackHTU/RSSBook/)
+[](https://deploy.workers.cloudflare.com/?url=https://github.com/HackHTU/RSSBook/tree/main/platform/cloudflare)
A better approach is to fork this repository, modify the configuration in [Github CodeSpace](https://github.com/codespaces/new/) or locally, then configure your own GitHub repository in Cloudflare Workers settings for deployment (or you can use GitHub Workflow).
@@ -203,7 +203,7 @@ Vercel is a popular serverless computing platform that also provides a generous
The entry file for Vercel is in the `platform-vercel` package: `platform/vercel/index.ts`.
-[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FHackHTU%2FRSSBook)
+[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FHackHTU%2FRSSBook&root-directory=platform%2Fvercel)
##### Netlify
@@ -211,7 +211,7 @@ Netlify is a popular serverless computing platform that also provides a generous
The entry file for Netlify is in the `platform-netlify` package: `platform/netlify/index.ts`.
-[](https://app.netlify.com/start/deploy?repository=https://github.com/HackHTU/RSSBook)
+[](https://app.netlify.com/start/deploy?repository=https://github.com/HackHTU/RSSBook&base=platform/netlify)
### Initial Configuration
diff --git a/README.md b/README.md
index f953084..5fc4325 100644
--- a/README.md
+++ b/README.md
@@ -183,7 +183,7 @@ Deno 的入口文件在 `platform-deno` 包中:`platform/deno/index.ts`。
Deno 是一个继 Node.js 之后的另一个运行时环境。
-[](https://console.deno.com/new?clone=https://github.com/HackHTU/RSSBook)
+[](https://console.deno.com/new?clone=https://github.com/HackHTU/RSSBook&path=platform/deno)
##### CloudFlare Workers
@@ -193,7 +193,7 @@ CloudFlare Workers 的入口文件在 `platform-cloudflare` 包中:`platform/c
在生产时,你可以直接点击下面的按钮来部署到 CloudFlare Workers,但是这种方式不支持**自定义配置**。
-[](https://deploy.workers.cloudflare.com/?url=https://github.com/HackHTU/RSSBook/)
+[](https://deploy.workers.cloudflare.com/?url=https://github.com/HackHTU/RSSBook/tree/main/platform/cloudflare)
更好的方法是自己 Fork 本仓库,在 [Github CodeSpace](https://github.com/codespaces/new/) 或本地修改配置后,然后使用在 Cloudflare Workers 的设置中绑定你自己的 GitHub 仓库进行部署(或者你可以使用 GitHub Workflow)。
@@ -203,7 +203,7 @@ Vercel 是一个非常流行的 Serverless 计算平台,也提供了慷慨的
Vercel 的入口文件在 `platform-vercel` 包中:`platform/vercel/index.ts`。
-[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FHackHTU%2FRSSBook)
+[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FHackHTU%2FRSSBook&root-directory=platform%2Fvercel)
##### Netlify
@@ -211,7 +211,7 @@ Netlify 是一个非常流行的 Serverless 计算平台,也提供了慷慨的
Netlify 的入口文件在 `platform-netlify` 包中:`platform/netlify/index.ts`。
-[](https://app.netlify.com/start/deploy?repository=https://github.com/HackHTU/RSSBook)
+[](https://app.netlify.com/start/deploy?repository=https://github.com/HackHTU/RSSBook&base=platform/netlify)
### 初始配置
diff --git a/bun.lock b/bun.lock
index 589b84a..1bd6b71 100644
--- a/bun.lock
+++ b/bun.lock
@@ -3,12 +3,13 @@
"configVersion": 1,
"workspaces": {
"": {
- "name": "rssbook",
+ "name": "rssbook-monorepo",
"devDependencies": {
"@biomejs/biome": "catalog:",
"@kitajs/ts-html-plugin": "catalog:",
"@types/bun": "catalog:",
"@types/sanitize-html": "catalog:",
+ "typescript": "^6.0.3",
},
},
"pkgs/rssbook": {
@@ -25,6 +26,7 @@
"@rowanmanning/feed-parser": "catalog:",
"cheerio": "catalog:",
"dayjs": "catalog:",
+ "domhandler": "catalog:",
"elysia": "catalog:",
"feed": "catalog:",
"ofetch": "catalog:",
@@ -32,6 +34,9 @@
"unstorage": "catalog:",
"uuid": "catalog:",
},
+ "devDependencies": {
+ "tsup": "catalog:",
+ },
},
"pkgs/utils-feed-helper": {
"name": "utils-feed-helper",
@@ -51,15 +56,14 @@
"dependencies": {
"elysia": "catalog:",
"rssbook": "workspace:*",
- },
- "devDependencies": {
- "wrangler": "catalog:",
+ "unstorage": "catalog:",
},
},
"platform/deno": {
"name": "@rssbook/platform-deno",
"dependencies": {
"rssbook": "workspace:*",
+ "unstorage": "catalog:",
},
},
"platform/netlify": {
@@ -67,9 +71,6 @@
"dependencies": {
"rssbook": "workspace:*",
},
- "devDependencies": {
- "netlify-cli": "catalog:",
- },
},
"platform/node": {
"name": "@rssbook/platform-node",
@@ -84,11 +85,9 @@
"platform/vercel": {
"name": "@rssbook/platform-vercel",
"dependencies": {
+ "elysia": "catalog:",
"rssbook": "workspace:*",
},
- "devDependencies": {
- "vercel": "catalog:",
- },
},
},
"catalog": {
@@ -107,28 +106,17 @@
"bun-types": "latest",
"cheerio": "^1.2.0",
"dayjs": "^1.11.21",
+ "domhandler": "^5.0.3",
"elysia": "^1.4.29",
"feed": "^5.2.1",
- "netlify-cli": "latest",
"ofetch": "^1.5.1",
"sanitize-html": "^2.17.5",
+ "tsup": "^8.5.1",
"tsx": "latest",
"unstorage": "1.17.5",
"uuid": "^13.0.2",
- "vercel": "latest",
- "wrangler": "latest",
},
"packages": {
- "@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="],
-
- "@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="],
-
- "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="],
-
- "@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="],
-
- "@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="],
-
"@biomejs/biome": ["@biomejs/biome@2.5.2", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.5.2", "@biomejs/cli-darwin-x64": "2.5.2", "@biomejs/cli-linux-arm64": "2.5.2", "@biomejs/cli-linux-arm64-musl": "2.5.2", "@biomejs/cli-linux-x64": "2.5.2", "@biomejs/cli-linux-x64-musl": "2.5.2", "@biomejs/cli-win32-arm64": "2.5.2", "@biomejs/cli-win32-x64": "2.5.2" }, "bin": { "biome": "bin/biome" } }, "sha512-VQ3RCqr7JmDIX+w6stWYl+g/3bYofN3q2wDBHUKKc/c7i5QWrFKFBZYCYPWTE6agsUPMIZZe6/CMmVUfUAhkKA=="],
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.5.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-e7P3P7EkwFc/KiX2AHw4YDLIBOMfG9CPCAwy52k5Bp0dfhkozx9hf6wCmIr2QeXy2XeccJ3V/Sg+hDmzYEqxSg=="],
@@ -149,54 +137,6 @@
"@borewit/text-codec": ["@borewit/text-codec@0.2.2", "", {}, "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ=="],
- "@bugsnag/browser": ["@bugsnag/browser@8.9.0", "", { "dependencies": { "@bugsnag/core": "^8.9.0" } }, "sha512-lsreEdlIQxEzJxFzp1T2yKH4Jzzm5YiXez0UK734XXfBRx8ZR2SDcNljo2EqctrRIqVrCM6wp6RvEo9hzlpeBg=="],
-
- "@bugsnag/core": ["@bugsnag/core@8.9.0", "", { "dependencies": { "@bugsnag/cuid": "^3.2.1", "@bugsnag/safe-json-stringify": "^6.0.0", "error-stack-parser": "^2.0.3", "iserror": "^0.0.2", "stack-generator": "^2.0.3" } }, "sha512-SrXO0isIZfrCem06/WDvC5MSd1Tj6Yjm4Wqdlv6jyjTvsow+cppvt7rS9Bps3qOeXfYCqWHN4ev02OdwnfZ1PQ=="],
-
- "@bugsnag/cuid": ["@bugsnag/cuid@3.2.2", "", {}, "sha512-7onuYLTMqMmHE9BBPG0YER4nFsU1rB+me1/YIeMusqcLbVbKKuG9u9+BDVDpje5e0llkkrVNOKYwmzM9DRIo7A=="],
-
- "@bugsnag/js": ["@bugsnag/js@8.9.0", "", { "dependencies": { "@bugsnag/browser": "^8.9.0", "@bugsnag/node": "^8.9.0" } }, "sha512-ChjKFVBF2CgepDXfMBkbEdcFUniJNN8py5V+tinaB8+vpJbr/fD1+VLFhkruSEn3jOafXrc1XJe9WHtsu9ht0g=="],
-
- "@bugsnag/node": ["@bugsnag/node@8.9.0", "", { "dependencies": { "@bugsnag/core": "^8.9.0", "byline": "^5.0.0", "error-stack-parser": "^2.0.3", "iserror": "^0.0.2", "pump": "^3.0.0", "stack-generator": "^2.0.3" } }, "sha512-WpogbMCY8/Rmg+1nsetp3GcPnXj5rpK1YxSmgEfiBM8B2nr/riYqrzuXIkrb1FZN1ti+Hfn1IecRAyGx3rUBBA=="],
-
- "@bugsnag/safe-json-stringify": ["@bugsnag/safe-json-stringify@6.1.0", "", {}, "sha512-ImA35rnM7bGr+J30R979FQ95BhRB4UO1KfJA0J2sVqc8nwnrS9hhE5mkTmQWMs8Vh1Da+hkLKs5jJB4JjNZp4A=="],
-
- "@bytecodealliance/preview2-shim": ["@bytecodealliance/preview2-shim@0.17.6", "", {}, "sha512-n3cM88gTen5980UOBAD6xDcNNL3ocTK8keab21bpx1ONdA+ARj7uD1qoFxOWCyKlkpSi195FH+GeAut7Oc6zZw=="],
-
- "@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.5.0", "", {}, "sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg=="],
-
- "@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.16.1", "", { "peerDependencies": { "unenv": "2.0.0-rc.24", "workerd": ">1.20260305.0 <2.0.0-0" }, "optionalPeers": ["workerd"] }, "sha512-ECxObrMfyTl5bhQf/lZCXwo5G6xX9IAUo+nDMKK4SZ8m4Jvvxp52vilxyySSWh2YTZz8+HQ07qGH/2rEom1vDw=="],
-
- "@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20260701.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-Zd9Y1bah6DwwBN2RW8vJohffQrIUazb8UXnqSNecOxM+jJLhUuvv5IOG8dbHcV83TyZAubea6gsQXo2yH1lDdw=="],
-
- "@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20260701.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-yBLsjS1qCWqFyCY37qRUrYfzHHvMGvjh8zRKJ6MvUivYDhkZTzqduppK38FoqYvayLJ5KbcxH7zo5rkxGqbsaA=="],
-
- "@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20260701.1", "", { "os": "linux", "cpu": "x64" }, "sha512-vMfqSIMfoo4xmZXEuUVqLpSFS921YKjiR9q7kDXPi6Vld1PK74UHg9LZuBavT2KSyemHUCTpj9y/4JSYOEyQbQ=="],
-
- "@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20260701.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-HRfwbKU2pK44V2NhoM0+iH0JJSj7nQ9Wv13ifIiGYCmTtDL8/zKtEhX7kQ3D4Vy/Cpjhttl0FkfqXj1aqLDPPg=="],
-
- "@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20260701.1", "", { "os": "win32", "cpu": "x64" }, "sha512-ngxCiIN9s/fM2o1IBMD0o1/mcXrv2NJVdyznh51UH8sQuvrTrXvV2nM0Uj/qU2wMwF6prgNBcdcd7AZeZGiBQA=="],
-
- "@colors/colors": ["@colors/colors@1.6.0", "", {}, "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA=="],
-
- "@cspotcode/source-map-support": ["@cspotcode/source-map-support@0.8.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.9" } }, "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw=="],
-
- "@dabh/diagnostics": ["@dabh/diagnostics@2.0.8", "", { "dependencies": { "@so-ric/colorspace": "^1.1.6", "enabled": "2.0.x", "kuler": "^2.0.0" } }, "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q=="],
-
- "@dependents/detective-less": ["@dependents/detective-less@5.0.3", "", { "dependencies": { "gonzales-pe": "^4.3.0", "node-source-walk": "^7.0.1" } }, "sha512-v6oD9Ukp+N7V4n6p5I/+mM5fIohSfkrDSGlFm5w/pYmchvbk+sMIHsLxrFJ5Lnujewj1BzWL0K84d88lwZAMQA=="],
-
- "@edge-runtime/format": ["@edge-runtime/format@2.2.1", "", {}, "sha512-JQTRVuiusQLNNLe2W9tnzBlV/GvSVcozLl4XZHk5swnRZ/v6jp8TqR8P7sqmJsQqblDZ3EztcWmLDbhRje/+8g=="],
-
- "@edge-runtime/node-utils": ["@edge-runtime/node-utils@2.3.0", "", {}, "sha512-uUtx8BFoO1hNxtHjp3eqVPC/mWImGb2exOfGjMLUoipuWgjej+f4o/VP4bUI8U40gu7Teogd5VTeZUkGvJSPOQ=="],
-
- "@edge-runtime/ponyfill": ["@edge-runtime/ponyfill@2.4.2", "", {}, "sha512-oN17GjFr69chu6sDLvXxdhg0Qe8EZviGSuqzR9qOiKh4MhFYGdBBcqRNzdmYeAdeRzOW2mM9yil4RftUQ7sUOA=="],
-
- "@edge-runtime/primitives": ["@edge-runtime/primitives@4.1.0", "", {}, "sha512-Vw0lbJ2lvRUqc7/soqygUX216Xb8T3WBZ987oywz6aJqRxcwSVWwr9e+Nqo2m9bxobA9mdbWNNoRY6S9eko1EQ=="],
-
- "@edge-runtime/vm": ["@edge-runtime/vm@3.2.0", "", { "dependencies": { "@edge-runtime/primitives": "4.1.0" } }, "sha512-0dEVyRLM/lG4gp1R/Ik5bfPl/1wX00xFwd5KcNH602tzBa09oF7pbTKETEhR1GjZ75K6OJnYFu8II2dyMhONMw=="],
-
- "@electric-sql/pglite": ["@electric-sql/pglite@0.3.16", "", {}, "sha512-mZkZfOd9OqTMHsK+1cje8OSzfAQcpD7JmILXTl5ahdempjUDdmg4euf1biDex5/LfQIDJ3gvCu6qDgdnDxfJmA=="],
-
"@elysiajs/eden": ["@elysiajs/eden@1.4.9", "", { "peerDependencies": { "elysia": ">=1.4.19" } }, "sha512-3CKVD4ycVjB8nCNssfmhnUuq3SzSHkUES3v5PNCFr9LxIrx39/HVRAZ8z2sLxrFqzUs48dCBZaxoZzJ5UUVHDA=="],
"@elysiajs/html": ["@elysiajs/html@1.4.2", "", { "dependencies": { "@kitajs/html": "^4.1.0", "@kitajs/ts-html-plugin": "^4.0.1" }, "peerDependencies": { "elysia": ">= 1.4.0" } }, "sha512-Db7dmbkN7gptckMpU0/Fq9Qi3QuhQr/CH60A+8rs+RT+74NUC8sONs5nkfMm5oL+6kCUWCv19uUwOjBP7zsjYQ=="],
@@ -209,14 +149,6 @@
"@elysiajs/static": ["@elysiajs/static@1.4.10", "", { "peerDependencies": { "elysia": ">= 1.4.0" } }, "sha512-BN/3PZVWjqkL/6TM7RH69Pae89CfU0OqfEYPeBp2hB/GR5Euds39Wy+0ug9Vbvu++GG4tRRANf2QjaLnriJveg=="],
- "@emnapi/core": ["@emnapi/core@1.11.2", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA=="],
-
- "@emnapi/runtime": ["@emnapi/runtime@1.11.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA=="],
-
- "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA=="],
-
- "@envelop/instrumentation": ["@envelop/instrumentation@1.0.0", "", { "dependencies": { "@whatwg-node/promise-helpers": "^1.2.1", "tslib": "^2.5.0" } }, "sha512-cxgkB66RQB95H3X27jlnxCRNTmPuSTgmBAq6/4n2Dtv4hsk4yz8FadA1ggmd0uZzvKqWD6CR+WFgTjhDqg7eyw=="],
-
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ=="],
"@esbuild/android-arm": ["@esbuild/android-arm@0.28.1", "", { "os": "android", "cpu": "arm" }, "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ=="],
@@ -269,3300 +201,426 @@
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.1", "", { "os": "win32", "cpu": "x64" }, "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A=="],
- "@fastify/accept-negotiator": ["@fastify/accept-negotiator@2.0.1", "", {}, "sha512-/c/TW2bO/v9JeEgoD/g1G5GxGeCF1Hafdf79WPmUlgYiBXummY0oX3VVq4yFkKKVBKDNlaDUYoab7g38RpPqCQ=="],
-
- "@fastify/ajv-compiler": ["@fastify/ajv-compiler@4.0.5", "", { "dependencies": { "ajv": "^8.12.0", "ajv-formats": "^3.0.1", "fast-uri": "^3.0.0" } }, "sha512-KoWKW+MhvfTRWL4qrhUwAAZoaChluo0m0vbiJlGMt2GXvL4LVPQEjt8kSpHI3IBq5Rez8fg+XeH3cneztq+C7A=="],
-
- "@fastify/busboy": ["@fastify/busboy@2.1.1", "", {}, "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA=="],
-
- "@fastify/error": ["@fastify/error@4.2.0", "", {}, "sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ=="],
-
- "@fastify/fast-json-stringify-compiler": ["@fastify/fast-json-stringify-compiler@5.0.3", "", { "dependencies": { "fast-json-stringify": "^6.0.0" } }, "sha512-uik7yYHkLr6fxd8hJSZ8c+xF4WafPK+XzneQDPU+D10r5X19GW8lJcom2YijX2+qtFF1ENJlHXKFM9ouXNJYgQ=="],
-
- "@fastify/forwarded": ["@fastify/forwarded@3.0.1", "", {}, "sha512-JqDochHFqXs3C3Ml3gOY58zM7OqO9ENqPo0UqAjAjH8L01fRZqwX9iLeX34//kiJubF7r2ZQHtBRU36vONbLlw=="],
-
- "@fastify/merge-json-schemas": ["@fastify/merge-json-schemas@0.2.1", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A=="],
-
- "@fastify/proxy-addr": ["@fastify/proxy-addr@5.1.0", "", { "dependencies": { "@fastify/forwarded": "^3.0.0", "ipaddr.js": "^2.1.0" } }, "sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw=="],
-
- "@fastify/send": ["@fastify/send@4.1.0", "", { "dependencies": { "@lukeed/ms": "^2.0.2", "escape-html": "~1.0.3", "fast-decode-uri-component": "^1.0.1", "http-errors": "^2.0.0", "mime": "^3" } }, "sha512-TMYeQLCBSy2TOFmV95hQWkiTYgC/SEx7vMdV+wnZVX4tt8VBLKzmH8vV9OzJehV0+XBfg+WxPMt5wp+JBUKsVw=="],
-
- "@fastify/static": ["@fastify/static@9.1.3", "", { "dependencies": { "@fastify/accept-negotiator": "^2.0.0", "@fastify/send": "^4.0.0", "content-disposition": "^1.0.1", "fastify-plugin": "^5.0.0", "fastq": "^1.17.1", "glob": "^13.0.0" } }, "sha512-aXrYtsiryLhRxRNaxNqsn7FUISeb7rB9q4eHUPIot5aeQBLNahnz1m6thzm7JWC1poSGXS9XrX8DvuMivp2hkQ=="],
-
- "@humanwhocodes/momoa": ["@humanwhocodes/momoa@2.0.4", "", {}, "sha512-RE815I4arJFtt+FVeU1Tgp9/Xvecacji8w/V6XtXsWWH/wz/eNkNbhb+ny/+PlVZjV0rxQpRSQKNKE3lcktHEA=="],
-
- "@iarna/toml": ["@iarna/toml@2.2.5", "", {}, "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg=="],
-
- "@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="],
-
- "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="],
-
- "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="],
-
- "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="],
-
- "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="],
-
- "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="],
-
- "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="],
-
- "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA=="],
-
- "@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.2.4", "", { "os": "linux", "cpu": "none" }, "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA=="],
-
- "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ=="],
-
- "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="],
-
- "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="],
-
- "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="],
-
- "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="],
-
- "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="],
-
- "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA=="],
-
- "@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.2.4" }, "os": "linux", "cpu": "none" }, "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw=="],
-
- "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.4" }, "os": "linux", "cpu": "s390x" }, "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg=="],
-
- "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="],
-
- "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="],
-
- "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="],
-
- "@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.5", "", { "dependencies": { "@emnapi/runtime": "^1.7.0" }, "cpu": "none" }, "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw=="],
-
- "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g=="],
-
- "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg=="],
-
- "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="],
-
- "@import-maps/resolve": ["@import-maps/resolve@2.0.0", "", {}, "sha512-RwzRTpmrrS6Q1ZhQExwuxJGK1Wqhv4stt+OF2JzS+uawewpwNyU7EJL1WpBex7aDiiGLs4FsXGkfUBdYuX7xiQ=="],
-
- "@inquirer/external-editor": ["@inquirer/external-editor@1.0.3", "", { "dependencies": { "chardet": "^2.1.1", "iconv-lite": "^0.7.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA=="],
-
- "@isaacs/balanced-match": ["@isaacs/balanced-match@4.0.1", "", {}, "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ=="],
-
- "@isaacs/brace-expansion": ["@isaacs/brace-expansion@5.0.1", "", { "dependencies": { "@isaacs/balanced-match": "^4.0.1" } }, "sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ=="],
-
- "@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="],
-
- "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="],
+ "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
- "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.9", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ=="],
+ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
"@kitajs/html": ["@kitajs/html@4.2.13", "", { "dependencies": { "csstype": "^3.1.3" } }, "sha512-o+8e61EsoLDPTP7rsPkYolca1YFybHuxU2Lr5fWDZCUkYT/6uBlVkvnZUdCXMQKentJL9dxwpR8/xK2Q+U4LhA=="],
"@kitajs/ts-html-plugin": ["@kitajs/ts-html-plugin@4.1.4", "", { "dependencies": { "chalk": "^5.6.2", "tslib": "^2.8.1", "yargs": "^18.0.0" }, "peerDependencies": { "@kitajs/html": "^4.2.10", "typescript": "^5.9.3" }, "bin": { "xss-scan": "dist/cli.js", "ts-html-plugin": "dist/cli.js" } }, "sha512-xK5mNrhnIy73kJFKx5yVGChJyWFRGmIaE0sjlVxVYllk5dyaEYVCrIh1N8AfnseEHka8gAqzPGW95HlkhDvnJA=="],
- "@lukeed/ms": ["@lukeed/ms@2.0.2", "", {}, "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA=="],
-
- "@mapbox/node-pre-gyp": ["@mapbox/node-pre-gyp@2.0.3", "", { "dependencies": { "consola": "^3.2.3", "detect-libc": "^2.0.0", "https-proxy-agent": "^7.0.5", "node-fetch": "^2.6.7", "nopt": "^8.0.0", "semver": "^7.5.3", "tar": "^7.4.0" }, "bin": { "node-pre-gyp": "bin/node-pre-gyp" } }, "sha512-uwPAhccfFJlsfCxMYTwOdVfOz3xqyj8xYL3zJj8f0pb30tLohnnFPhLuqp4/qoEz8sNxe4SESZedcBojRefIzg=="],
-
- "@napi-rs/keyring": ["@napi-rs/keyring@1.2.0", "", { "optionalDependencies": { "@napi-rs/keyring-darwin-arm64": "1.2.0", "@napi-rs/keyring-darwin-x64": "1.2.0", "@napi-rs/keyring-freebsd-x64": "1.2.0", "@napi-rs/keyring-linux-arm-gnueabihf": "1.2.0", "@napi-rs/keyring-linux-arm64-gnu": "1.2.0", "@napi-rs/keyring-linux-arm64-musl": "1.2.0", "@napi-rs/keyring-linux-riscv64-gnu": "1.2.0", "@napi-rs/keyring-linux-x64-gnu": "1.2.0", "@napi-rs/keyring-linux-x64-musl": "1.2.0", "@napi-rs/keyring-win32-arm64-msvc": "1.2.0", "@napi-rs/keyring-win32-ia32-msvc": "1.2.0", "@napi-rs/keyring-win32-x64-msvc": "1.2.0" } }, "sha512-d0d4Oyxm+v980PEq1ZH2PmS6cvpMIRc17eYpiU47KgW+lzxklMu6+HOEOPmxrpnF/XQZ0+Q78I2mgMhbIIo/dg=="],
-
- "@napi-rs/keyring-darwin-arm64": ["@napi-rs/keyring-darwin-arm64@1.2.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-CA83rDeyONDADO25JLZsh3eHY8yTEtm/RS6ecPsY+1v+dSawzT9GywBMu2r6uOp1IEhQs/xAfxgybGAFr17lSA=="],
-
- "@napi-rs/keyring-darwin-x64": ["@napi-rs/keyring-darwin-x64@1.2.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-dBHjtKRCj4ByfnfqIKIJLo3wueQNJhLRyuxtX/rR4K/XtcS7VLlRD01XXizjpre54vpmObj63w+ZpHG+mGM8uA=="],
-
- "@napi-rs/keyring-freebsd-x64": ["@napi-rs/keyring-freebsd-x64@1.2.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-DPZFr11pNJSnaoh0dzSUNF+T6ORhy3CkzUT3uGixbA71cAOPJ24iG8e8QrLOkuC/StWrAku3gBnth2XMWOcR3Q=="],
-
- "@napi-rs/keyring-linux-arm-gnueabihf": ["@napi-rs/keyring-linux-arm-gnueabihf@1.2.0", "", { "os": "linux", "cpu": "arm" }, "sha512-8xv6DyEMlvRdqJzp4F39RLUmmTQsLcGYYv/3eIfZNZN1O5257tHxTrFYqAsny659rJJK2EKeSa7PhrSibQqRWQ=="],
-
- "@napi-rs/keyring-linux-arm64-gnu": ["@napi-rs/keyring-linux-arm64-gnu@1.2.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-Pu2V6Py+PBt7inryEecirl+t+ti8bhZphjP+W68iVaXHUxLdWmkgL9KI1VkbRHbx5k8K5Tew9OP218YfmVguIA=="],
-
- "@napi-rs/keyring-linux-arm64-musl": ["@napi-rs/keyring-linux-arm64-musl@1.2.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-8TDymrpC4P1a9iDEaegT7RnrkmrJN5eNZh3Im3UEV5PPYGtrb82CRxsuFohthCWQW81O483u1bu+25+XA4nKUw=="],
-
- "@napi-rs/keyring-linux-riscv64-gnu": ["@napi-rs/keyring-linux-riscv64-gnu@1.2.0", "", { "os": "linux", "cpu": "none" }, "sha512-awsB5XI1MYL7fwfjMDGmKOWvNgJEO7mM7iVEMS0fO39f0kVJnOSjlu7RHcXAF0LOx+0VfF3oxbWqJmZbvRCRHw=="],
-
- "@napi-rs/keyring-linux-x64-gnu": ["@napi-rs/keyring-linux-x64-gnu@1.2.0", "", { "os": "linux", "cpu": "x64" }, "sha512-8E+7z4tbxSJXxIBqA+vfB1CGajpCDRyTyqXkBig5NtASrv4YXcntSo96Iah2QDR5zD3dSTsmbqJudcj9rKKuHQ=="],
-
- "@napi-rs/keyring-linux-x64-musl": ["@napi-rs/keyring-linux-x64-musl@1.2.0", "", { "os": "linux", "cpu": "x64" }, "sha512-8RZ8yVEnmWr/3BxKgBSzmgntI7lNEsY7xouNfOsQkuVAiCNmxzJwETspzK3PQ2FHtDxgz5vHQDEBVGMyM4hUHA=="],
-
- "@napi-rs/keyring-win32-arm64-msvc": ["@napi-rs/keyring-win32-arm64-msvc@1.2.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-AoqaDZpQ6KPE19VBLpxyORcp+yWmHI9Xs9Oo0PJ4mfHma4nFSLVdhAubJCxdlNptHe5va7ghGCHj3L9Akiv4cQ=="],
-
- "@napi-rs/keyring-win32-ia32-msvc": ["@napi-rs/keyring-win32-ia32-msvc@1.2.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-EYL+EEI6bCsYi3LfwcQdnX3P/R76ENKNn+3PmpGheBsUFLuh0gQuP7aMVHM4rTw6UVe+L3vCLZSptq/oeacz0A=="],
-
- "@napi-rs/keyring-win32-x64-msvc": ["@napi-rs/keyring-win32-x64-msvc@1.2.0", "", { "os": "win32", "cpu": "x64" }, "sha512-xFlx/TsmqmCwNU9v+AVnEJgoEAlBYgzFF5Ihz1rMpPAt4qQWWkMd4sCyM1gMJ1A/GnRqRegDiQpwaxGUHFtFbA=="],
-
- "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.6", "", { "dependencies": { "@tybys/wasm-util": "^0.10.3" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg=="],
-
- "@netlify/ai": ["@netlify/ai@0.4.2", "", { "dependencies": { "@netlify/api": "^14.0.19" } }, "sha512-PWHLAEKG16KCUE+O+je+aoXEUs/tUsXPoMcHfzYxFgJ4xczTIY97iaqu62cW9kf9KzZ27TV/t6gqTAX5NKO6ww=="],
-
- "@netlify/api": ["@netlify/api@14.0.19", "", { "dependencies": { "@netlify/open-api": "^2.52.0", "node-fetch": "^3.0.0", "p-wait-for": "^5.0.0", "picoquery": "^2.5.0" } }, "sha512-oJ+7K+ioZ8sNoNzJbx+uIDmRynf5A+0ler2t9nHhhfwrrhx7mWgFaX5vGiEjvkpIxBAwfW8XyedhEdsm2OCK+w=="],
-
- "@netlify/binary-info": ["@netlify/binary-info@1.0.0", "", {}, "sha512-4wMPu9iN3/HL97QblBsBay3E1etIciR84izI3U+4iALY+JHCrI+a2jO0qbAZ/nxKoegypYEaiiqWXylm+/zfrw=="],
-
- "@netlify/blobs": ["@netlify/blobs@10.7.9", "", { "dependencies": { "@netlify/dev-utils": "4.4.6", "@netlify/otel": "^6.0.3", "@netlify/runtime-utils": "2.3.0" } }, "sha512-NEM8aNAMZCCWBWymomMM/fn5wmPGyGE8pendgsSu5mL7UNz+aXqGcHS0MiHWU/osyg+geiZuS+Rx956SVrMM/w=="],
-
- "@netlify/build": ["@netlify/build@35.15.0", "", { "dependencies": { "@bugsnag/js": "^8.0.0", "@netlify/blobs": "^10.4.4", "@netlify/cache-utils": "^6.0.5", "@netlify/config": "^24.6.0", "@netlify/edge-bundler": "14.10.3", "@netlify/functions-utils": "^6.2.35", "@netlify/git-utils": "^6.0.4", "@netlify/opentelemetry-utils": "^2.0.2", "@netlify/plugins-list": "^6.81.6", "@netlify/run-utils": "^6.0.3", "@netlify/zip-it-and-ship-it": "14.7.1", "@sindresorhus/slugify": "^2.0.0", "ansi-escapes": "^7.0.0", "ansis": "^4.1.0", "clean-stack": "^5.0.0", "execa": "^8.0.0", "fdir": "^6.0.1", "figures": "^6.0.0", "filter-obj": "^6.0.0", "hot-shots": "11.4.0", "ignore": "^7.0.0", "indent-string": "^5.0.0", "is-plain-obj": "^4.0.0", "keep-func-props": "^6.0.0", "log-process-errors": "^11.0.0", "memoize-one": "^6.0.0", "minimatch": "^10.2.4", "os-name": "^6.0.0", "p-event": "^6.0.0", "p-filter": "^4.0.0", "p-locate": "^6.0.0", "p-map": "^7.0.0", "p-reduce": "^3.0.0", "package-directory": "^8.0.0", "path-exists": "^5.0.0", "pretty-ms": "^9.0.0", "ps-list": "^8.0.0", "read-package-up": "^11.0.0", "readdirp": "^4.0.0", "resolve": "^2.0.0-next.5", "rfdc": "^1.3.0", "safe-json-stringify": "^1.2.0", "semver": "^7.3.8", "string-width": "^7.0.0", "supports-color": "^10.0.0", "terminal-link": "^4.0.0", "ts-node": "^10.9.1", "typescript": "^5.0.0", "yaml": "^2.8.0", "yargs": "^17.6.0", "zod": "^3.25.76" }, "peerDependencies": { "@netlify/opentelemetry-sdk-setup": "^2.0.0", "@opentelemetry/api": "~1.8.0" }, "optionalPeers": ["@netlify/opentelemetry-sdk-setup"], "bin": { "netlify-build": "./bin.js" } }, "sha512-Ig/EiLjLn3tKJ6+YobKwdOQtpnrpJobB+BZpZBOeeFJq4GX3452709VDaqnGb5X5nDrRcrqabHUglZ72FgdQFw=="],
-
- "@netlify/build-info": ["@netlify/build-info@10.5.1", "", { "dependencies": { "@bugsnag/js": "^8.0.0", "@iarna/toml": "^2.2.5", "dot-prop": "^9.0.0", "find-up": "^7.0.0", "minimatch": "^10.2.4", "read-pkg": "^9.0.0", "semver": "^7.3.8", "yaml": "^2.8.0", "yargs": "^17.6.0" }, "bin": { "build-info": "./bin.js" } }, "sha512-AQqI6S7bMazA/wYnsuE8Lp95ldeKO5RsYvQEboKAjGV2NiwDqw44Z1n7cKj8OALdp8coTvIKLcbAounx6ERbiA=="],
-
- "@netlify/cache": ["@netlify/cache@3.4.8", "", { "dependencies": { "@netlify/runtime-utils": "2.3.0" } }, "sha512-20zPmLS8x2VXSNQHwxj1+jJgKAec/EoeKJahIgPmTolXq2X6nEdyulHTTHHA4KzxqGTkrzTcfNzZjEkjvRyj9Q=="],
-
- "@netlify/cache-utils": ["@netlify/cache-utils@6.0.5", "", { "dependencies": { "cpy": "^11.0.0", "get-stream": "^9.0.0", "globby": "^14.0.0", "junk": "^4.0.0", "locate-path": "^7.0.0", "move-file": "^3.0.0", "readdirp": "^4.0.0" } }, "sha512-MZmTLVR+KabjCFOvSGZj2ZbGPCutqKVWuyAihJHQ8f8BGGOzu6vzuBmXf6L5nT1d2u8ZifjDXrHhCH4SNxdb1g=="],
-
- "@netlify/config": ["@netlify/config@24.6.0", "", { "dependencies": { "@iarna/toml": "^2.2.5", "@netlify/api": "^14.0.19", "@netlify/headers-parser": "^9.0.3", "@netlify/redirect-parser": "^15.0.4", "chalk": "^5.0.0", "cron-parser": "^4.1.0", "deepmerge": "^4.2.2", "dot-prop": "^9.0.0", "execa": "^8.0.0", "fast-safe-stringify": "^2.0.7", "figures": "^6.0.0", "filter-obj": "^6.0.0", "find-up": "^7.0.0", "indent-string": "^5.0.0", "is-plain-obj": "^4.0.0", "map-obj": "^5.0.0", "omit.js": "^2.0.2", "p-locate": "^6.0.0", "path-type": "^6.0.0", "read-package-up": "^11.0.0", "tomlify-j0.4": "^3.0.0", "validate-npm-package-name": "^5.0.0", "yaml": "^2.8.0", "yargs": "^17.6.0", "zod": "^4.0.5" }, "bin": { "netlify-config": "./bin.js" } }, "sha512-bR2FhZiWFz+EIQBl/T9p9lRNBuInYqWtqtGQywwM1dkvNI2ViSQBmeEDY/qXh9EECB8g2f7ZBC2sF4KeNGtX0w=="],
-
- "@netlify/database-dev": ["@netlify/database-dev@0.10.1", "", { "dependencies": { "@electric-sql/pglite": "^0.3.15", "pg-gateway": "0.3.0-beta.4" } }, "sha512-kHLdS6r45TsDiS5aBrHUmzqPvoaWQEUZnaZeMwnIrLMwX8f2bIa6YAMOJJYJhyKm2drVo3C/T92/lJ5iGV/VBQ=="],
-
- "@netlify/dev": ["@netlify/dev@4.18.9", "", { "dependencies": { "@netlify/ai": "^0.4.2", "@netlify/blobs": "10.7.9", "@netlify/config": "^24.6.0", "@netlify/database-dev": "0.10.1", "@netlify/dev-utils": "4.4.6", "@netlify/edge-functions-dev": "1.0.22", "@netlify/functions-dev": "1.3.1", "@netlify/headers": "2.1.11", "@netlify/images": "1.3.10", "@netlify/redirects": "3.1.13", "@netlify/runtime": "4.1.25", "@netlify/static": "3.1.10", "ulid": "^3.0.0" } }, "sha512-cHXktcJQuVHGukRhMkdfYvN8/bsvkILh5wEKeVRmmlML6oCZnxnii9mcNjhQbQ5NotsbW6/NShuuEH3caufG6Q=="],
-
- "@netlify/dev-utils": ["@netlify/dev-utils@4.4.6", "", { "dependencies": { "@whatwg-node/server": "^0.10.0", "ansis": "^4.1.0", "atomically": "^2.0.3", "chokidar": "^4.0.1", "decache": "^4.6.2", "dettle": "^1.0.5", "dot-prop": "9.0.0", "empathic": "^2.0.0", "env-paths": "^3.0.0", "image-size": "^2.0.2", "js-image-generator": "^1.0.4", "parse-gitignore": "^2.0.0", "semver": "^7.7.2", "tmp-promise": "^3.0.3" } }, "sha512-P6X+xS3glvhiTX6AAocYvnZtqXtWhvxMX4AdPgv2iw6cXjGL2criUveX7bSjp6DiyHfvQpNdG0ZRpeBEVAFf1g=="],
-
- "@netlify/edge-bundler": ["@netlify/edge-bundler@14.10.3", "", { "dependencies": { "@import-maps/resolve": "^2.0.0", "@sveltejs/acorn-typescript": "^1.0.9", "acorn": "^8.15.0", "ajv": "^8.11.2", "ajv-errors": "^3.0.0", "better-ajv-errors": "^1.2.0", "common-path-prefix": "^3.0.0", "env-paths": "^3.0.0", "esbuild": "0.28.0", "execa": "^8.0.0", "find-up": "^7.0.0", "get-port": "^7.0.0", "node-stream-zip": "^1.15.0", "p-retry": "^6.0.0", "p-wait-for": "^5.0.0", "parse-imports": "^2.2.1", "path-key": "^4.0.0", "semver": "^7.3.8", "tar": "^7.5.12", "tmp-promise": "^3.0.3", "urlpattern-polyfill": "8.0.2" } }, "sha512-o+ww3ZUsdkdDEdeIhX6yT4wCNt99ar3JUreQowgXPCAyQeiXWf2i3xkoVwm5bQb77BhFmTTMp0kIeRMiGEqU4g=="],
-
- "@netlify/edge-functions": ["@netlify/edge-functions@3.0.8", "", { "dependencies": { "@netlify/types": "2.8.0" } }, "sha512-ml1oCDsRTTRmZS2nUj8XRD1b6+foEiZT3lPk7qQ4nv/jnxylHJ20ooPzPDH+cJmGjXFrMeR14/91W1o6D2ftBg=="],
-
- "@netlify/edge-functions-bootstrap": ["@netlify/edge-functions-bootstrap@2.17.1", "", {}, "sha512-KyNJbDhK1rC5wEeI7bXPgfl8QvADMHqNy2nwNJG60EHVRXTF0zxFnOpt/p0m2C512gcMXRrKZxaOZQ032RHVbw=="],
-
- "@netlify/edge-functions-dev": ["@netlify/edge-functions-dev@1.0.22", "", { "dependencies": { "@netlify/dev-utils": "4.4.6", "@netlify/edge-bundler": "^14.10.3", "@netlify/edge-functions": "3.0.8", "@netlify/edge-functions-bootstrap": "2.16.0", "@netlify/runtime-utils": "2.3.0", "get-port": "^7.1.0" } }, "sha512-FmN7aikLn1fs+GUpcxgyM+MgEhwRfwz6XoGF+pAjDMO27eA3Vt8IeV/gx/hZfCeehMFHev4X2vENC1WXlmtKhQ=="],
-
- "@netlify/functions": ["@netlify/functions@5.3.0", "", { "dependencies": { "@netlify/types": "2.8.0" } }, "sha512-FP+xCoZMkMOUsKa4UdG/oiK/2md1/TxuXvqqieaMVMgDbFFMaHI8h0oHCViUY73kPbKV6HyzayaSXGYXKpBSoQ=="],
-
- "@netlify/functions-dev": ["@netlify/functions-dev@1.3.1", "", { "dependencies": { "@netlify/blobs": "10.7.9", "@netlify/dev-utils": "4.4.6", "@netlify/functions": "5.3.0", "@netlify/zip-it-and-ship-it": "^14.7.1", "cron-parser": "^4.9.0", "decache": "^4.6.2", "extract-zip": "^2.0.1", "is-stream": "^4.0.1", "jwt-decode": "^4.0.0", "lambda-local": "^2.2.0", "read-package-up": "^11.0.0", "semver": "^7.6.3", "source-map-support": "^0.5.21" } }, "sha512-zoJ/Q1ODjQLMQN2zvtPpDBqLUjE0gRCahQcj9w+IJ/5k9ys9bK0hb5a7ar/LjUsXWA5o9bNqHbJFy3a4RgDl6Q=="],
-
- "@netlify/functions-utils": ["@netlify/functions-utils@6.2.35", "", { "dependencies": { "@netlify/zip-it-and-ship-it": "14.7.1", "cpy": "^11.0.0", "path-exists": "^5.0.0" } }, "sha512-PvJljLaZt1Tq3hwNmddnmN51J6w8v77iOdKwQaMA9jyINno8y6PP+uabb0kvAGO+KQ9r380IuUFPiHVv3VgKzw=="],
-
- "@netlify/git-utils": ["@netlify/git-utils@6.0.4", "", { "dependencies": { "execa": "^8.0.0", "map-obj": "^5.0.0", "micro-memoize": "^5.1.1", "micromatch": "^4.0.2" } }, "sha512-yp5WucNt1PGV+qG9Bq/qXPHZyl375s2AfB7bdoCFi7VeHZzCJ4bZJB9OpWf8lwNKqzkc2njYygNo5kwKavzY/w=="],
-
- "@netlify/headers": ["@netlify/headers@2.1.11", "", { "dependencies": { "@netlify/headers-parser": "^9.0.3" } }, "sha512-pWqoovsTBKlSfM7HKT0KnZ3x5ael2P+smiAogH5zuGU+ncQA7zBRHwLDi3nBy4iBM1MQAZ9BEjAXE42/yn/GQg=="],
-
- "@netlify/headers-parser": ["@netlify/headers-parser@9.0.3", "", { "dependencies": { "@iarna/toml": "^2.2.5", "escape-string-regexp": "^5.0.0", "fast-safe-stringify": "^2.0.7", "is-plain-obj": "^4.0.0", "map-obj": "^5.0.0", "path-exists": "^5.0.0" } }, "sha512-KNzC9RaKDwJVS44iTK6JxNA6LeXH0PUw0pLktWpmMVI/0FR98bvxaHcAisjHqbThAjxL9QjL1UZh0KzHCkxpNQ=="],
-
- "@netlify/images": ["@netlify/images@1.3.10", "", { "dependencies": { "ipx": "^3.1.1" } }, "sha512-3ZlN8PNBaZFNr+uERzOtb9rQkb23xxS4k4iW188mgvSrMhxdVXPS5pkpOxAHPEunatSlT/a6oCuy3fsltrtr6A=="],
-
- "@netlify/local-functions-proxy": ["@netlify/local-functions-proxy@2.0.3", "", { "optionalDependencies": { "@netlify/local-functions-proxy-darwin-arm64": "1.1.1", "@netlify/local-functions-proxy-darwin-x64": "1.1.1", "@netlify/local-functions-proxy-freebsd-arm64": "1.1.1", "@netlify/local-functions-proxy-freebsd-x64": "1.1.1", "@netlify/local-functions-proxy-linux-arm": "1.1.1", "@netlify/local-functions-proxy-linux-arm64": "1.1.1", "@netlify/local-functions-proxy-linux-ia32": "1.1.1", "@netlify/local-functions-proxy-linux-ppc64": "1.1.1", "@netlify/local-functions-proxy-linux-x64": "1.1.1", "@netlify/local-functions-proxy-openbsd-x64": "1.1.1", "@netlify/local-functions-proxy-win32-ia32": "1.1.1", "@netlify/local-functions-proxy-win32-x64": "1.1.1" } }, "sha512-siVwmrp7Ow+7jLALi6jXOja4Y4uHMMgOLLQMgd+OZ1TESOstrJvkUisJEDAc9hx7u0v/B0mh5g1g1huiH3uS3A=="],
-
- "@netlify/local-functions-proxy-darwin-arm64": ["@netlify/local-functions-proxy-darwin-arm64@1.1.1", "", { "os": "darwin", "cpu": "arm64", "bin": { "local-functions-proxy": "bin/local-functions-proxy" } }, "sha512-lphJ9qqZ3glnKWEqlemU1LMqXxtJ/tKf7VzakqqyjigwLscXSZSb6fupSjQfd4tR1xqxA76ylws/2HDhc/gs+Q=="],
-
- "@netlify/local-functions-proxy-darwin-x64": ["@netlify/local-functions-proxy-darwin-x64@1.1.1", "", { "os": "darwin", "cpu": "x64", "bin": { "local-functions-proxy": "bin/local-functions-proxy" } }, "sha512-4CRB0H+dXZzoEklq5Jpmg+chizXlVwCko94d8+UHWCgy/bA3M/rU/BJ8OLZisnJaAktHoeLABKtcLOhtRHpxZQ=="],
+ "@nodable/entities": ["@nodable/entities@2.2.0", "", {}, "sha512-9uGyhaQavEUMC8AIddIjau4NsnsXhou+j5sBAGojCM1oxmQpVKTWR/9JxABD6UAv12vpIms55fPZKFQEhG6uBg=="],
- "@netlify/local-functions-proxy-freebsd-arm64": ["@netlify/local-functions-proxy-freebsd-arm64@1.1.1", "", { "os": "freebsd", "cpu": "arm64", "bin": { "local-functions-proxy": "bin/local-functions-proxy" } }, "sha512-u13lWTVMJDF0A6jX7V4N3HYGTIHLe5d1Z2wT43fSIHwXkTs6UXi72cGSraisajG+5JFIwHfPr7asw5vxFC0P9w=="],
+ "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.62.2", "", { "os": "android", "cpu": "arm" }, "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg=="],
- "@netlify/local-functions-proxy-freebsd-x64": ["@netlify/local-functions-proxy-freebsd-x64@1.1.1", "", { "os": "freebsd", "cpu": "x64", "bin": { "local-functions-proxy": "bin/local-functions-proxy" } }, "sha512-g5xw4xATK5YDzvXtzJ8S1qSkWBiyF8VVRehXPMOAMzpGjCX86twYhWp8rbAk7yA1zBWmmWrWNA2Odq/MgpKJJg=="],
+ "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.62.2", "", { "os": "android", "cpu": "arm64" }, "sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw=="],
- "@netlify/local-functions-proxy-linux-arm": ["@netlify/local-functions-proxy-linux-arm@1.1.1", "", { "os": "linux", "cpu": "arm", "bin": { "local-functions-proxy": "bin/local-functions-proxy" } }, "sha512-YsTpL+AbHwQrfHWXmKnwUrJBjoUON363nr6jUG1ueYnpbbv6wTUA7gI5snMi/gkGpqFusBthAA7C30e6bixfiA=="],
+ "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.62.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A=="],
- "@netlify/local-functions-proxy-linux-arm64": ["@netlify/local-functions-proxy-linux-arm64@1.1.1", "", { "os": "linux", "cpu": "arm64", "bin": { "local-functions-proxy": "bin/local-functions-proxy" } }, "sha512-dPGu1H5n8na7mBKxiXQ+FNmthDAiA57wqgpm5JMAHtcdcmRvcXwJkwWVGvwfj8ShhYJHQaSaS9oPgO+mpKkgmA=="],
+ "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.62.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA=="],
- "@netlify/local-functions-proxy-linux-ia32": ["@netlify/local-functions-proxy-linux-ia32@1.1.1", "", { "os": "linux", "cpu": "ia32", "bin": { "local-functions-proxy": "bin/local-functions-proxy" } }, "sha512-Ra0FlXDrmPRaq+rYH3/ttkXSrwk1D5Zx/Na7UPfJZxMY7Qo5iY4bgi/FuzjzWzlp0uuKZOhYOYzYzsIIyrSvmw=="],
+ "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.62.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw=="],
- "@netlify/local-functions-proxy-linux-ppc64": ["@netlify/local-functions-proxy-linux-ppc64@1.1.1", "", { "os": "linux", "cpu": "ppc64", "bin": { "local-functions-proxy": "bin/local-functions-proxy" } }, "sha512-oXf1satwqwUUxz7LHS1BxbRqc4FFEKIDFTls04eXiLReFR3sqv9H/QuYNTCCDMuRcCOd92qKyDfATdnxT4HR8w=="],
+ "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.62.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg=="],
- "@netlify/local-functions-proxy-linux-x64": ["@netlify/local-functions-proxy-linux-x64@1.1.1", "", { "os": "linux", "cpu": "x64", "bin": { "local-functions-proxy": "bin/local-functions-proxy" } }, "sha512-bS3u4JuDg/eC0y4Na3i/29JBOxrdUvsK5JSjHfzUeZEbOcuXYf4KavTpHS5uikdvTgyczoSrvbmQJ5m0FLXfLA=="],
+ "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.62.2", "", { "os": "linux", "cpu": "arm" }, "sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg=="],
- "@netlify/local-functions-proxy-openbsd-x64": ["@netlify/local-functions-proxy-openbsd-x64@1.1.1", "", { "os": "openbsd", "cpu": "x64", "bin": { "local-functions-proxy": "bin/local-functions-proxy" } }, "sha512-1xLef/kLRNkBTXJ+ZGoRFcwsFxd/B2H3oeJZyXaZ3CN5umd9Mv9wZuAD74NuMt/535yRva8jtAJqvEgl9xMSdA=="],
+ "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.62.2", "", { "os": "linux", "cpu": "arm" }, "sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA=="],
- "@netlify/local-functions-proxy-win32-ia32": ["@netlify/local-functions-proxy-win32-ia32@1.1.1", "", { "os": "win32", "cpu": "ia32", "bin": { "local-functions-proxy.exe": "bin/local-functions-proxy.exe" } }, "sha512-4IOMDBxp2f8VbIkhZ85zGNDrZR4ey8d68fCMSOIwitjsnKav35YrCf8UmAh3UR6CNIRJdJL4MW1GYePJ7iJ8uA=="],
+ "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.62.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA=="],
- "@netlify/local-functions-proxy-win32-x64": ["@netlify/local-functions-proxy-win32-x64@1.1.1", "", { "os": "win32", "cpu": "x64", "bin": { "local-functions-proxy.exe": "bin/local-functions-proxy.exe" } }, "sha512-VCBXBJWBujVxyo5f+3r8ovLc9I7wJqpmgDn3ixs1fvdrER5Ac+SzYwYH4mUug9HI08mzTSAKZErzKeuadSez3w=="],
+ "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.62.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ=="],
- "@netlify/open-api": ["@netlify/open-api@2.56.0", "", {}, "sha512-EhhCDnk6Pga/joEtTif9Lt4lEHwR7o1MyMImonpZBtdAaekaRt07M8rXDCvzg/IFfOlth6NWGz0WcuuxHMOiOg=="],
+ "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.62.2", "", { "os": "linux", "cpu": "none" }, "sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg=="],
- "@netlify/opentelemetry-utils": ["@netlify/opentelemetry-utils@2.0.2", "", { "peerDependencies": { "@opentelemetry/api": "~1.8.0" } }, "sha512-ZnSw1AV/7kBWOwwXjobcd/LXhy576qrnla0PnKs/U4zBa5R3X9YNnFq29251l/nJ8GmnBzJI7dt9dfTfn8Z80w=="],
+ "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.62.2", "", { "os": "linux", "cpu": "none" }, "sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ=="],
- "@netlify/otel": ["@netlify/otel@6.0.3", "", { "dependencies": { "@opentelemetry/api": "1.9.0", "@opentelemetry/core": "2.7.1", "@opentelemetry/instrumentation": "^0.217.0", "@opentelemetry/resources": "2.7.1", "@opentelemetry/sdk-trace-node": "2.7.1" } }, "sha512-NIjIjB/aItiXKB6+wzSwfSyYqbNsVSzzlEPryxxTC5ZJiYSYSm82wAOcQ+9VdiufQyZO5t8jzHVPULo7a9L9sQ=="],
+ "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.62.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A=="],
- "@netlify/plugins-list": ["@netlify/plugins-list@6.81.8", "", {}, "sha512-ur/f3aO+TaBI1OQEmWnHcYaenizj0hAgoB1n3Pff+S1dddqSwwFmw5Z7MWaXqvaRrkVUyxiRT5IgeKdt4CW7Hg=="],
+ "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.62.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w=="],
- "@netlify/redirect-parser": ["@netlify/redirect-parser@15.0.4", "", { "dependencies": { "@iarna/toml": "^2.2.5", "fast-safe-stringify": "^2.1.1", "is-plain-obj": "^4.0.0", "path-exists": "^5.0.0" } }, "sha512-UYHRCO4HZI6WMpf8RheaCWnGafeJeFTsp/5yK887fyGqohDmFbc26NuFUvRl7J6sNu+di/1lLmRXP+yJ1X9TDA=="],
+ "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.62.2", "", { "os": "linux", "cpu": "none" }, "sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg=="],
- "@netlify/redirects": ["@netlify/redirects@3.1.13", "", { "dependencies": { "@netlify/dev-utils": "4.4.6", "@netlify/redirect-parser": "^15.0.4", "cookie": "^1.0.2", "jsonwebtoken": "9.0.3", "netlify-redirector": "^0.5.0" } }, "sha512-28M4pyahGHf5kIqhdA6Y687g/QBmW0clPHmh/xGvxmwHIW+fN5aMzCO4JrM2v9bA/yDFoGjSQynckAG1MGr0/g=="],
+ "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.62.2", "", { "os": "linux", "cpu": "none" }, "sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q=="],
- "@netlify/run-utils": ["@netlify/run-utils@6.0.3", "", { "dependencies": { "execa": "^8.0.0" } }, "sha512-ooXXGQKtTJEYxCgwcwpr3a/cHYv+oIuKgswzAMCTOnE/lm4DU9+kz42vMXA+hXoMJFS6kSnuKsSIL9GePaJ0gQ=="],
+ "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.62.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg=="],
- "@netlify/runtime": ["@netlify/runtime@4.1.25", "", { "dependencies": { "@netlify/blobs": "^10.7.9", "@netlify/cache": "3.4.8", "@netlify/runtime-utils": "2.3.0", "@netlify/types": "2.8.0" } }, "sha512-+RKE+oaygMDyp1ybcseQRT/OMVkiNOgUDfM0Q4+Nwl+vMGCZavmHKIAkGWRWlduw8UUcA094xSoHNcLQWCFgjg=="],
+ "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.62.2", "", { "os": "linux", "cpu": "x64" }, "sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A=="],
- "@netlify/runtime-utils": ["@netlify/runtime-utils@2.3.0", "", {}, "sha512-cW8weDvsKV7zfia2m5EcBy6KILGoPD+eYZ3qWNGnIo05DGF28goPES0xKSDkNYgAF/2rRSIhie2qcBhbGVgSRg=="],
+ "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.62.2", "", { "os": "linux", "cpu": "x64" }, "sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg=="],
- "@netlify/serverless-functions-api": ["@netlify/serverless-functions-api@2.16.0", "", { "dependencies": { "@netlify/types": "^2.6.0" } }, "sha512-yomP0pYRWPREiAuQbLghz9YbMb2+Mkmk1DMjyXVbIyrJ8QZhEtGcYQWAyl6cgki5CVqnOXvfIrRdLSf8bvxJCA=="],
+ "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.62.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg=="],
- "@netlify/static": ["@netlify/static@3.1.10", "", { "dependencies": { "mime-types": "^3.0.0" } }, "sha512-DOEVJ8SawvQ/ddWCSzfjqsQOE4GfF+LvxhWuB1OHSFvWnDEyBpobrLO6YWUG3lKq+0OhPBu50XFtKfdQNpUiFA=="],
+ "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.62.2", "", { "os": "none", "cpu": "arm64" }, "sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA=="],
- "@netlify/types": ["@netlify/types@2.8.0", "", {}, "sha512-8/g0Pt6y6wXj5Ia5eeYLiXhRfWeqZXGXpGFeCiiQdUOem+FPtXdA4+YdGxqzWc7D0AvptKSO01KGeeVWHSu8Kg=="],
+ "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.62.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg=="],
- "@netlify/zip-it-and-ship-it": ["@netlify/zip-it-and-ship-it@14.7.1", "", { "dependencies": { "@babel/parser": "^7.22.5", "@babel/types": "^7.28.5", "@netlify/binary-info": "^1.0.0", "@netlify/serverless-functions-api": "2.16.0", "@vercel/nft": "0.29.4", "archiver": "^7.0.0", "common-path-prefix": "^3.0.0", "copy-file": "^11.0.0", "es-module-lexer": "^1.0.0", "esbuild": "0.28.0", "execa": "^8.0.0", "fast-glob": "^3.3.3", "filter-obj": "^6.0.0", "find-up": "^7.0.0", "is-path-inside": "^4.0.0", "junk": "^4.0.0", "locate-path": "^7.0.0", "merge-options": "^3.0.4", "minimatch": "^10.2.4", "normalize-path": "^3.0.0", "p-map": "^7.0.0", "path-exists": "^5.0.0", "precinct": "^12.0.0", "require-package-name": "^2.0.1", "resolve": "^2.0.0-next.1", "semver": "^7.3.8", "tmp-promise": "^3.0.2", "toml": "^3.0.0", "unixify": "^1.0.0", "urlpattern-polyfill": "8.0.2", "yargs": "^17.0.0", "zod": "^3.23.8" }, "bin": { "zip-it-and-ship-it": "./bin.js" } }, "sha512-In1BkoGFct5tid87oPTkTsITXrgUElgKDKzEmtxlezA4Oo95t2Gnv5m3IRVbI6eRcp3uygahxQPv/J8meX+n1Q=="],
+ "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.62.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q=="],
- "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
+ "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.62.2", "", { "os": "win32", "cpu": "x64" }, "sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg=="],
- "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
+ "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.62.2", "", { "os": "win32", "cpu": "x64" }, "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA=="],
- "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
-
- "@octokit/auth-token": ["@octokit/auth-token@6.0.0", "", {}, "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w=="],
-
- "@octokit/core": ["@octokit/core@7.0.6", "", { "dependencies": { "@octokit/auth-token": "^6.0.0", "@octokit/graphql": "^9.0.3", "@octokit/request": "^10.0.6", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "before-after-hook": "^4.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q=="],
-
- "@octokit/endpoint": ["@octokit/endpoint@11.0.3", "", { "dependencies": { "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-FWFlNxghg4HrXkD3ifYbS/IdL/mDHjh9QcsNyhQjN8dplUoZbejsdpmuqdA76nxj2xoWPs7p8uX2SNr9rYu0Ag=="],
-
- "@octokit/graphql": ["@octokit/graphql@9.0.3", "", { "dependencies": { "@octokit/request": "^10.0.6", "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA=="],
-
- "@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="],
-
- "@octokit/plugin-paginate-rest": ["@octokit/plugin-paginate-rest@14.0.0", "", { "dependencies": { "@octokit/types": "^16.0.0" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw=="],
-
- "@octokit/plugin-request-log": ["@octokit/plugin-request-log@6.0.0", "", { "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-UkOzeEN3W91/eBq9sPZNQ7sUBvYCqYbrrD8gTbBuGtHEuycE4/awMXcYvx6sVYo7LypPhmQwwpUe4Yyu4QZN5Q=="],
-
- "@octokit/plugin-rest-endpoint-methods": ["@octokit/plugin-rest-endpoint-methods@17.0.0", "", { "dependencies": { "@octokit/types": "^16.0.0" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw=="],
-
- "@octokit/request": ["@octokit/request@10.0.11", "", { "dependencies": { "@octokit/endpoint": "^11.0.3", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "content-type": "^2.0.0", "json-with-bigint": "^3.5.3", "universal-user-agent": "^7.0.2" } }, "sha512-+s7HUxjfFqOMS9VlIwDffq0MikjSAK0gSpG73W+meAvVAvX4MBrHYTK5Bj3Uot55qFT4gzUtfzE4mGWY4Br8/Q=="],
-
- "@octokit/request-error": ["@octokit/request-error@7.1.0", "", { "dependencies": { "@octokit/types": "^16.0.0" } }, "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw=="],
+ "@rowanmanning/feed-parser": ["@rowanmanning/feed-parser@2.1.3", "", { "dependencies": { "fast-xml-parser": "^5.5.9", "html-entities": "^2.3.3" } }, "sha512-sfr85UZqCNGwPwJ8pBqnZq/vO4wrC/UEfNuCnJAtNSPPQCK7w1sfH6PAzwfNpaAK3erafFFm6m+6TKyCSFw6Bw=="],
- "@octokit/rest": ["@octokit/rest@22.0.1", "", { "dependencies": { "@octokit/core": "^7.0.6", "@octokit/plugin-paginate-rest": "^14.0.0", "@octokit/plugin-request-log": "^6.0.0", "@octokit/plugin-rest-endpoint-methods": "^17.0.0" } }, "sha512-Jzbhzl3CEexhnivb1iQ0KJ7s5vvjMWcmRtq5aUsKmKDrRW6z3r84ngmiFKFvpZjpiU/9/S6ITPFRpn5s/3uQJw=="],
+ "@rssbook/platform-cloudflare": ["@rssbook/platform-cloudflare@workspace:platform/cloudflare"],
- "@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="],
+ "@rssbook/platform-deno": ["@rssbook/platform-deno@workspace:platform/deno"],
- "@opentelemetry/api": ["@opentelemetry/api@1.9.1", "", {}, "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q=="],
+ "@rssbook/platform-netlify": ["@rssbook/platform-netlify@workspace:platform/netlify"],
- "@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.217.0", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-Cdq0jW2lknrNfrAm92MyEAvpe2cRsKjdnQLHUL6xRA4IVUnsWx6P65E7NcUO0Y+L4w1Aee5iV8FvjSwd+lrs9A=="],
+ "@rssbook/platform-node": ["@rssbook/platform-node@workspace:platform/node"],
- "@opentelemetry/context-async-hooks": ["@opentelemetry/context-async-hooks@2.7.1", "", { "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-OPFBYuXEn1E4ja3Y6eeA7O+ZnLBNcXTV5Cgsn1VaqBZ6hC5FnpZPLBNme1LJY8ZtF4aOujPKFoeWN4ik487KuQ=="],
+ "@rssbook/platform-vercel": ["@rssbook/platform-vercel@workspace:platform/vercel"],
- "@opentelemetry/core": ["@opentelemetry/core@2.7.1", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw=="],
+ "@sinclair/typebox": ["@sinclair/typebox@0.34.49", "", {}, "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A=="],
- "@opentelemetry/instrumentation": ["@opentelemetry/instrumentation@0.217.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.217.0", "import-in-the-middle": "^3.0.0", "require-in-the-middle": "^8.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-24ucQMjz7Y34Kw3trbxL2ZrssbtgWnR+Clpaa+YdeWuuyH3Cvk23Q03PcQvqiZrDvt8AmQmjgg9v6Y9PHoxG7w=="],
+ "@tokenizer/inflate": ["@tokenizer/inflate@0.4.1", "", { "dependencies": { "debug": "^4.4.3", "token-types": "^6.1.1" } }, "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA=="],
- "@opentelemetry/resources": ["@opentelemetry/resources@2.7.1", "", { "dependencies": { "@opentelemetry/core": "2.7.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-DeT6KKolmC4e/dRQvMQ/RwlnzhaqeiFOXY5ngoOPJ07GgVVKxZOg9EcrNZb5aTzUn+iCrJldAgOfQm1O/QfPAQ=="],
+ "@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="],
- "@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.7.1", "", { "dependencies": { "@opentelemetry/core": "2.7.1", "@opentelemetry/resources": "2.7.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-NAYIlsF8MPUsKqJMiDQJTMPOmlbawC1Iz/omMLygZ1C9am8fTKYjTaI+OZM+WTY3t3Glo0wnOg/6/pac6RGPPw=="],
+ "@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="],
- "@opentelemetry/sdk-trace-node": ["@opentelemetry/sdk-trace-node@2.7.1", "", { "dependencies": { "@opentelemetry/context-async-hooks": "2.7.1", "@opentelemetry/core": "2.7.1", "@opentelemetry/sdk-trace-base": "2.7.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-pCpQxU68lV+I9s9svqMyVu5iHdDDUnqUpSxqwyCU8A9ejEsSnMPCbearwsUO4yk08ZJzAIUCFuReMdVQvHrdvg=="],
+ "@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="],
- "@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.41.1", "", {}, "sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA=="],
+ "@types/node": ["@types/node@26.1.0", "", { "dependencies": { "undici-types": "~8.3.0" } }, "sha512-O0A1G3xPGy4w7AgQdAQYUlQ+BKk2Oovw8eRpofyp5KdBZULnbe+WqaOVNrm705SHphCiG4XHsACrSmPu1f+Kgw=="],
- "@oxc-project/types": ["@oxc-project/types@0.110.0", "", {}, "sha512-6Ct21OIlrEnFEJk5LT4e63pk3btsI6/TusD/GStLi7wYlGJNOl1GI9qvXAnRAxQU9zqA2Oz+UwhfTOU2rPZVow=="],
+ "@types/sanitize-html": ["@types/sanitize-html@2.16.1", "", { "dependencies": { "htmlparser2": "^10.1" } }, "sha512-n9wjs8bCOTyN/ynwD8s/nTcTreIHB1vf31vhLMGqUPNHaweKC4/fAl4Dj+hUlCTKYgm4P3k83fmiFfzkZ6sgMA=="],
- "@oxc-transform/binding-android-arm-eabi": ["@oxc-transform/binding-android-arm-eabi@0.111.0", "", { "os": "android", "cpu": "arm" }, "sha512-NdFLicvorfHYu0g2ftjVJaH7+Dz27AQUNJOq8t/ofRUoWmczOodgUCHx8C1M1htCN4ZmhS/FzfSy6yd/UngJGg=="],
+ "acorn": ["acorn@8.17.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg=="],
- "@oxc-transform/binding-android-arm64": ["@oxc-transform/binding-android-arm64@0.111.0", "", { "os": "android", "cpu": "arm64" }, "sha512-J2v9ajarD2FYlhHtjbgZUFsS2Kvi27pPxDWLGCy7i8tO60xBoozX9/ktSgbiE/QsxKaUhfv4zVKppKWUo71PmQ=="],
+ "ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
- "@oxc-transform/binding-darwin-arm64": ["@oxc-transform/binding-darwin-arm64@0.111.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-2UYmExxpXzmiHTldhNlosWqG9Nc4US51K0GB9RLcGlTE23WO33vVo1NVAKwxPE+KYuhffwDnRYTovTMUjzwvZA=="],
+ "ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
- "@oxc-transform/binding-darwin-x64": ["@oxc-transform/binding-darwin-x64@0.111.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-c4YRwfLV8Pj/ToiTCbndZaHxM2BD4W3bltr/fjXZcGypEK+U2RZFDL7tIZYT/tyneAC9hCORZKDaKhLLNuzPtA=="],
+ "any-promise": ["any-promise@1.3.0", "", {}, "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="],
- "@oxc-transform/binding-freebsd-x64": ["@oxc-transform/binding-freebsd-x64@0.111.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-prvf32IcEuLnLZbNVomFosBu0CaZpyj3YsZ6epbOgJy8iJjfLsXBb+PrkO/NBKzjuJoJa2+u7jFKRE0KT7gSOw=="],
+ "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="],
- "@oxc-transform/binding-linux-arm-gnueabihf": ["@oxc-transform/binding-linux-arm-gnueabihf@0.111.0", "", { "os": "linux", "cpu": "arm" }, "sha512-+se3579Wp7VOk8TnTZCpT+obTAyzOw2b/UuoM0+51LtbzCSfjKxd4A+o7zRl7GyPrPZvx57KdbMOC9rWB1xNrw=="],
+ "anynum": ["anynum@1.0.1", "", {}, "sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A=="],
- "@oxc-transform/binding-linux-arm-musleabihf": ["@oxc-transform/binding-linux-arm-musleabihf@0.111.0", "", { "os": "linux", "cpu": "arm" }, "sha512-8faC99pStqaSDPK/vBgaagAHUeL0LcIzfeSjSiDTtvPGc3AwZIeqC1tx3CP15a6tWXjdgS/IUw4IjfD5HweBlg=="],
+ "boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="],
- "@oxc-transform/binding-linux-arm64-gnu": ["@oxc-transform/binding-linux-arm64-gnu@0.111.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-HtfQv8j796gzI5WR/RaP6IMwFpiL0vYeDrUA1hYhlPzTHKYan/B+NlhJkKOI1v24yAl/yEnFmb0pxIxLNqBqBA=="],
+ "bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="],
- "@oxc-transform/binding-linux-arm64-musl": ["@oxc-transform/binding-linux-arm64-musl@0.111.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-ARyfcMCIxVLDgLf6FQ8Oo1/TFySpnquV+vuSb4SFQZfYDqgMklzwv0NYXxWD0aB6enElyMDs6pQJBzusEKCkOg=="],
+ "bundle-require": ["bundle-require@5.1.0", "", { "dependencies": { "load-tsconfig": "^0.2.3" }, "peerDependencies": { "esbuild": ">=0.18" } }, "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA=="],
- "@oxc-transform/binding-linux-ppc64-gnu": ["@oxc-transform/binding-linux-ppc64-gnu@0.111.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-PKpVRrSvBNK3tv9vwxn7Fay+QWZmprPGlEqJcseBJllQc5mFMD4Q/w44chu5iR9ZLsDeSHzmNWrgMLo4J0sP2A=="],
+ "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="],
- "@oxc-transform/binding-linux-riscv64-gnu": ["@oxc-transform/binding-linux-riscv64-gnu@0.111.0", "", { "os": "linux", "cpu": "none" }, "sha512-9bUml6rMgk+8GF5rvNMweFspkzSiCjqpV6HduwiUyexqfGKrmjq9IZOxxvnzkE2RGdQzP507NNDoVNYIoGQYuA=="],
+ "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
- "@oxc-transform/binding-linux-riscv64-musl": ["@oxc-transform/binding-linux-riscv64-musl@0.111.0", "", { "os": "linux", "cpu": "none" }, "sha512-tzGCohGxaeH6KRJjfYZd4mHCoGjCai6N+zZi1Oj+tSDMAAdyvs1dRzYb8PNUGnybCg3Te4M0jLPzWZaSmnKraQ=="],
+ "cheerio": ["cheerio@1.2.0", "", { "dependencies": { "cheerio-select": "^2.1.0", "dom-serializer": "^2.0.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", "encoding-sniffer": "^0.2.1", "htmlparser2": "^10.1.0", "parse5": "^7.3.0", "parse5-htmlparser2-tree-adapter": "^7.1.0", "parse5-parser-stream": "^7.1.2", "undici": "^7.19.0", "whatwg-mimetype": "^4.0.0" } }, "sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg=="],
- "@oxc-transform/binding-linux-s390x-gnu": ["@oxc-transform/binding-linux-s390x-gnu@0.111.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-sRG1KIfZ0ML9ToEygm5aM/5GJeBA05uHlgW3M0Rx/DNWMJhuahLmqWuB02aWSmijndLfEKXLLXIWhvWupRG8lg=="],
+ "cheerio-select": ["cheerio-select@2.1.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-select": "^5.1.0", "css-what": "^6.1.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.0.1" } }, "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g=="],
- "@oxc-transform/binding-linux-x64-gnu": ["@oxc-transform/binding-linux-x64-gnu@0.111.0", "", { "os": "linux", "cpu": "x64" }, "sha512-T0Kmvk+OdlUdABdXlDIf3MQReMzFfC75NEI9x8jxy5pKooACEFg0k0V8gyR3gq4DzbDCfucqFQDWNvSgIopAbQ=="],
+ "chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="],
- "@oxc-transform/binding-linux-x64-musl": ["@oxc-transform/binding-linux-x64-musl@0.111.0", "", { "os": "linux", "cpu": "x64" }, "sha512-EgoutsP3YfqzN8a9vpc9+XLr0bmBl0dA3uOMiP77+exATCPxJBkJErGmQkqk6RtTp5XqX6q6mB45qWQyKk6+pA=="],
+ "cliui": ["cliui@9.0.1", "", { "dependencies": { "string-width": "^7.2.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w=="],
- "@oxc-transform/binding-openharmony-arm64": ["@oxc-transform/binding-openharmony-arm64@0.111.0", "", { "os": "none", "cpu": "arm64" }, "sha512-d8J+ejc0j5WODbVwR/QxFaI65YMwvG0W53vcVCHwa6ja1QI5lpe7sislrefG2EFYgnY47voMRzlXab5d4gEcDw=="],
+ "commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="],
- "@oxc-transform/binding-wasm32-wasi": ["@oxc-transform/binding-wasm32-wasi@0.111.0", "", { "dependencies": { "@napi-rs/wasm-runtime": "^1.1.1" }, "cpu": "none" }, "sha512-HtyIZO8IwuZgXkyb56rysLz1OLbfLhEu8A3BeuyJXzUseAj96yuxgGt3cu3QYX9AXb9pfRfA3c/fvlhsDugyTQ=="],
+ "confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="],
- "@oxc-transform/binding-win32-arm64-msvc": ["@oxc-transform/binding-win32-arm64-msvc@0.111.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-YeP80Riptc0MkVVBnzbmoFuHVLUq278+MbwNo9sTLALmzTIJxJqN029xRZbG+Bun7aLsoZhmRnm3J5JZ1NcP5w=="],
+ "consola": ["consola@3.4.2", "", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="],
- "@oxc-transform/binding-win32-ia32-msvc": ["@oxc-transform/binding-win32-ia32-msvc@0.111.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-A6ztCXpoSHt6PbvGAFqB0MLOcGG7ZJrrPXY1iB0zfOB1atLgI8oNePGxPl03XSbwpiTsFJ1oo8rj9DXcBzgT9g=="],
+ "cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="],
- "@oxc-transform/binding-win32-x64-msvc": ["@oxc-transform/binding-win32-x64-msvc@0.111.0", "", { "os": "win32", "cpu": "x64" }, "sha512-QddKW4kBH0Wof6Y65eYCNHM4iOGmCTWLLcNYY1FGswhzmTYOUVXajNROR+iCXAOFnOF0ldtsR79SyqgyHH1Bgg=="],
+ "cookie-es": ["cookie-es@1.2.3", "", {}, "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw=="],
- "@parcel/watcher": ["@parcel/watcher@2.5.6", "", { "dependencies": { "detect-libc": "^2.0.3", "is-glob": "^4.0.3", "node-addon-api": "^7.0.0", "picomatch": "^4.0.3" }, "optionalDependencies": { "@parcel/watcher-android-arm64": "2.5.6", "@parcel/watcher-darwin-arm64": "2.5.6", "@parcel/watcher-darwin-x64": "2.5.6", "@parcel/watcher-freebsd-x64": "2.5.6", "@parcel/watcher-linux-arm-glibc": "2.5.6", "@parcel/watcher-linux-arm-musl": "2.5.6", "@parcel/watcher-linux-arm64-glibc": "2.5.6", "@parcel/watcher-linux-arm64-musl": "2.5.6", "@parcel/watcher-linux-x64-glibc": "2.5.6", "@parcel/watcher-linux-x64-musl": "2.5.6", "@parcel/watcher-win32-arm64": "2.5.6", "@parcel/watcher-win32-ia32": "2.5.6", "@parcel/watcher-win32-x64": "2.5.6" } }, "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ=="],
+ "crossws": ["crossws@0.4.9", "", { "peerDependencies": { "srvx": ">=0.11.5" }, "optionalPeers": ["srvx"] }, "sha512-iWx+1OMSG2aOHpjyf9AESOzkwsVdS49cXM9dVrI2PDhxU5l2RIWE/KG56gk4BbAnsMoycvniJ9OnOxO9LRzHVA=="],
- "@parcel/watcher-android-arm64": ["@parcel/watcher-android-arm64@2.5.6", "", { "os": "android", "cpu": "arm64" }, "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A=="],
+ "css-select": ["css-select@5.2.2", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw=="],
- "@parcel/watcher-darwin-arm64": ["@parcel/watcher-darwin-arm64@2.5.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA=="],
+ "css-what": ["css-what@6.2.2", "", {}, "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA=="],
- "@parcel/watcher-darwin-x64": ["@parcel/watcher-darwin-x64@2.5.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg=="],
+ "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
- "@parcel/watcher-freebsd-x64": ["@parcel/watcher-freebsd-x64@2.5.6", "", { "os": "freebsd", "cpu": "x64" }, "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng=="],
+ "dayjs": ["dayjs@1.11.21", "", {}, "sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA=="],
- "@parcel/watcher-linux-arm-glibc": ["@parcel/watcher-linux-arm-glibc@2.5.6", "", { "os": "linux", "cpu": "arm" }, "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ=="],
+ "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
- "@parcel/watcher-linux-arm-musl": ["@parcel/watcher-linux-arm-musl@2.5.6", "", { "os": "linux", "cpu": "arm" }, "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg=="],
+ "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="],
- "@parcel/watcher-linux-arm64-glibc": ["@parcel/watcher-linux-arm64-glibc@2.5.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA=="],
+ "defu": ["defu@6.1.7", "", {}, "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ=="],
- "@parcel/watcher-linux-arm64-musl": ["@parcel/watcher-linux-arm64-musl@2.5.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA=="],
+ "destr": ["destr@2.0.5", "", {}, "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA=="],
- "@parcel/watcher-linux-x64-glibc": ["@parcel/watcher-linux-x64-glibc@2.5.6", "", { "os": "linux", "cpu": "x64" }, "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ=="],
+ "dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="],
- "@parcel/watcher-linux-x64-musl": ["@parcel/watcher-linux-x64-musl@2.5.6", "", { "os": "linux", "cpu": "x64" }, "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg=="],
+ "domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="],
- "@parcel/watcher-wasm": ["@parcel/watcher-wasm@2.5.6", "", { "dependencies": { "is-glob": "^4.0.3", "napi-wasm": "^1.1.0", "picomatch": "^4.0.3" } }, "sha512-byAiBZ1t3tXQvc8dMD/eoyE7lTXYorhn+6uVW5AC+JGI1KtJC/LvDche5cfUE+qiefH+Ybq0bUCJU0aB1cSHUA=="],
+ "domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="],
- "@parcel/watcher-win32-arm64": ["@parcel/watcher-win32-arm64@2.5.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q=="],
+ "domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="],
- "@parcel/watcher-win32-ia32": ["@parcel/watcher-win32-ia32@2.5.6", "", { "os": "win32", "cpu": "ia32" }, "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g=="],
+ "elysia": ["elysia@1.4.29", "", { "dependencies": { "cookie": "^1.1.1", "exact-mirror": "^0.2.7", "fast-decode-uri-component": "^1.0.1", "memoirist": "^0.4.0" }, "peerDependencies": { "@sinclair/typebox": ">= 0.34.0 < 1", "@types/bun": ">= 1.2.0", "file-type": ">= 20.0.0", "openapi-types": ">= 12.0.0", "typescript": ">= 5.0.0" }, "optionalPeers": ["@types/bun", "typescript"] }, "sha512-GwMRGGwSdjfPt+w3LA0fqTuYJtS8uVRJicvoar98/HrO5qdFKDc9CwjIb6Kja+v39lkY+58hr2JvdR9jQzlUuA=="],
- "@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.6", "", { "os": "win32", "cpu": "x64" }, "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw=="],
+ "emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="],
- "@pinojs/redact": ["@pinojs/redact@0.4.0", "", {}, "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg=="],
+ "encoding-sniffer": ["encoding-sniffer@0.2.1", "", { "dependencies": { "iconv-lite": "^0.6.3", "whatwg-encoding": "^3.1.1" } }, "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw=="],
- "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
+ "entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="],
- "@pnpm/config.env-replace": ["@pnpm/config.env-replace@1.1.0", "", {}, "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w=="],
+ "esbuild": ["esbuild@0.28.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.1", "@esbuild/android-arm": "0.28.1", "@esbuild/android-arm64": "0.28.1", "@esbuild/android-x64": "0.28.1", "@esbuild/darwin-arm64": "0.28.1", "@esbuild/darwin-x64": "0.28.1", "@esbuild/freebsd-arm64": "0.28.1", "@esbuild/freebsd-x64": "0.28.1", "@esbuild/linux-arm": "0.28.1", "@esbuild/linux-arm64": "0.28.1", "@esbuild/linux-ia32": "0.28.1", "@esbuild/linux-loong64": "0.28.1", "@esbuild/linux-mips64el": "0.28.1", "@esbuild/linux-ppc64": "0.28.1", "@esbuild/linux-riscv64": "0.28.1", "@esbuild/linux-s390x": "0.28.1", "@esbuild/linux-x64": "0.28.1", "@esbuild/netbsd-arm64": "0.28.1", "@esbuild/netbsd-x64": "0.28.1", "@esbuild/openbsd-arm64": "0.28.1", "@esbuild/openbsd-x64": "0.28.1", "@esbuild/openharmony-arm64": "0.28.1", "@esbuild/sunos-x64": "0.28.1", "@esbuild/win32-arm64": "0.28.1", "@esbuild/win32-ia32": "0.28.1", "@esbuild/win32-x64": "0.28.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw=="],
- "@pnpm/network.ca-file": ["@pnpm/network.ca-file@1.0.2", "", { "dependencies": { "graceful-fs": "4.2.10" } }, "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA=="],
+ "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
- "@pnpm/npm-conf": ["@pnpm/npm-conf@3.0.3", "", { "dependencies": { "@pnpm/config.env-replace": "^1.1.0", "@pnpm/network.ca-file": "^1.0.1", "config-chain": "^1.1.11" } }, "sha512-//0sR/cow/s4ICQaYoAobOl4aU8cjU6x/V24V7XkKotb9+O+3zySIYp146vpaobYHnxa4pZX8NkV54Z5AwbDKA=="],
+ "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
- "@pnpm/tabtab": ["@pnpm/tabtab@0.5.4", "", { "dependencies": { "debug": "^4.3.1", "enquirer": "^2.3.6", "minimist": "^1.2.5", "untildify": "^4.0.0" } }, "sha512-bWLDlHsBlgKY/05wDN/V3ETcn5G2SV/SiA2ZmNvKGGlmVX4G5li7GRDhHcgYvHJHyJ8TUStqg2xtHmCs0UbAbg=="],
+ "exact-mirror": ["exact-mirror@0.2.7", "", { "peerDependencies": { "@sinclair/typebox": "^0.34.15" }, "optionalPeers": ["@sinclair/typebox"] }, "sha512-+MeEmDcLA4o/vjK2zujgk+1VTxPR4hdp23qLqkWfStbECtAq9gmsvQa3LW6z/0GXZyHJobrCnmy1cdeE7BjsYg=="],
- "@poppinss/colors": ["@poppinss/colors@4.1.6", "", { "dependencies": { "kleur": "^4.1.5" } }, "sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg=="],
+ "fast-decode-uri-component": ["fast-decode-uri-component@1.0.1", "", {}, "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg=="],
- "@poppinss/dumper": ["@poppinss/dumper@0.6.5", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@sindresorhus/is": "^7.0.2", "supports-color": "^10.0.0" } }, "sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw=="],
+ "fast-xml-builder": ["fast-xml-builder@1.2.1", "", { "dependencies": { "path-expression-matcher": "^1.5.0", "xml-naming": "^0.1.0" } }, "sha512-tPb5TTWfgfVx5BNSi2xV0eLr89POeXXn0dXIsCJ9m1narrWxeIyx6je9d7Rce/3NyXLbvuQmLkxq+RuxMWejvw=="],
- "@poppinss/exception": ["@poppinss/exception@1.2.3", "", {}, "sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw=="],
+ "fast-xml-parser": ["fast-xml-parser@5.9.3", "", { "dependencies": { "@nodable/entities": "^2.2.0", "fast-xml-builder": "^1.2.0", "is-unsafe": "^1.0.1", "path-expression-matcher": "^1.5.0", "strnum": "^2.4.1", "xml-naming": "^0.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-brCNCeScma/kqa54J4PIDriSSSLssRkuYaUCpvHJulGc3HGI/xxKUCTDcYkAdqJsyb//ydpbxecjC3hB9+tb/g=="],
- "@renovatebot/pep440": ["@renovatebot/pep440@4.2.1", "", {}, "sha512-2FK1hF93Fuf1laSdfiEmJvSJPVIDHEUTz68D3Fi9s0IZrrpaEcj6pTFBTbYvsgC5du4ogrtf5re7yMMvrKNgkw=="],
+ "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
- "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-rc.1", "", { "os": "android", "cpu": "arm64" }, "sha512-He6ZoCfv5D7dlRbrhNBkuMVIHd0GDnjJwbICE1OWpG7G3S2gmJ+eXkcNLJjzjNDpeI2aRy56ou39AJM9AD8YFA=="],
+ "feed": ["feed@5.2.1", "", { "dependencies": { "xml-js": "^1.6.11" } }, "sha512-jTynzYPWs9ALjro0GW8j7sv9y7cJBeOdD4Y88kVqYy/eyusIX3g+499JiTDIlD9Ge/unebx57T4Uzo6vpYvMtA=="],
- "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-rc.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-YzJdn08kSOXnj85ghHauH2iHpOJ6eSmstdRTLyaziDcUxe9SyQJgGyx/5jDIhDvtOcNvMm2Ju7m19+S/Rm1jFg=="],
+ "file-type": ["file-type@22.0.1", "", { "dependencies": { "@tokenizer/inflate": "^0.4.1", "strtok3": "^10.3.5", "token-types": "^6.1.2", "uint8array-extras": "^1.5.0" } }, "sha512-ww5Mhre0EE+jmBvOXTmXAbEMuZE7uX4a3+oRCQFNj8w++g3ev913N6tXQz0XTXbueQ5TWQfm6BdaViEHHn8bhA=="],
- "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0-rc.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-cIvAbqM+ZVV6lBSKSBtlNqH5iCiW933t1q8j0H66B3sjbe8AxIRetVqfGgcHcJtMzBIkIALlL9fcDrElWLJQcQ=="],
+ "fix-dts-default-cjs-exports": ["fix-dts-default-cjs-exports@1.0.1", "", { "dependencies": { "magic-string": "^0.30.17", "mlly": "^1.7.4", "rollup": "^4.34.8" } }, "sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg=="],
- "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0-rc.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-rVt+B1B/qmKwCl1XD02wKfgh3vQPXRXdB/TicV2w6g7RVAM1+cZcpigwhLarqiVCxDObFZ7UgXCxPC7tpDoRog=="],
+ "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
- "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.1", "", { "os": "linux", "cpu": "arm" }, "sha512-69YKwJJBOFprQa1GktPgbuBOfnn+EGxu8sBJ1TjPER+zhSpYeaU4N07uqmyBiksOLGXsMegymuecLobfz03h8Q=="],
+ "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="],
- "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0-rc.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-9JDhHUf3WcLfnViFWm+TyorqUtnSAHaCzlSNmMOq824prVuuzDOK91K0Hl8DUcEb9M5x2O+d2/jmBMsetRIn3g=="],
+ "get-east-asian-width": ["get-east-asian-width@1.6.0", "", {}, "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA=="],
- "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0-rc.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-UvApLEGholmxw/HIwmUnLq3CwdydbhaHHllvWiCTNbyGom7wTwOtz5OAQbAKZYyiEOeIXZNPkM7nA4Dtng7CLw=="],
+ "h3": ["h3@1.15.11", "", { "dependencies": { "cookie-es": "^1.2.3", "crossws": "^0.3.5", "defu": "^6.1.6", "destr": "^2.0.5", "iron-webcrypto": "^1.2.1", "node-mock-http": "^1.0.4", "radix3": "^1.1.2", "ufo": "^1.6.3", "uncrypto": "^0.1.3" } }, "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg=="],
- "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0-rc.1", "", { "os": "linux", "cpu": "x64" }, "sha512-uVctNgZHiGnJx5Fij7wHLhgw4uyZBVi6mykeWKOqE7bVy9Hcxn0fM/IuqdMwk6hXlaf9fFShDTFz2+YejP+x0A=="],
+ "html-entities": ["html-entities@2.6.0", "", {}, "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ=="],
- "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0-rc.1", "", { "os": "linux", "cpu": "x64" }, "sha512-T6Eg0xWwcxd/MzBcuv4Z37YVbUbJxy5cMNnbIt/Yr99wFwli30O4BPlY8hKeGyn6lWNtU0QioBS46lVzDN38bg=="],
+ "htmlparser2": ["htmlparser2@10.1.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", "entities": "^7.0.1" } }, "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ=="],
- "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.0-rc.1", "", { "os": "none", "cpu": "arm64" }, "sha512-PuGZVS2xNJyLADeh2F04b+Cz4NwvpglbtWACgrDOa5YDTEHKwmiTDjoD5eZ9/ptXtcpeFrMqD2H4Zn33KAh1Eg=="],
+ "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
- "@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0-rc.1", "", { "dependencies": { "@napi-rs/wasm-runtime": "^1.1.1" }, "cpu": "none" }, "sha512-2mOxY562ihHlz9lEXuaGEIDCZ1vI+zyFdtsoa3M62xsEunDXQE+DVPO4S4x5MPK9tKulG/aFcA/IH5eVN257Cw=="],
+ "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
- "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0-rc.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-oQVOP5cfAWZwRD0Q3nGn/cA9FW3KhMMuQ0NIndALAe6obqjLhqYVYDiGGRGrxvnjJsVbpLwR14gIUYnpIcHR1g=="],
+ "iron-webcrypto": ["iron-webcrypto@1.2.1", "", {}, "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg=="],
- "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0-rc.1", "", { "os": "win32", "cpu": "x64" }, "sha512-Ydsxxx++FNOuov3wCBPaYjZrEvKOOGq3k+BF4BPridhg2pENfitSRD2TEuQ8i33bp5VptuNdC9IzxRKU031z5A=="],
+ "is-plain-object": ["is-plain-object@5.0.0", "", {}, "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="],
- "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.1", "", {}, "sha512-UTBjtTxVOhodhzFVp/ayITaTETRHPUPYZPXQe0WU0wOgxghMojXxYjOiPOauKIYNWJAWS2fd7gJgGQK8GU8vDA=="],
+ "is-unsafe": ["is-unsafe@1.0.1", "", {}, "sha512-CLK2+VdgERgD96EYm5lUQssZYlRg2tkZnbsxZoacmSiRxiFJ4Nk4SzjCl+Ur+v3kXIY9dTIdb3IH22y1mZ56LA=="],
- "@rollup/pluginutils": ["@rollup/pluginutils@5.4.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg=="],
+ "joycon": ["joycon@3.1.1", "", {}, "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw=="],
- "@rowanmanning/feed-parser": ["@rowanmanning/feed-parser@2.1.3", "", { "dependencies": { "fast-xml-parser": "^5.5.9", "html-entities": "^2.3.3" } }, "sha512-sfr85UZqCNGwPwJ8pBqnZq/vO4wrC/UEfNuCnJAtNSPPQCK7w1sfH6PAzwfNpaAK3erafFFm6m+6TKyCSFw6Bw=="],
+ "launder": ["launder@1.7.1", "", { "dependencies": { "dayjs": "^1.11.7" } }, "sha512-mU6WRz5EusL9ZZuiZ5SO4Y6C0P9PAUR9iwdb6bzj4KDihm28DiHFw+/yk9DBH4f+Pv1wuzQ4e2jV3oQ7mkIqvw=="],
- "@rssbook/platform-cloudflare": ["@rssbook/platform-cloudflare@workspace:platform/cloudflare"],
+ "lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="],
- "@rssbook/platform-deno": ["@rssbook/platform-deno@workspace:platform/deno"],
+ "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="],
- "@rssbook/platform-netlify": ["@rssbook/platform-netlify@workspace:platform/netlify"],
+ "load-tsconfig": ["load-tsconfig@0.2.5", "", {}, "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg=="],
- "@rssbook/platform-node": ["@rssbook/platform-node@workspace:platform/node"],
+ "lru-cache": ["lru-cache@11.5.1", "", {}, "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A=="],
- "@rssbook/platform-vercel": ["@rssbook/platform-vercel@workspace:platform/vercel"],
+ "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
- "@sec-ant/readable-stream": ["@sec-ant/readable-stream@0.4.1", "", {}, "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg=="],
+ "memoirist": ["memoirist@0.4.0", "", {}, "sha512-zxTgA0mSYELa66DimuNQDvyLq36AwDlTuVRbnQtB+VuTcKWm5Qc4z3WkSpgsFWHNhexqkIooqpv4hdcqrX5Nmg=="],
- "@sinclair/typebox": ["@sinclair/typebox@0.34.49", "", {}, "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A=="],
+ "mlly": ["mlly@1.8.2", "", { "dependencies": { "acorn": "^8.16.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.3" } }, "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA=="],
- "@sindresorhus/is": ["@sindresorhus/is@7.2.0", "", {}, "sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw=="],
+ "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
- "@sindresorhus/merge-streams": ["@sindresorhus/merge-streams@2.3.0", "", {}, "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg=="],
+ "mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="],
- "@sindresorhus/slugify": ["@sindresorhus/slugify@2.2.1", "", { "dependencies": { "@sindresorhus/transliterate": "^1.0.0", "escape-string-regexp": "^5.0.0" } }, "sha512-MkngSCRZ8JdSOCHRaYd+D01XhvU3Hjy6MGl06zhOk614hp9EOAp5gIkBeQg7wtmxpitU6eAL4kdiRMcJa2dlrw=="],
+ "nanoid": ["nanoid@3.3.15", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA=="],
- "@sindresorhus/transliterate": ["@sindresorhus/transliterate@1.6.0", "", { "dependencies": { "escape-string-regexp": "^5.0.0" } }, "sha512-doH1gimEu3A46VX6aVxpHTeHrytJAG6HgdxntYnCFiIFHEM/ZGpG8KiZGBChchjQmG0XFIBL552kBTjVcMZXwQ=="],
+ "node-fetch-native": ["node-fetch-native@1.6.7", "", {}, "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q=="],
- "@so-ric/colorspace": ["@so-ric/colorspace@1.1.6", "", { "dependencies": { "color": "^5.0.2", "text-hex": "1.0.x" } }, "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw=="],
+ "node-mock-http": ["node-mock-http@1.0.4", "", {}, "sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ=="],
- "@speed-highlight/core": ["@speed-highlight/core@1.2.17", "", {}, "sha512-Z92FwKpCtfaW1V0jTU/fh3QzYEZN8wDwrzRIBoADCJfn4mJCNcJN/XegifX7BDrQ8/h9Xh/JnbyMchL0FqXrkg=="],
+ "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
- "@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.10", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-4WfKk68eTih+MiJD4fSbxN7E8kVBmTMPWHUPYjvl2N0rMs53YLTT8/YjKU5Dtnz5LqDjl7LEw4U7lXR2W3J5WA=="],
+ "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="],
- "@tokenizer/inflate": ["@tokenizer/inflate@0.4.1", "", { "dependencies": { "debug": "^4.4.3", "token-types": "^6.1.1" } }, "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA=="],
+ "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
- "@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="],
+ "ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="],
- "@tootallnate/once": ["@tootallnate/once@2.0.0", "", {}, "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A=="],
+ "openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="],
- "@tootallnate/quickjs-emscripten": ["@tootallnate/quickjs-emscripten@0.23.0", "", {}, "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA=="],
+ "parse-srcset": ["parse-srcset@1.0.2", "", {}, "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q=="],
- "@ts-morph/common": ["@ts-morph/common@0.11.1", "", { "dependencies": { "fast-glob": "^3.2.7", "minimatch": "^3.0.4", "mkdirp": "^1.0.4", "path-browserify": "^1.0.1" } }, "sha512-7hWZS0NRpEsNV8vWJzg7FEz6V8MaLNeJOmwmghqUXTpzk16V1LLZhdo+4QvE/+zv4cVci0OviuJFnqhEfoV3+g=="],
+ "parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="],
- "@tsconfig/node10": ["@tsconfig/node10@1.0.12", "", {}, "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ=="],
+ "parse5-htmlparser2-tree-adapter": ["parse5-htmlparser2-tree-adapter@7.1.0", "", { "dependencies": { "domhandler": "^5.0.3", "parse5": "^7.0.0" } }, "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g=="],
- "@tsconfig/node12": ["@tsconfig/node12@1.0.11", "", {}, "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag=="],
+ "parse5-parser-stream": ["parse5-parser-stream@7.1.2", "", { "dependencies": { "parse5": "^7.0.0" } }, "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow=="],
- "@tsconfig/node14": ["@tsconfig/node14@1.0.3", "", {}, "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow=="],
+ "path-expression-matcher": ["path-expression-matcher@1.6.1", "", {}, "sha512-h7bxdzhHk8Knyc4Tj+jMaa7fEEoUJy7p1qtbVgkYg1Uhpe5Np5VuGXCRZnkZvU+Q42M1vStt0ifa3ueykRJPmQ=="],
- "@tsconfig/node16": ["@tsconfig/node16@1.0.4", "", {}, "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA=="],
+ "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
- "@tybys/wasm-util": ["@tybys/wasm-util@0.10.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg=="],
+ "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
- "@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="],
+ "picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
- "@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="],
+ "pirates": ["pirates@4.0.7", "", {}, "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA=="],
- "@types/http-proxy": ["@types/http-proxy@1.17.17", "", { "dependencies": { "@types/node": "*" } }, "sha512-ED6LB+Z1AVylNTu7hdzuBqOgMnvG/ld6wGCG8wFnAzKX5uyW2K3WD52v0gnLCTK/VLpXtKckgWuyScYK6cSPaw=="],
+ "pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="],
- "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
+ "postcss": ["postcss@8.5.16", "", { "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg=="],
- "@types/node": ["@types/node@25.6.0", "", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ=="],
+ "postcss-load-config": ["postcss-load-config@6.0.1", "", { "dependencies": { "lilconfig": "^3.1.1" }, "peerDependencies": { "jiti": ">=1.21.0", "postcss": ">=8.0.9", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["jiti", "postcss", "tsx", "yaml"] }, "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g=="],
- "@types/normalize-package-data": ["@types/normalize-package-data@2.4.4", "", {}, "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA=="],
+ "radix3": ["radix3@1.1.2", "", {}, "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA=="],
- "@types/retry": ["@types/retry@0.12.2", "", {}, "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow=="],
+ "readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="],
- "@types/sanitize-html": ["@types/sanitize-html@2.16.1", "", { "dependencies": { "htmlparser2": "^10.1" } }, "sha512-n9wjs8bCOTyN/ynwD8s/nTcTreIHB1vf31vhLMGqUPNHaweKC4/fAl4Dj+hUlCTKYgm4P3k83fmiFfzkZ6sgMA=="],
+ "resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="],
- "@types/triple-beam": ["@types/triple-beam@1.3.5", "", {}, "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw=="],
+ "rollup": ["rollup@4.62.2", "", { "dependencies": { "@types/estree": "1.0.9" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.62.2", "@rollup/rollup-android-arm64": "4.62.2", "@rollup/rollup-darwin-arm64": "4.62.2", "@rollup/rollup-darwin-x64": "4.62.2", "@rollup/rollup-freebsd-arm64": "4.62.2", "@rollup/rollup-freebsd-x64": "4.62.2", "@rollup/rollup-linux-arm-gnueabihf": "4.62.2", "@rollup/rollup-linux-arm-musleabihf": "4.62.2", "@rollup/rollup-linux-arm64-gnu": "4.62.2", "@rollup/rollup-linux-arm64-musl": "4.62.2", "@rollup/rollup-linux-loong64-gnu": "4.62.2", "@rollup/rollup-linux-loong64-musl": "4.62.2", "@rollup/rollup-linux-ppc64-gnu": "4.62.2", "@rollup/rollup-linux-ppc64-musl": "4.62.2", "@rollup/rollup-linux-riscv64-gnu": "4.62.2", "@rollup/rollup-linux-riscv64-musl": "4.62.2", "@rollup/rollup-linux-s390x-gnu": "4.62.2", "@rollup/rollup-linux-x64-gnu": "4.62.2", "@rollup/rollup-linux-x64-musl": "4.62.2", "@rollup/rollup-openbsd-x64": "4.62.2", "@rollup/rollup-openharmony-arm64": "4.62.2", "@rollup/rollup-win32-arm64-msvc": "4.62.2", "@rollup/rollup-win32-ia32-msvc": "4.62.2", "@rollup/rollup-win32-x64-gnu": "4.62.2", "@rollup/rollup-win32-x64-msvc": "4.62.2", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA=="],
- "@types/yauzl": ["@types/yauzl@2.10.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q=="],
+ "rssbook": ["rssbook@workspace:pkgs/rssbook"],
- "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.62.1", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.62.1", "@typescript-eslint/types": "^8.62.1", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-yQ3RgY5RkSBpsNS1Bx/JQEcA24FOSdfGktoyprAr5u18390UQdtVcfnEv4nIrIshNnavlVyZBKxQwT1fIAE6cg=="],
+ "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
- "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.62.1", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-xadytJqX9vJVQ2fdQjkcIVigwaOJNWkpjdLt6cEQ+xPnrI1fkp+/jZE/I97k9KUjqtpd25i0HeyZf3T6dutv2g=="],
+ "sanitize-html": ["sanitize-html@2.17.5", "", { "dependencies": { "deepmerge": "^4.2.2", "escape-string-regexp": "^4.0.0", "htmlparser2": "^10.1.0", "is-plain-object": "^5.0.0", "launder": "^1.7.1", "parse-srcset": "^1.0.2", "postcss": "^8.3.11" } }, "sha512-ZmU1joGRrvoyctKIiuwUxqR6moLoU2Wk+2bMccN6f7UwhAmwYDvWziqPxRDDN2Qip62NqnIrVrT9akbL6Wretg=="],
- "@typescript-eslint/types": ["@typescript-eslint/types@8.62.1", "", {}, "sha512-ooCzJFaf+Hg+uG6fA3NRFGuFjlfNlDhBthbv4ZPU/0elCAFUfnyXUvf/WOpHz/jYwSmvU2GkR2LtyUfy1AxZ1Q=="],
+ "sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="],
- "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.62.1", "", { "dependencies": { "@typescript-eslint/project-service": "8.62.1", "@typescript-eslint/tsconfig-utils": "8.62.1", "@typescript-eslint/types": "8.62.1", "@typescript-eslint/visitor-keys": "8.62.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-xMcW9oP9u7fAMXYs9A65CVmtLQe2r//oXINHfi8HV+oiqhih17sbLdhXr4540YWlgpDKQdY854OL5ZrdCiQsAA=="],
+ "source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="],
- "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.62.1", "", { "dependencies": { "@typescript-eslint/types": "8.62.1", "eslint-visitor-keys": "^5.0.0" } }, "sha512-4g3BLxfdTMy8iZG0MaBkadnlRrCJ74cQiFbyEVMrkwIoqdyaXXQM22cotDvrl4x28wgIZ9rEJRoM+mmhSJpJ1g=="],
+ "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
- "@vercel/backends": ["@vercel/backends@0.8.21", "", { "dependencies": { "@vercel/build-utils": "13.32.2", "@vercel/nft": "1.10.0", "@vercel/static-config": "3.4.0", "execa": "3.2.0", "fs-extra": "11.1.0", "get-port": "5.1.1", "oxc-transform": "0.111.0", "path-to-regexp": "8.3.0", "resolve.exports": "2.0.3", "rolldown": "1.0.0-rc.1", "srvx": "0.11.16", "ts-morph": "12.0.0", "tsx": "4.21.0", "zod": "3.22.4" } }, "sha512-c0mKXgfxMyb44+kMGbNoQJomf37QAfd6yL/2imhAnAChR1Ddkxk6uwbrOC89LcG+QmN2ZoEFUSJdMInSfreztw=="],
+ "srvx": ["srvx@0.11.21", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-GWTHjKMeekX8CwJf4VU9Oo6mJpSGaflGMddbCvR+Cmmh9sslRMiGbAoqqZacE0r1ncARh6buCEETr2W52F8b1w=="],
- "@vercel/blob": ["@vercel/blob@2.4.0", "", { "dependencies": { "async-retry": "^1.3.3", "is-buffer": "^2.0.5", "is-node-process": "^1.2.0", "throttleit": "^2.1.0", "undici": "^6.23.0" } }, "sha512-ncQ8CRb6XoEAYJwjOTRGpACRT6h/AeY+/33gLyeVxG5BIes27OPm1jmqreF+JHjcTmGhClTP+kBpmyLfbV0xew=="],
+ "string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
- "@vercel/build-utils": ["@vercel/build-utils@13.32.2", "", { "dependencies": { "@vercel/python-analysis": "0.11.1", "cjs-module-lexer": "1.2.3", "es-module-lexer": "1.5.0" } }, "sha512-XgATgMjt2NHF6HHo1wii6f43qlWjaFx3o+9L7aOUPLQgqwgYp2BGwuuBjg8U6sNgut1QsmFBMvNqTAUBvack9Q=="],
+ "strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="],
- "@vercel/cervel": ["@vercel/cervel@0.1.29", "", { "dependencies": { "@vercel/backends": "0.8.21" }, "bin": { "cervel": "bin/cervel.mjs" } }, "sha512-tjwW6bj1g9qwOO0y/3WTGiarvyWmsWOtLkPB0esFmnscMZCxRqzNh0WJqrbbWxMdC5A2LXzDDHuZ4sXpRg4Tqw=="],
+ "strnum": ["strnum@2.4.1", "", { "dependencies": { "anynum": "^1.0.1" } }, "sha512-M9eUSMT2dCB2cTNPG7UYj6KuK7RJR2SN2+yCV/fTW3xzTCS6EaGZ5pSMgDIjB7r8zSfTGk+dvvn9rTjpVS9Mwg=="],
- "@vercel/cli-auth": ["@vercel/cli-auth@0.3.0", "", { "dependencies": { "@napi-rs/keyring": "1.2.0", "@vercel/cli-config": "0.2.0", "async-listen": "3.0.0", "open": "8.4.0", "zod": "4.1.11" } }, "sha512-9nsdxUpV/L+9CBVGeRw/Qby2azhi2lk01jp0aTH+Hxx2U61+mAmbi5qChHnrbEmQdx2Ih7dp4LxO3nj1Q7f/5Q=="],
+ "strtok3": ["strtok3@10.3.5", "", { "dependencies": { "@tokenizer/token": "^0.3.0" } }, "sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA=="],
- "@vercel/cli-config": ["@vercel/cli-config@0.2.0", "", { "dependencies": { "xdg-app-paths": "5", "zod": "4.1.11" } }, "sha512-fJRRRB7734BDuXZ89yBEaA2ncYhH7bWX30mk04W80J6VAfQc+4iB8lyzAdaGpFV3/vNlkt9VZt+/uoQoWX6UsQ=="],
+ "sucrase": ["sucrase@3.35.1", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "tinyglobby": "^0.2.11", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw=="],
- "@vercel/container": ["@vercel/container@0.0.4", "", {}, "sha512-lbUnpg2Iawoi0oDlFE6Se43FNUd2B2nMnCXD9Af7NsysMENFaNu2ajzgsocTxt+O6djUgW9IdBnIRHgnHs+vZQ=="],
+ "thenify": ["thenify@3.3.1", "", { "dependencies": { "any-promise": "^1.0.0" } }, "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw=="],
- "@vercel/detect-agent": ["@vercel/detect-agent@1.2.3", "", {}, "sha512-VYNCgUc0nOmC4WJmWw9GkrKdfr8Zl4/rxhC5SvgacBgxiW9W/9NRttUoHHXV8xdII3MaRgkZZVX8Ikzc/Jmjag=="],
+ "thenify-all": ["thenify-all@1.6.0", "", { "dependencies": { "thenify": ">= 3.1.0 < 4" } }, "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA=="],
- "@vercel/elysia": ["@vercel/elysia@0.1.98", "", { "dependencies": { "@vercel/node": "5.8.22", "@vercel/static-config": "3.4.0" } }, "sha512-VJMPfvslPFP5lg7oNN+BZEXHdaejmI25OEyco5OvZWpR80MYpTPil+2+hbkoR6lN3kVUC9/xahtgsdfouOvNbA=="],
+ "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="],
- "@vercel/error-utils": ["@vercel/error-utils@2.2.0", "", {}, "sha512-WFWiRxfPzoYWYifaj4thSKvAaZZwUOqD4k5GINRIgZgCiS2E3iAJbWbIsIZmkQdTecWFHcWGA6q48CjisgpOBA=="],
+ "tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="],
- "@vercel/express": ["@vercel/express@0.1.112", "", { "dependencies": { "@vercel/cervel": "0.1.29", "@vercel/nft": "1.10.0", "@vercel/node": "5.8.22", "@vercel/static-config": "3.4.0", "fs-extra": "11.1.0", "path-to-regexp": "8.3.0", "ts-morph": "12.0.0", "zod": "3.22.4" } }, "sha512-Xpw1/+G/NpWYzBh3HqT+npYKo+yrLhSKC+hekBYCoddfDjSTevF1hQSbdEo7oHbZbgQo7h5Rb2vWVQ404zSmkA=="],
+ "token-types": ["token-types@6.1.2", "", { "dependencies": { "@borewit/text-codec": "^0.2.1", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww=="],
- "@vercel/fastify": ["@vercel/fastify@0.1.101", "", { "dependencies": { "@vercel/node": "5.8.22", "@vercel/static-config": "3.4.0" } }, "sha512-+oUCBpSs8ANQmdQB6CTCWTfdxKutCzKEMkMtUJVLVnO0eaiONADkZeaFjjZwMl4Cg6z49ifhJ3QVJD1IWwIwsA=="],
+ "tree-kill": ["tree-kill@1.2.2", "", { "bin": { "tree-kill": "cli.js" } }, "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="],
- "@vercel/fun": ["@vercel/fun@1.3.0", "", { "dependencies": { "@tootallnate/once": "2.0.0", "async-listen": "1.2.0", "debug": "4.3.4", "generic-pool": "3.4.2", "micro": "9.3.5-canary.3", "ms": "2.1.1", "node-fetch": "2.6.7", "path-to-regexp": "8.2.0", "promisepipe": "3.0.0", "semver": "7.5.4", "stat-mode": "0.3.0", "stream-to-promise": "2.2.0", "tar": "7.5.7", "tinyexec": "0.3.2", "tree-kill": "1.2.2", "uid-promise": "1.0.0", "xdg-app-paths": "5.1.0", "yauzl-promise": "2.1.3" } }, "sha512-8erw9uPe0dFg45THkNxmjtvMX143SkZebmjgSVbcM3XCkXu3RIiBaJMcMNG8aaS+rnTuw8+d4De9HVT0M/r3wg=="],
+ "ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="],
- "@vercel/gatsby-plugin-vercel-analytics": ["@vercel/gatsby-plugin-vercel-analytics@1.0.11", "", { "dependencies": { "web-vitals": "0.2.4" } }, "sha512-iTEA0vY6RBPuEzkwUTVzSHDATo1aF6bdLLspI68mQ/BTbi5UQEGjpjyzdKOVcSYApDtFU6M6vypZ1t4vIEnHvw=="],
+ "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
- "@vercel/gatsby-plugin-vercel-builder": ["@vercel/gatsby-plugin-vercel-builder@2.2.24", "", { "dependencies": { "@sinclair/typebox": "0.25.24", "@vercel/build-utils": "13.32.2", "esbuild": "0.27.0", "etag": "1.8.1", "fs-extra": "11.1.0" } }, "sha512-+rqPToquzHOnSsist6FylJY++Z7ScRvl87gJshGhYYXJSCMki5zquX+D8wliOb5H0SW3KGwmTNQ10gUJt52c8w=="],
+ "tsup": ["tsup@8.5.1", "", { "dependencies": { "bundle-require": "^5.1.0", "cac": "^6.7.14", "chokidar": "^4.0.3", "consola": "^3.4.0", "debug": "^4.4.0", "esbuild": "^0.27.0", "fix-dts-default-cjs-exports": "^1.0.0", "joycon": "^3.1.1", "picocolors": "^1.1.1", "postcss-load-config": "^6.0.1", "resolve-from": "^5.0.0", "rollup": "^4.34.8", "source-map": "^0.7.6", "sucrase": "^3.35.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.11", "tree-kill": "^1.2.2" }, "peerDependencies": { "@microsoft/api-extractor": "^7.36.0", "@swc/core": "^1", "postcss": "^8.4.12", "typescript": ">=4.5.0" }, "optionalPeers": ["@microsoft/api-extractor", "@swc/core", "postcss", "typescript"], "bin": { "tsup": "dist/cli-default.js", "tsup-node": "dist/cli-node.js" } }, "sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing=="],
- "@vercel/go": ["@vercel/go@3.10.2", "", {}, "sha512-ZJqx1GzD1qqMkI92jEHp04zCwbZ7tNwcgI58e7t8HiYvILwCZ4Rvq0hfjTjsk+0Zn8A369qs4eAHvCYu38uB5A=="],
+ "tsx": ["tsx@4.23.0", "", { "dependencies": { "esbuild": "~0.28.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-eUdUIaCr963q2h5u3+QwvYp0+eqPvn+egeqZUm0hwERCqqx1E3kK5ehbGCvqSE5MQAULr67ww0cA3jKc3YkM1w=="],
- "@vercel/h3": ["@vercel/h3@0.1.107", "", { "dependencies": { "@vercel/node": "5.8.22", "@vercel/static-config": "3.4.0" } }, "sha512-Vqo5AvE5ku3MIWAi56KlYbLIg/PfX7Xxp94xbNa7J7dbREbO8PZWD0QLKvOw1uGqyoa5YqNZZ3RdtCfDq9Z6oA=="],
+ "typescript": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="],
- "@vercel/hono": ["@vercel/hono@0.2.101", "", { "dependencies": { "@vercel/nft": "1.10.0", "@vercel/node": "5.8.22", "@vercel/static-config": "3.4.0", "fs-extra": "11.1.0", "path-to-regexp": "8.3.0", "ts-morph": "12.0.0", "zod": "3.22.4" } }, "sha512-sm24D5OUJEo9cuvPHeVgouuc0zhZdIhX28BdtvIek5b7FGmvOE8VExOhteWU5YufhJR0M1y1zbbVC2ULr/arHw=="],
+ "ufo": ["ufo@1.6.4", "", {}, "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA=="],
- "@vercel/hydrogen": ["@vercel/hydrogen@1.4.0", "", { "dependencies": { "@vercel/static-config": "3.4.0", "ts-morph": "12.0.0" } }, "sha512-gf3ELmAjcia7WNGNHAB/rFWFU0l5fJ7mTMgGC5hvcCjSIE4kp8WWuGYiTQgQ8W6GF/1FJAxa2J3BhY/yxjY8tA=="],
+ "uint8array-extras": ["uint8array-extras@1.5.0", "", {}, "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A=="],
- "@vercel/koa": ["@vercel/koa@0.1.81", "", { "dependencies": { "@vercel/node": "5.8.22", "@vercel/static-config": "3.4.0" } }, "sha512-sAavmnlTM9LhYPWAgIvWuKgpeozk/tB4ZPLe/Fl7qE6EJiN3sCjtaSatXhla5w1Zxy0veBI5u0LCcjref4Oxag=="],
+ "uncrypto": ["uncrypto@0.1.3", "", {}, "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q=="],
- "@vercel/nestjs": ["@vercel/nestjs@0.2.102", "", { "dependencies": { "@vercel/node": "5.8.22", "@vercel/static-config": "3.4.0" } }, "sha512-CMDoq4dFBI1rLmAuya7UPXfcg7h0y1xdNVCeFhzlZOfp7EF1+PFkn3dVF8tIX46yiUFCz94weiLHSAoh+mRD4g=="],
+ "undici": ["undici@7.28.0", "", {}, "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA=="],
- "@vercel/next": ["@vercel/next@4.20.2", "", { "dependencies": { "@vercel/nft": "1.10.0" } }, "sha512-mroXgsjkmI6i5m1dMRmFNHO8wExhhgOzFRHT0pg195uOVyuNU2JxBCtU47FowbcnLbrAmIfStWkPSHLwPRh6FA=="],
+ "undici-types": ["undici-types@8.3.0", "", {}, "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ=="],
- "@vercel/nft": ["@vercel/nft@0.29.4", "", { "dependencies": { "@mapbox/node-pre-gyp": "^2.0.0", "@rollup/pluginutils": "^5.1.3", "acorn": "^8.6.0", "acorn-import-attributes": "^1.9.5", "async-sema": "^3.1.1", "bindings": "^1.4.0", "estree-walker": "2.0.2", "glob": "^10.4.5", "graceful-fs": "^4.2.9", "node-gyp-build": "^4.2.2", "picomatch": "^4.0.2", "resolve-from": "^5.0.0" }, "bin": { "nft": "out/cli.js" } }, "sha512-6lLqMNX3TuycBPABycx7A9F1bHQR7kiQln6abjFbPrf5C/05qHM9M5E4PeTE59c7z8g6vHnx1Ioihb2AQl7BTA=="],
+ "unstorage": ["unstorage@1.17.5", "", { "dependencies": { "anymatch": "^3.1.3", "chokidar": "^5.0.0", "destr": "^2.0.5", "h3": "^1.15.10", "lru-cache": "^11.2.7", "node-fetch-native": "^1.6.7", "ofetch": "^1.5.1", "ufo": "^1.6.3" }, "peerDependencies": { "@azure/app-configuration": "^1.8.0", "@azure/cosmos": "^4.2.0", "@azure/data-tables": "^13.3.0", "@azure/identity": "^4.6.0", "@azure/keyvault-secrets": "^4.9.0", "@azure/storage-blob": "^12.26.0", "@capacitor/preferences": "^6 || ^7 || ^8", "@deno/kv": ">=0.9.0", "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.34.3", "@vercel/blob": ">=0.27.1", "@vercel/functions": "^2.2.12 || ^3.0.0", "@vercel/kv": "^1 || ^2 || ^3", "aws4fetch": "^1.0.20", "db0": ">=0.2.1", "idb-keyval": "^6.2.1", "ioredis": "^5.4.2", "uploadthing": "^7.4.4" }, "optionalPeers": ["@azure/app-configuration", "@azure/cosmos", "@azure/data-tables", "@azure/identity", "@azure/keyvault-secrets", "@azure/storage-blob", "@capacitor/preferences", "@deno/kv", "@netlify/blobs", "@planetscale/database", "@upstash/redis", "@vercel/blob", "@vercel/functions", "@vercel/kv", "aws4fetch", "db0", "idb-keyval", "ioredis", "uploadthing"] }, "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg=="],
- "@vercel/node": ["@vercel/node@5.8.22", "", { "dependencies": { "@edge-runtime/node-utils": "2.3.0", "@edge-runtime/primitives": "4.1.0", "@edge-runtime/vm": "3.2.0", "@types/node": "20.11.0", "@vercel/build-utils": "13.32.2", "@vercel/error-utils": "2.2.0", "@vercel/nft": "1.10.0", "@vercel/static-config": "3.4.0", "async-listen": "3.0.0", "cjs-module-lexer": "1.2.3", "edge-runtime": "2.5.9", "es-module-lexer": "1.4.1", "esbuild": "0.27.0", "etag": "1.8.1", "mime-types": "2.1.35", "node-fetch": "2.6.9", "path-to-regexp": "6.1.0", "path-to-regexp-updated": "npm:path-to-regexp@6.3.0", "ts-morph": "12.0.0", "tsx": "4.21.0", "typescript": "npm:typescript@5.9.3", "undici": "5.28.4" } }, "sha512-WfciIhDVh9RwFmvrWp7FAdYCBPV94bZvIo4JbaHqZbvBJ2Bmnv/Pgj1fTjjLKfnKTbLJy+8HN1FXB8KYDnwGZQ=="],
+ "utils-feed-helper": ["utils-feed-helper@workspace:pkgs/utils-feed-helper"],
- "@vercel/oidc": ["@vercel/oidc@3.2.0", "", {}, "sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug=="],
+ "uuid": ["uuid@13.0.2", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-vzi9uRZ926x4XV73S/4qQaTwPXM2JBj6/6lI/byHH1jOpCzb0zDbfytgA9LcN/hzb2l7WQSQnxITOVx5un/wGw=="],
- "@vercel/prepare-flags-definitions": ["@vercel/prepare-flags-definitions@0.3.0", "", {}, "sha512-/0nuDFwYje0nqZnVKSd2VfJy2wOPQwbkas1qO1JQgtb0sLl+EeSCW4O9hrvq55pN50PNlAZ/APSeWHIAT9ZGHg=="],
+ "whatwg-encoding": ["whatwg-encoding@3.1.1", "", { "dependencies": { "iconv-lite": "0.6.3" } }, "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ=="],
- "@vercel/python": ["@vercel/python@6.47.3", "", { "dependencies": { "@vercel/python-analysis": "0.11.1" } }, "sha512-h8j5Fln+GYPkWCIXZdhUq1ibFQofjeiV6LMgeY31NEFn/lolz/UxDwqSZswM9H1aP/5lH54VCe/iFE4dMlSWiQ=="],
+ "whatwg-mimetype": ["whatwg-mimetype@4.0.0", "", {}, "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="],
- "@vercel/python-analysis": ["@vercel/python-analysis@0.11.1", "", { "dependencies": { "@bytecodealliance/preview2-shim": "0.17.6", "@renovatebot/pep440": "4.2.1", "fs-extra": "11.1.1", "js-yaml": "4.1.1", "minimatch": "10.1.1", "smol-toml": "1.5.2", "zod": "3.22.4" } }, "sha512-EPPLuXJQhIDUx08H9nG76AR2HSgBquwe3OAX5s2w20M923iaWeGGVkhX/4yZ89CJfXEZgE1Aj/mX7lVHOVIcYA=="],
+ "wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="],
- "@vercel/redwood": ["@vercel/redwood@2.5.0", "", { "dependencies": { "@vercel/nft": "1.10.0", "@vercel/static-config": "3.4.0", "semver": "6.3.1", "ts-morph": "12.0.0" } }, "sha512-LO24CbieV57rGAqrq5n5z/B+fMYeUqbE/Ge3qMeKRuS3Q9awgcBgB6WYLCe8crHM7XJd5AZjbuyoS/6k3r0X0w=="],
+ "xml-js": ["xml-js@1.6.11", "", { "dependencies": { "sax": "^1.2.4" }, "bin": { "xml-js": "./bin/cli.js" } }, "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g=="],
- "@vercel/remix-builder": ["@vercel/remix-builder@5.9.1", "", { "dependencies": { "@vercel/error-utils": "2.2.0", "@vercel/nft": "1.10.0", "@vercel/static-config": "3.4.0", "path-to-regexp": "6.1.0", "path-to-regexp-updated": "npm:path-to-regexp@6.3.0", "ts-morph": "12.0.0" } }, "sha512-s0SpfV640nmQ1BsKCPMYugGrH/V+319onTo2ZILSmsy6NTBlmeN0zJU9nPcP8dBEotGqtp68NfOlOUUdrVBzkw=="],
+ "xml-naming": ["xml-naming@0.1.0", "", {}, "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw=="],
- "@vercel/ruby": ["@vercel/ruby@2.5.1", "", {}, "sha512-e23M7pfORQyyxSUmeQ8PqEnyLJ4swhtNrnyctU5E02UCaUYFm5YAdNfdPYRP4It874H4Wyvm+LV8u7CPBrfcPA=="],
+ "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="],
- "@vercel/rust": ["@vercel/rust@1.4.0", "", { "dependencies": { "execa": "5", "get-port": "5.1.1", "smol-toml": "1.5.2" } }, "sha512-/zRJtaf3pvdxZ7+tShmIo8StdbkCSBDcY7Gy7LOwNTIxm6GU9vkS+BlCwJHkOu6qNkbcSRH6ZQIeo4vNmqzKdQ=="],
+ "yargs": ["yargs@18.0.0", "", { "dependencies": { "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" } }, "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg=="],
- "@vercel/sandbox": ["@vercel/sandbox@2.1.1", "", { "dependencies": { "@vercel/oidc": "3.2.0", "@workflow/serde": "4.1.0-beta.2", "async-retry": "1.3.3", "jose": "6.2.3", "jsonlines": "0.1.1", "ms": "2.1.3", "picocolors": "^1.1.1", "tar-stream": "3.1.7", "undici": "^7.27.1", "xdg-app-paths": "5.1.0", "zod": "3.24.4" } }, "sha512-gKhW+YlvU15Qxya7jQKByB+sqA1dWat5zx/rvxT52E3Ryg9MAIXgqD5wd1d+CoJDbdHL26gIOcksTZY5sFpplA=="],
+ "yargs-parser": ["yargs-parser@22.0.0", "", {}, "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw=="],
- "@vercel/static-build": ["@vercel/static-build@2.11.4", "", { "dependencies": { "@vercel/gatsby-plugin-vercel-analytics": "1.0.11", "@vercel/gatsby-plugin-vercel-builder": "2.2.24", "@vercel/static-config": "3.4.0", "ts-morph": "12.0.0" } }, "sha512-Cyjsg4yfiSSwRSq4TaYnxDzh3Cembx6hlUCKD0z3QCvEYFjTR3j1bFKk8haekwSB16qg25k9fiPEZMP7B/Azag=="],
+ "dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
- "@vercel/static-config": ["@vercel/static-config@3.4.0", "", { "dependencies": { "ajv": "8.6.3", "json-schema-to-ts": "1.6.4", "ts-morph": "12.0.0" } }, "sha512-wCq90CMUB//ggnFh77NQO1xaLFsS4LigQIqKrH6ohnr9Br/KI1FhlErx62WfCOuueWaW+LVsbLOqNXIUjK8t6A=="],
+ "h3/crossws": ["crossws@0.3.5", "", { "dependencies": { "uncrypto": "^0.1.3" } }, "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA=="],
- "@vue/compiler-core": ["@vue/compiler-core@3.5.39", "", { "dependencies": { "@babel/parser": "^7.29.7", "@vue/shared": "3.5.39", "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-16KBTEXAJCpDr0mwlw+AZyhu8iyC7R3S2vBwsI7QnWJU6X3WKc9VKeNEZpiMdZ569qWhz9574L3vV55qRL0Vtw=="],
+ "parse5/entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="],
- "@vue/compiler-dom": ["@vue/compiler-dom@3.5.39", "", { "dependencies": { "@vue/compiler-core": "3.5.39", "@vue/shared": "3.5.39" } }, "sha512-oQPigALqYbNxTNPvNgSOe+czwVExfbVF02lz8jP0S3AXJiu3jxYDygNUiqSep4ezzW8XgnubqH63My2A7JR/vg=="],
-
- "@vue/compiler-sfc": ["@vue/compiler-sfc@3.5.39", "", { "dependencies": { "@babel/parser": "^7.29.7", "@vue/compiler-core": "3.5.39", "@vue/compiler-dom": "3.5.39", "@vue/compiler-ssr": "3.5.39", "@vue/shared": "3.5.39", "estree-walker": "^2.0.2", "magic-string": "^0.30.21", "postcss": "^8.5.15", "source-map-js": "^1.2.1" } }, "sha512-d0ki86iOyN8LoZPBmk5SJWNwHP19CnDDCfuo//+2WJa2g5Ke0Jay983PIBIcSSzldC68I8DrD5GrHV3OSDfodg=="],
-
- "@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.39", "", { "dependencies": { "@vue/compiler-dom": "3.5.39", "@vue/shared": "3.5.39" } }, "sha512-Ce7/wvwMHai74bdszfXExdazFigYnlF9zgCmEQUcM1j0fOymlouZ7XilTYNo8oUjhlnjYOZbGrcYKuqjz89Ucw=="],
-
- "@vue/shared": ["@vue/shared@3.5.39", "", {}, "sha512-l1rrBtBfTnmxvtsvdQDXltUUy8S1Y+ZaqdfUzmAnJkTd8Z8rv5v/ytW+TKiqEOWyHPoqtPlNFSs0lhRmYVSHVA=="],
-
- "@whatwg-node/disposablestack": ["@whatwg-node/disposablestack@0.0.6", "", { "dependencies": { "@whatwg-node/promise-helpers": "^1.0.0", "tslib": "^2.6.3" } }, "sha512-LOtTn+JgJvX8WfBVJtF08TGrdjuFzGJc4mkP8EdDI8ADbvO7kiexYep1o8dwnt0okb0jYclCDXF13xU7Ge4zSw=="],
-
- "@whatwg-node/fetch": ["@whatwg-node/fetch@0.10.13", "", { "dependencies": { "@whatwg-node/node-fetch": "^0.8.3", "urlpattern-polyfill": "^10.0.0" } }, "sha512-b4PhJ+zYj4357zwk4TTuF2nEe0vVtOrwdsrNo5hL+u1ojXNhh1FgJ6pg1jzDlwlT4oBdzfSwaBwMCtFCsIWg8Q=="],
-
- "@whatwg-node/node-fetch": ["@whatwg-node/node-fetch@0.8.6", "", { "dependencies": { "@fastify/busboy": "^3.1.1", "@whatwg-node/disposablestack": "^0.0.6", "@whatwg-node/promise-helpers": "^1.3.2", "tslib": "^2.6.3" } }, "sha512-BDMdYFcerLQkwA2RTldxOqRCs6ZQD1S7UgP3pUdGUkcbgTrP/V5ko77ZkCww9DHmC4lpoYuwigGfQYj285gMvA=="],
-
- "@whatwg-node/promise-helpers": ["@whatwg-node/promise-helpers@1.3.2", "", { "dependencies": { "tslib": "^2.6.3" } }, "sha512-Nst5JdK47VIl9UcGwtv2Rcgyn5lWtZ0/mhRQ4G8NN2isxpq2TO30iqHzmwoJycjWuyUfg3GFXqP/gFHXeV57IA=="],
-
- "@whatwg-node/server": ["@whatwg-node/server@0.10.18", "", { "dependencies": { "@envelop/instrumentation": "^1.0.0", "@whatwg-node/disposablestack": "^0.0.6", "@whatwg-node/fetch": "^0.10.13", "@whatwg-node/promise-helpers": "^1.3.2", "tslib": "^2.6.3" } }, "sha512-kMwLlxUbduttIgaPdSkmEarFpP+mSY8FEm+QWMBRJwxOHWkri+cxd8KZHO9EMrB9vgUuz+5WEaCawaL5wGVoXg=="],
-
- "@workflow/serde": ["@workflow/serde@4.1.0-beta.2", "", {}, "sha512-8kkeoQKLDaKXefjV5dbhBj2aErfKp1Mc4pb6tj8144cF+Em5SPbyMbyLCHp+BVrFfFVCBluCtMx+jjvaFVZGww=="],
-
- "abbrev": ["abbrev@3.0.1", "", {}, "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg=="],
-
- "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="],
-
- "abstract-logging": ["abstract-logging@2.0.1", "", {}, "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA=="],
-
- "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
-
- "acorn": ["acorn@8.17.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg=="],
-
- "acorn-import-attributes": ["acorn-import-attributes@1.9.5", "", { "peerDependencies": { "acorn": "^8" } }, "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ=="],
-
- "acorn-walk": ["acorn-walk@8.3.5", "", { "dependencies": { "acorn": "^8.11.0" } }, "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw=="],
-
- "agent-base": ["agent-base@8.0.0", "", {}, "sha512-QT8i0hCz6C/KQ+KTAbSNwCHDGdmUJl2tp2ZpNlGSWCfhUNVbYG2WLE3MdZGBAgXPV4GAvjGMxo+C1hroyxmZEg=="],
-
- "ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="],
-
- "ajv-errors": ["ajv-errors@3.0.0", "", { "peerDependencies": { "ajv": "^8.0.1" } }, "sha512-V3wD15YHfHz6y0KdhYFjyy9vWtEVALT9UrxfN3zqlI6dMioHnJrqOYfyPKol3oqrnCM9uwkcdCwkJ0WUcbLMTQ=="],
-
- "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="],
-
- "ansi-align": ["ansi-align@3.0.1", "", { "dependencies": { "string-width": "^4.1.0" } }, "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w=="],
-
- "ansi-colors": ["ansi-colors@4.1.3", "", {}, "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw=="],
-
- "ansi-escapes": ["ansi-escapes@7.3.0", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg=="],
-
- "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
-
- "ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
-
- "ansi-to-html": ["ansi-to-html@0.7.2", "", { "dependencies": { "entities": "^2.2.0" }, "bin": { "ansi-to-html": "bin/ansi-to-html" } }, "sha512-v6MqmEpNlxF+POuyhKkidusCHWWkaLcGRURzivcU3I9tv7k4JVhFcnukrM5Rlk2rUywdZuzYAZ+kbZqWCnfN3g=="],
-
- "ansis": ["ansis@4.3.1", "", {}, "sha512-BJ8/l4R5LRE7hW9WdSuGYrLSHi2ynxeFpDFbH0K/CgNeY/tyhk+vO6TYxXC5r5CpUhNVX310xzPsN/H9lCdfOA=="],
-
- "any-promise": ["any-promise@1.3.0", "", {}, "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="],
-
- "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="],
-
- "archiver": ["archiver@7.0.1", "", { "dependencies": { "archiver-utils": "^5.0.2", "async": "^3.2.4", "buffer-crc32": "^1.0.0", "readable-stream": "^4.0.0", "readdir-glob": "^1.1.2", "tar-stream": "^3.0.0", "zip-stream": "^6.0.1" } }, "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ=="],
-
- "archiver-utils": ["archiver-utils@5.0.2", "", { "dependencies": { "glob": "^10.0.0", "graceful-fs": "^4.2.0", "is-stream": "^2.0.1", "lazystream": "^1.0.0", "lodash": "^4.17.15", "normalize-path": "^3.0.0", "readable-stream": "^4.0.0" } }, "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA=="],
-
- "arg": ["arg@4.1.0", "", {}, "sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg=="],
-
- "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
-
- "array-buffer-byte-length": ["array-buffer-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "is-array-buffer": "^3.0.5" } }, "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw=="],
-
- "array-timsort": ["array-timsort@1.0.3", "", {}, "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ=="],
-
- "array.prototype.flatmap": ["array.prototype.flatmap@1.3.3", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-shim-unscopables": "^1.0.2" } }, "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg=="],
-
- "arraybuffer.prototype.slice": ["arraybuffer.prototype.slice@1.0.4", "", { "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" } }, "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ=="],
-
- "ascii-table": ["ascii-table@0.0.9", "", {}, "sha512-xpkr6sCDIYTPqzvjG8M3ncw1YOTaloWZOyrUmicoEifBEKzQzt+ooUpRpQ/AbOoJfO/p2ZKiyp79qHThzJDulQ=="],
-
- "ast-module-types": ["ast-module-types@6.0.2", "", {}, "sha512-6KuK/7nZ/2Qh7sGuVEiwxjCxzTY2Pdb5mTo5z1e6/J8BA0tvjR7G8vQJKrQMTqwmnA3UPEyKIFX4YUS1DO1Hvw=="],
-
- "ast-types": ["ast-types@0.13.4", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w=="],
-
- "async": ["async@3.2.6", "", {}, "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="],
-
- "async-function": ["async-function@1.0.0", "", {}, "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA=="],
-
- "async-listen": ["async-listen@3.0.0", "", {}, "sha512-V+SsTpDqkrWTimiotsyl33ePSjA5/KrithwupuvJ6ztsqPvGv6ge4OredFhPffVXiLN/QUWvE0XcqJaYgt6fOg=="],
-
- "async-retry": ["async-retry@1.3.3", "", { "dependencies": { "retry": "0.13.1" } }, "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw=="],
-
- "async-sema": ["async-sema@3.1.1", "", {}, "sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg=="],
-
- "atomic-sleep": ["atomic-sleep@1.0.0", "", {}, "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ=="],
-
- "atomically": ["atomically@2.1.1", "", { "dependencies": { "stubborn-fs": "^2.0.0", "when-exit": "^2.1.4" } }, "sha512-P4w9o2dqARji6P7MHprklbfiArZAWvo07yW7qs3pdljb3BWr12FIB7W+p0zJiuiVsUpRO0iZn1kFFcpPegg0tQ=="],
-
- "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="],
-
- "avvio": ["avvio@9.2.0", "", { "dependencies": { "@fastify/error": "^4.0.0", "fastq": "^1.17.1" } }, "sha512-2t/sy01ArdHHE0vRH5Hsay+RtCZt3dLPji7W7/MMOCEgze5b7SNDC4j5H6FnVgPkI1MTNFGzHdHrVXDDl7QSSQ=="],
-
- "b4a": ["b4a@1.8.1", "", { "peerDependencies": { "react-native-b4a": "*" }, "optionalPeers": ["react-native-b4a"] }, "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw=="],
-
- "backoff": ["backoff@2.5.0", "", { "dependencies": { "precond": "0.2" } }, "sha512-wC5ihrnUXmR2douXmXLCe5O3zg3GKIyvRi/hi58a/XyRxVI+3/yM0PYueQOZXPXQ9pxBislYkw+sF9b7C/RuMA=="],
-
- "balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
-
- "bare-events": ["bare-events@2.9.1", "", { "peerDependencies": { "bare-abort-controller": "*" }, "optionalPeers": ["bare-abort-controller"] }, "sha512-Z0oHEHAFDZkffN8Qc39zNZjQlMDkPJRyyyZieU1VH7u8c5S+qHZ2S8ixdKIAxEjfHO7FJxXmJWgteOghVanIsg=="],
-
- "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
-
- "basic-ftp": ["basic-ftp@5.3.1", "", {}, "sha512-bopVNp6ugyA150DDuZfPFdt1KZ5a94ZDiwX4hMgZDzF+GttD80lEy8kj98kbyhLXnPvhtIo93mdnLIjpCAeeOw=="],
-
- "before-after-hook": ["before-after-hook@4.0.0", "", {}, "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ=="],
-
- "better-ajv-errors": ["better-ajv-errors@1.2.0", "", { "dependencies": { "@babel/code-frame": "^7.16.0", "@humanwhocodes/momoa": "^2.0.2", "chalk": "^4.1.2", "jsonpointer": "^5.0.0", "leven": "^3.1.0 < 4" }, "peerDependencies": { "ajv": "4.11.8 - 8" } }, "sha512-UW+IsFycygIo7bclP9h5ugkNH8EjCSgqyFB/yQ4Hqqa1OEYDtb0uFIkYE0b6+CjkgJYVM5UKI/pJPxjYe9EZlA=="],
-
- "bindings": ["bindings@1.5.0", "", { "dependencies": { "file-uri-to-path": "1.0.0" } }, "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ=="],
-
- "bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="],
-
- "blake3-wasm": ["blake3-wasm@2.1.5", "", {}, "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g=="],
-
- "body-parser": ["body-parser@2.3.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^2.0.0", "debug": "^4.4.3", "http-errors": "^2.0.1", "iconv-lite": "^0.7.2", "on-finished": "^2.4.1", "qs": "^6.15.2", "raw-body": "^3.0.2", "type-is": "^2.1.0" } }, "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw=="],
-
- "boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="],
-
- "boxen": ["boxen@8.0.1", "", { "dependencies": { "ansi-align": "^3.0.1", "camelcase": "^8.0.0", "chalk": "^5.3.0", "cli-boxes": "^3.0.0", "string-width": "^7.2.0", "type-fest": "^4.21.0", "widest-line": "^5.0.0", "wrap-ansi": "^9.0.0" } }, "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw=="],
-
- "brace-expansion": ["brace-expansion@5.0.7", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA=="],
-
- "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
-
- "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
-
- "buffer-crc32": ["buffer-crc32@1.0.0", "", {}, "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w=="],
-
- "buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="],
-
- "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
-
- "bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="],
-
- "bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="],
-
- "byline": ["byline@5.0.0", "", {}, "sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q=="],
-
- "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
-
- "call-bind": ["call-bind@1.0.9", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "get-intrinsic": "^1.3.0", "set-function-length": "^1.2.2" } }, "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ=="],
-
- "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
-
- "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="],
-
- "callsite": ["callsite@1.0.0", "", {}, "sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ=="],
-
- "camelcase": ["camelcase@8.0.0", "", {}, "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA=="],
-
- "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
-
- "chardet": ["chardet@2.2.0", "", {}, "sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA=="],
-
- "cheerio": ["cheerio@1.2.0", "", { "dependencies": { "cheerio-select": "^2.1.0", "dom-serializer": "^2.0.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", "encoding-sniffer": "^0.2.1", "htmlparser2": "^10.1.0", "parse5": "^7.3.0", "parse5-htmlparser2-tree-adapter": "^7.1.0", "parse5-parser-stream": "^7.1.2", "undici": "^7.19.0", "whatwg-mimetype": "^4.0.0" } }, "sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg=="],
-
- "cheerio-select": ["cheerio-select@2.1.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-select": "^5.1.0", "css-what": "^6.1.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.0.1" } }, "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g=="],
-
- "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
-
- "chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="],
-
- "ci-info": ["ci-info@4.4.0", "", {}, "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg=="],
-
- "citty": ["citty@0.1.6", "", { "dependencies": { "consola": "^3.2.3" } }, "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ=="],
-
- "cjs-module-lexer": ["cjs-module-lexer@1.2.3", "", {}, "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ=="],
-
- "clean-deep": ["clean-deep@3.4.0", "", { "dependencies": { "lodash.isempty": "^4.4.0", "lodash.isplainobject": "^4.0.6", "lodash.transform": "^4.6.0" } }, "sha512-Lo78NV5ItJL/jl+B5w0BycAisaieJGXK1qYi/9m4SjR8zbqmrUtO7Yhro40wEShGmmxs/aJLI/A+jNhdkXK8mw=="],
-
- "clean-stack": ["clean-stack@5.3.0", "", { "dependencies": { "escape-string-regexp": "5.0.0" } }, "sha512-9ngPTOhYGQqNVSfeJkYXHmF7AGWp4/nN5D/QqNQs3Dvxd1Kk/WpjHfNujKHYUQ/5CoGyOyFNoWSPk5afzP0QVg=="],
-
- "cli-boxes": ["cli-boxes@3.0.0", "", {}, "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g=="],
-
- "cli-cursor": ["cli-cursor@3.1.0", "", { "dependencies": { "restore-cursor": "^3.1.0" } }, "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw=="],
-
- "cli-spinners": ["cli-spinners@2.9.2", "", {}, "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="],
-
- "cli-width": ["cli-width@3.0.0", "", {}, "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw=="],
-
- "cliui": ["cliui@9.0.1", "", { "dependencies": { "string-width": "^7.2.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w=="],
-
- "clone": ["clone@1.0.4", "", {}, "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg=="],
-
- "code-block-writer": ["code-block-writer@10.1.1", "", {}, "sha512-67ueh2IRGst/51p0n6FvPrnRjAGHY5F8xdjkgrYE7DDzpJe6qA07RYQ9VcoUeo5ATOjSOiWpSL3SWBRRbempMw=="],
-
- "color": ["color@5.0.3", "", { "dependencies": { "color-convert": "^3.1.3", "color-string": "^2.1.3" } }, "sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA=="],
-
- "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
-
- "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
-
- "color-string": ["color-string@2.1.4", "", { "dependencies": { "color-name": "^2.0.0" } }, "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg=="],
-
- "colors": ["colors@1.4.0", "", {}, "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA=="],
-
- "commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="],
-
- "comment-json": ["comment-json@4.6.2", "", { "dependencies": { "array-timsort": "^1.0.3", "esprima": "^4.0.1" } }, "sha512-R2rze/hDX30uul4NZoIZ76ImSJLFxn/1/ZxtKC1L77y2X1k+yYu1joKbAtMA2Fg3hZrTOiw0I5mwVMo0cf250w=="],
-
- "common-path-prefix": ["common-path-prefix@3.0.0", "", {}, "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w=="],
-
- "compress-commons": ["compress-commons@6.0.2", "", { "dependencies": { "crc-32": "^1.2.0", "crc32-stream": "^6.0.0", "is-stream": "^2.0.1", "normalize-path": "^3.0.0", "readable-stream": "^4.0.0" } }, "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg=="],
-
- "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
-
- "confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="],
-
- "config-chain": ["config-chain@1.1.13", "", { "dependencies": { "ini": "^1.3.4", "proto-list": "~1.2.1" } }, "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ=="],
-
- "configstore": ["configstore@7.1.0", "", { "dependencies": { "atomically": "^2.0.3", "dot-prop": "^9.0.0", "graceful-fs": "^4.2.11", "xdg-basedir": "^5.1.0" } }, "sha512-N4oog6YJWbR9kGyXvS7jEykLDXIE2C0ILYqNBZBp9iwiJpoCBWYsuAdW6PPFn6w06jjnC+3JstVvWHO4cZqvRg=="],
-
- "consola": ["consola@3.4.2", "", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="],
-
- "content-disposition": ["content-disposition@1.1.0", "", {}, "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g=="],
-
- "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="],
-
- "convert-hrtime": ["convert-hrtime@3.0.0", "", {}, "sha512-7V+KqSvMiHp8yWDuwfww06XleMWVVB9b9tURBx+G7UTADuo5hYPuowKloz4OzOqbPezxgo+fdQ1522WzPG4OeA=="],
-
- "cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="],
-
- "cookie-es": ["cookie-es@1.2.3", "", {}, "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw=="],
-
- "cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="],
-
- "copy-file": ["copy-file@11.1.0", "", { "dependencies": { "graceful-fs": "^4.2.11", "p-event": "^6.0.0" } }, "sha512-X8XDzyvYaA6msMyAM575CUoygY5b44QzLcGRKsK3MFmXcOvQa518dNPLsKYwkYsn72g3EiW+LE0ytd/FlqWmyw=="],
-
- "core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="],
-
- "cpy": ["cpy@11.1.0", "", { "dependencies": { "copy-file": "^11.0.0", "globby": "^14.0.2", "junk": "^4.0.1", "micromatch": "^4.0.7", "p-filter": "^4.1.0", "p-map": "^7.0.2" } }, "sha512-QGHetPSSuprVs+lJmMDcivvrBwTKASzXQ5qxFvRC2RFESjjod71bDvFvhxTjDgkNjrrb72AI6JPjfYwxrIy33A=="],
-
- "crc-32": ["crc-32@1.2.2", "", { "bin": { "crc32": "bin/crc32.njs" } }, "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ=="],
-
- "crc32-stream": ["crc32-stream@6.0.0", "", { "dependencies": { "crc-32": "^1.2.0", "readable-stream": "^4.0.0" } }, "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g=="],
-
- "create-require": ["create-require@1.1.1", "", {}, "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ=="],
-
- "cron-parser": ["cron-parser@4.9.0", "", { "dependencies": { "luxon": "^3.2.1" } }, "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q=="],
-
- "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
-
- "crossws": ["crossws@0.4.5", "", { "peerDependencies": { "srvx": ">=0.11.5" }, "optionalPeers": ["srvx"] }, "sha512-wUR89x/Rw7/8t+vn0CmGDYM9TD6VtARGb0LD5jq2wjtMy1vCP4M+sm6N6TigWeTYvnA8MoW29NqqXD0ep0rfBA=="],
-
- "css-select": ["css-select@5.2.2", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw=="],
-
- "css-tree": ["css-tree@3.2.1", "", { "dependencies": { "mdn-data": "2.27.1", "source-map-js": "^1.2.1" } }, "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA=="],
-
- "css-what": ["css-what@6.2.2", "", {}, "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA=="],
-
- "cssfilter": ["cssfilter@0.0.10", "", {}, "sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw=="],
-
- "csso": ["csso@5.0.5", "", { "dependencies": { "css-tree": "~2.2.0" } }, "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ=="],
-
- "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
-
- "cyclist": ["cyclist@1.0.2", "", {}, "sha512-0sVXIohTfLqVIW3kb/0n6IiWF3Ifj5nm2XaSrLq2DI6fKIGa2fYAZdk917rUneaeLVpYfFcyXE2ft0fe3remsA=="],
-
- "data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="],
-
- "data-view-buffer": ["data-view-buffer@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="],
-
- "data-view-byte-length": ["data-view-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ=="],
-
- "data-view-byte-offset": ["data-view-byte-offset@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" } }, "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ=="],
-
- "dayjs": ["dayjs@1.11.21", "", {}, "sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA=="],
-
- "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
-
- "decache": ["decache@4.6.2", "", { "dependencies": { "callsite": "^1.0.0" } }, "sha512-2LPqkLeu8XWHU8qNCS3kcF6sCcb5zIzvWaAHYSvPfwhdd7mHuah29NssMzrTYyHN4F5oFy2ko9OBYxegtU0FEw=="],
-
- "deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="],
-
- "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="],
-
- "default-browser": ["default-browser@5.5.0", "", { "dependencies": { "bundle-name": "^4.1.0", "default-browser-id": "^5.0.0" } }, "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw=="],
-
- "default-browser-id": ["default-browser-id@5.0.1", "", {}, "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q=="],
-
- "defaults": ["defaults@1.0.4", "", { "dependencies": { "clone": "^1.0.2" } }, "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A=="],
-
- "define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="],
-
- "define-lazy-prop": ["define-lazy-prop@3.0.0", "", {}, "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg=="],
-
- "define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="],
-
- "defu": ["defu@6.1.7", "", {}, "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ=="],
-
- "degenerator": ["degenerator@5.0.1", "", { "dependencies": { "ast-types": "^0.13.4", "escodegen": "^2.1.0", "esprima": "^4.0.1" } }, "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ=="],
-
- "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
-
- "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
-
- "destr": ["destr@2.0.5", "", {}, "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA=="],
-
- "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
-
- "detective-amd": ["detective-amd@6.1.0", "", { "dependencies": { "ast-module-types": "^6.0.1", "escodegen": "^2.1.0", "get-amd-module-type": "^6.0.2", "node-source-walk": "^7.0.1" }, "bin": { "detective-amd": "bin/cli.js" } }, "sha512-fmI6LGMvotqd49QaA3ZYw+q0aGp2yXmMjzIuY6fH9j9YFIXY/73yDhMwhX9cPbhWd+AH06NH1Di/LKOuCH0Ubg=="],
-
- "detective-cjs": ["detective-cjs@6.1.1", "", { "dependencies": { "ast-module-types": "^6.0.1", "node-source-walk": "^7.0.1" } }, "sha512-pSh7mkCKEtLlmANqLu3KDFS3NV8Hx41jy/JF1/gAWOgU+Uo5QTkeI1tWNP4dWGo4L0E9j18Ez9EPsTleautKqA=="],
-
- "detective-es6": ["detective-es6@5.0.2", "", { "dependencies": { "node-source-walk": "^7.0.1" } }, "sha512-+qHHGYhjupiVs4rnIpI9nZ5B130A4AmE35ZX1w33hb46vcZ7T3jfDbvmPw0FhWtMHn5BS5HHu7ZtnZ53bMcXZA=="],
-
- "detective-postcss": ["detective-postcss@8.0.4", "", { "dependencies": { "is-url-superb": "^4.0.0", "postcss-values-parser": "^6.0.2" }, "peerDependencies": { "postcss": "^8.4.47" } }, "sha512-DZ7M/hWPZyr17ZUdoQ+TVXaPj70mYr4XXrAE+GeJbca44haCvZgb191L/jLJmFYewhxRJuBd4lUtNSu986TXag=="],
-
- "detective-sass": ["detective-sass@6.0.2", "", { "dependencies": { "gonzales-pe": "^4.3.0", "node-source-walk": "^7.0.1" } }, "sha512-i3xpXHDKS0qI2aFW4asQ7fqlPK00ndOVZELvQapFJCaF0VxYmsNWtd0AmvXbTLMk7bfO5VdIeorhY9KfmHVoVA=="],
-
- "detective-scss": ["detective-scss@5.0.2", "", { "dependencies": { "gonzales-pe": "^4.3.0", "node-source-walk": "^7.0.1" } }, "sha512-9JOEMZ8pDh3ShXmftq7hoQqqJsClaGgxo1hghfCeFlmKf5TC/Twtwb0PAaK8dXwpg9Z0uCmEYSrCxO+kel2eEg=="],
-
- "detective-stylus": ["detective-stylus@5.0.1", "", {}, "sha512-Dgn0bUqdGbE3oZJ+WCKf8Dmu7VWLcmRJGc6RCzBgG31DLIyai9WAoEhYRgIHpt/BCRMrnXLbGWGPQuBUrnF0TA=="],
-
- "detective-typescript": ["detective-typescript@14.1.2", "", { "dependencies": { "@typescript-eslint/typescript-estree": "^8.58.2", "ast-module-types": "^6.0.1", "node-source-walk": "^7.0.1" }, "peerDependencies": { "typescript": "^5.4.4 || ^6.0.2" } }, "sha512-bIeEn0eVi/JRsE1YizBR2ilnMlWRAIBJJ6kXCKNFxEEWhUcEY3R6I3KYIAy48ieURbD1hcb3Ebvl8AqeoPMSzg=="],
-
- "detective-vue2": ["detective-vue2@2.3.0", "", { "dependencies": { "@dependents/detective-less": "^5.0.1", "@vue/compiler-sfc": "^3.5.32", "detective-es6": "^5.0.1", "detective-sass": "^6.0.1", "detective-scss": "^5.0.1", "detective-stylus": "^5.0.1", "detective-typescript": "^14.1.0" }, "peerDependencies": { "typescript": "^5.4.4 || ^6.0.2" } }, "sha512-3gwbZPqVTm9sL9XdZsgEJ7x4x99O853VVZHapQAiEkGuMJMpFPjHDrecSgfqnS5JW3FJfYXesLZGvUOibjn49g=="],
-
- "dettle": ["dettle@1.0.5", "", {}, "sha512-ZVyjhAJ7sCe1PNXEGveObOH9AC8QvMga3HJIghHawtG7mE4K5pW9nz/vDGAr/U7a3LWgdOzEE7ac9MURnyfaTA=="],
-
- "diff": ["diff@4.0.4", "", {}, "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ=="],
-
- "dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="],
-
- "domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="],
-
- "domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="],
-
- "domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="],
-
- "dot-prop": ["dot-prop@10.1.0", "", { "dependencies": { "type-fest": "^5.0.0" } }, "sha512-MVUtAugQMOff5RnBy2d9N31iG0lNwg1qAoAOn7pOK5wf94WIaE3My2p3uwTQuvS2AcqchkcR3bHByjaM0mmi7Q=="],
-
- "dotenv": ["dotenv@17.4.2", "", {}, "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw=="],
-
- "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
-
- "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="],
-
- "ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="],
-
- "edge-runtime": ["edge-runtime@2.5.9", "", { "dependencies": { "@edge-runtime/format": "2.2.1", "@edge-runtime/ponyfill": "2.4.2", "@edge-runtime/vm": "3.2.0", "async-listen": "3.0.1", "mri": "1.2.0", "picocolors": "1.0.0", "pretty-ms": "7.0.1", "signal-exit": "4.0.2", "time-span": "4.0.0" }, "bin": { "edge-runtime": "dist/cli/index.js" } }, "sha512-pk+k0oK0PVXdlT4oRp4lwh+unuKB7Ng4iZ2HB+EZ7QCEQizX360Rp/F4aRpgpRgdP2ufB35N+1KppHmYjqIGSg=="],
-
- "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
-
- "elysia": ["elysia@1.4.29", "", { "dependencies": { "cookie": "^1.1.1", "exact-mirror": "^0.2.7", "fast-decode-uri-component": "^1.0.1", "memoirist": "^0.4.0" }, "peerDependencies": { "@sinclair/typebox": ">= 0.34.0 < 1", "@types/bun": ">= 1.2.0", "file-type": ">= 20.0.0", "openapi-types": ">= 12.0.0", "typescript": ">= 5.0.0" }, "optionalPeers": ["@types/bun", "typescript"] }, "sha512-GwMRGGwSdjfPt+w3LA0fqTuYJtS8uVRJicvoar98/HrO5qdFKDc9CwjIb6Kja+v39lkY+58hr2JvdR9jQzlUuA=="],
-
- "emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="],
-
- "empathic": ["empathic@2.0.1", "", {}, "sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q=="],
-
- "enabled": ["enabled@2.0.0", "", {}, "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ=="],
-
- "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],
-
- "encoding-sniffer": ["encoding-sniffer@0.2.1", "", { "dependencies": { "iconv-lite": "^0.6.3", "whatwg-encoding": "^3.1.1" } }, "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw=="],
-
- "end-of-stream": ["end-of-stream@1.1.0", "", { "dependencies": { "once": "~1.3.0" } }, "sha512-EoulkdKF/1xa92q25PbjuDcgJ9RDHYU2Rs3SCIvs2/dSQ3BpmxneNHmA/M7fe60M3PrV7nNGTTNbkK62l6vXiQ=="],
-
- "enquirer": ["enquirer@2.4.1", "", { "dependencies": { "ansi-colors": "^4.1.1", "strip-ansi": "^6.0.1" } }, "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ=="],
-
- "entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="],
-
- "env-paths": ["env-paths@4.0.0", "", { "dependencies": { "is-safe-filename": "^0.1.0" } }, "sha512-pxP8eL2SwwaTRi/KHYwLYXinDs7gL3jxFcBYmEdYfZmZXbaVDvdppd0XBU8qVz03rDfKZMXg1omHCbsJjZrMsw=="],
-
- "envinfo": ["envinfo@7.21.0", "", { "bin": { "envinfo": "dist/cli.js" } }, "sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow=="],
-
- "environment": ["environment@1.1.0", "", {}, "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q=="],
-
- "error-stack-parser": ["error-stack-parser@2.1.4", "", { "dependencies": { "stackframe": "^1.3.4" } }, "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ=="],
-
- "error-stack-parser-es": ["error-stack-parser-es@1.0.5", "", {}, "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA=="],
-
- "es-abstract": ["es-abstract@1.24.2", "", { "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" } }, "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg=="],
-
- "es-abstract-get": ["es-abstract-get@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "es-object-atoms": "^1.1.2", "is-callable": "^1.2.7", "object-inspect": "^1.13.4" } }, "sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg=="],
-
- "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
-
- "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
-
- "es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="],
-
- "es-object-atoms": ["es-object-atoms@1.1.2", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw=="],
-
- "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="],
-
- "es-shim-unscopables": ["es-shim-unscopables@1.1.0", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw=="],
-
- "es-to-primitive": ["es-to-primitive@1.3.4", "", { "dependencies": { "es-abstract-get": "^1.0.0", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "is-callable": "^1.2.7", "is-date-object": "^1.1.0", "is-symbol": "^1.1.1" } }, "sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw=="],
-
- "esbuild": ["esbuild@0.28.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.1", "@esbuild/android-arm": "0.28.1", "@esbuild/android-arm64": "0.28.1", "@esbuild/android-x64": "0.28.1", "@esbuild/darwin-arm64": "0.28.1", "@esbuild/darwin-x64": "0.28.1", "@esbuild/freebsd-arm64": "0.28.1", "@esbuild/freebsd-x64": "0.28.1", "@esbuild/linux-arm": "0.28.1", "@esbuild/linux-arm64": "0.28.1", "@esbuild/linux-ia32": "0.28.1", "@esbuild/linux-loong64": "0.28.1", "@esbuild/linux-mips64el": "0.28.1", "@esbuild/linux-ppc64": "0.28.1", "@esbuild/linux-riscv64": "0.28.1", "@esbuild/linux-s390x": "0.28.1", "@esbuild/linux-x64": "0.28.1", "@esbuild/netbsd-arm64": "0.28.1", "@esbuild/netbsd-x64": "0.28.1", "@esbuild/openbsd-arm64": "0.28.1", "@esbuild/openbsd-x64": "0.28.1", "@esbuild/openharmony-arm64": "0.28.1", "@esbuild/sunos-x64": "0.28.1", "@esbuild/win32-arm64": "0.28.1", "@esbuild/win32-ia32": "0.28.1", "@esbuild/win32-x64": "0.28.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw=="],
-
- "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
-
- "escape-goat": ["escape-goat@4.0.0", "", {}, "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg=="],
-
- "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="],
-
- "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
-
- "escodegen": ["escodegen@2.1.0", "", { "dependencies": { "esprima": "^4.0.1", "estraverse": "^5.2.0", "esutils": "^2.0.2" }, "optionalDependencies": { "source-map": "~0.6.1" }, "bin": { "esgenerate": "bin/esgenerate.js", "escodegen": "bin/escodegen.js" } }, "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w=="],
-
- "eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="],
-
- "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="],
-
- "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="],
-
- "estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
-
- "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
-
- "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="],
-
- "event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="],
-
- "eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="],
-
- "events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="],
-
- "events-intercept": ["events-intercept@2.0.0", "", {}, "sha512-blk1va0zol9QOrdZt0rFXo5KMkNPVSp92Eju/Qz8THwKWKRKeE0T8Br/1aW6+Edkyq9xHYgYxn2QtOnUKPUp+Q=="],
-
- "events-universal": ["events-universal@1.0.1", "", { "dependencies": { "bare-events": "^2.7.0" } }, "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw=="],
-
- "exact-mirror": ["exact-mirror@0.2.7", "", { "peerDependencies": { "@sinclair/typebox": "^0.34.15" }, "optionalPeers": ["@sinclair/typebox"] }, "sha512-+MeEmDcLA4o/vjK2zujgk+1VTxPR4hdp23qLqkWfStbECtAq9gmsvQa3LW6z/0GXZyHJobrCnmy1cdeE7BjsYg=="],
-
- "execa": ["execa@5.1.1", "", { "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" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="],
-
- "express": ["express@5.2.1", "", { "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" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="],
-
- "express-logging": ["express-logging@1.1.1", "", { "dependencies": { "on-headers": "^1.0.0" } }, "sha512-1KboYwxxCG5kwkJHR5LjFDTD1Mgl8n4PIMcCuhhd/1OqaxlC68P3QKbvvAbZVUtVgtlxEdTgSUwf6yxwzRCuuA=="],
-
- "extract-zip": ["extract-zip@2.0.1", "", { "dependencies": { "debug": "^4.1.1", "get-stream": "^5.1.0", "yauzl": "^2.10.0" }, "optionalDependencies": { "@types/yauzl": "^2.9.1" }, "bin": { "extract-zip": "cli.js" } }, "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg=="],
-
- "fast-decode-uri-component": ["fast-decode-uri-component@1.0.1", "", {}, "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg=="],
-
- "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
-
- "fast-equals": ["fast-equals@5.4.0", "", {}, "sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw=="],
-
- "fast-fifo": ["fast-fifo@1.3.2", "", {}, "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ=="],
-
- "fast-glob": ["fast-glob@3.3.3", "", { "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.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
-
- "fast-json-stringify": ["fast-json-stringify@6.4.0", "", { "dependencies": { "@fastify/merge-json-schemas": "^0.2.0", "ajv": "^8.12.0", "ajv-formats": "^3.0.1", "fast-uri": "^3.0.0", "json-schema-ref-resolver": "^3.0.0", "rfdc": "^1.2.0" } }, "sha512-ibRCQ0GZKJIQ+P3Et1h0LhPgp3PMTYk0MH8O+kW3lNYsvmaQww5Nn3f1jf73Q0jR1Yz3a1CDP4/NZD3vOajWJQ=="],
-
- "fast-querystring": ["fast-querystring@1.1.2", "", { "dependencies": { "fast-decode-uri-component": "^1.0.1" } }, "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg=="],
-
- "fast-safe-stringify": ["fast-safe-stringify@2.1.1", "", {}, "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA=="],
-
- "fast-stringify": ["fast-stringify@4.0.0", "", {}, "sha512-lE2DIivBaLysf6hK5WH/VfMgqRbvBVHcpGVVTmA5Zi8oWIjq9YxIt6lYGdUgP1HNSXxTIat7HEIDnrSvXSeKQw=="],
-
- "fast-uri": ["fast-uri@3.1.3", "", {}, "sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg=="],
-
- "fast-xml-builder": ["fast-xml-builder@1.1.4", "", { "dependencies": { "path-expression-matcher": "^1.1.3" } }, "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg=="],
-
- "fast-xml-parser": ["fast-xml-parser@5.5.12", "", { "dependencies": { "fast-xml-builder": "^1.1.4", "path-expression-matcher": "^1.5.0", "strnum": "^2.2.3" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-nUR0q8PPfoA/svPM43Gup7vLOZWppaNrYgGmrVqrAVJa7cOH4hMG6FX9M4mQ8dZA1/ObGZHzES7Ed88hxEBSJg=="],
-
- "fastest-levenshtein": ["fastest-levenshtein@1.0.16", "", {}, "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg=="],
-
- "fastify": ["fastify@5.9.0", "", { "dependencies": { "@fastify/ajv-compiler": "^4.0.5", "@fastify/error": "^4.0.0", "@fastify/fast-json-stringify-compiler": "^5.0.0", "@fastify/proxy-addr": "^5.0.0", "abstract-logging": "^2.0.1", "avvio": "^9.0.0", "fast-json-stringify": "^6.0.0", "find-my-way": "^9.6.0", "light-my-request": "^6.0.0", "pino": "^9.14.0 || ^10.1.0", "process-warning": "^5.0.0", "rfdc": "^1.3.1", "secure-json-parse": "^4.0.0", "semver": "^7.6.0", "toad-cache": "^3.7.0" } }, "sha512-VMS5lE0zj+MZlJpQa3Qv5iGjfun0H2N7VRgoBwpcTNQ2bdIQpv7fDpb+HGteGbicBsGkzGS+X+hdx9mmrfWuHQ=="],
-
- "fastify-plugin": ["fastify-plugin@5.1.0", "", {}, "sha512-FAIDA8eovSt5qcDgcBvDuX/v0Cjz0ohGhENZ/wpc3y+oZCY2afZ9Baqql3g/lC+OHRnciQol4ww7tuthOb9idw=="],
-
- "fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="],
-
- "fd-slicer": ["fd-slicer@1.1.0", "", { "dependencies": { "pend": "~1.2.0" } }, "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g=="],
-
- "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
-
- "fecha": ["fecha@4.2.3", "", {}, "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw=="],
-
- "feed": ["feed@5.2.1", "", { "dependencies": { "xml-js": "^1.6.11" } }, "sha512-jTynzYPWs9ALjro0GW8j7sv9y7cJBeOdD4Y88kVqYy/eyusIX3g+499JiTDIlD9Ge/unebx57T4Uzo6vpYvMtA=="],
-
- "fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="],
-
- "figures": ["figures@6.1.0", "", { "dependencies": { "is-unicode-supported": "^2.0.0" } }, "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg=="],
-
- "file-type": ["file-type@22.0.1", "", { "dependencies": { "@tokenizer/inflate": "^0.4.1", "strtok3": "^10.3.5", "token-types": "^6.1.2", "uint8array-extras": "^1.5.0" } }, "sha512-ww5Mhre0EE+jmBvOXTmXAbEMuZE7uX4a3+oRCQFNj8w++g3ev913N6tXQz0XTXbueQ5TWQfm6BdaViEHHn8bhA=="],
-
- "file-uri-to-path": ["file-uri-to-path@1.0.0", "", {}, "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="],
-
- "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
-
- "filter-obj": ["filter-obj@6.1.0", "", {}, "sha512-xdMtCAODmPloU9qtmPcdBV9Kd27NtMse+4ayThxqIHUES5Z2S6bGpap5PpdmNM56ub7y3i1eyr+vJJIIgWGKmA=="],
-
- "finalhandler": ["finalhandler@2.1.1", "", { "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" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="],
-
- "find-my-way": ["find-my-way@9.6.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-querystring": "^1.0.0", "safe-regex2": "^5.0.0" } }, "sha512-Zf4Xve4RymLl7NgaavNebZ01joJ8MfVerOG43wy7SHLO+r+K0C6d/SE0BiR7AV5V1VOCFlOP7ecdo+I4qmiHrQ=="],
-
- "find-up": ["find-up@8.0.0", "", { "dependencies": { "locate-path": "^8.0.0", "unicorn-magic": "^0.3.0" } }, "sha512-JGG8pvDi2C+JxidYdIwQDyS/CgcrIdh18cvgxcBge3wSHRQOrooMD3GlFBcmMJAN9M42SAZjDp5zv1dglJjwww=="],
-
- "find-up-simple": ["find-up-simple@1.0.1", "", {}, "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ=="],
-
- "fn.name": ["fn.name@1.1.0", "", {}, "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="],
-
- "folder-walker": ["folder-walker@3.2.0", "", { "dependencies": { "from2": "^2.1.0" } }, "sha512-VjAQdSLsl6AkpZNyrQJfO7BXLo4chnStqb055bumZMbRUPpVuPN3a4ktsnRCmrFZjtMlYLkyXiR5rAs4WOpC4Q=="],
-
- "follow-redirects": ["follow-redirects@1.16.0", "", { "peerDependencies": { "debug": "*" }, "optionalPeers": ["debug"] }, "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw=="],
-
- "for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="],
-
- "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="],
-
- "formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="],
-
- "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
-
- "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="],
-
- "from2": ["from2@2.3.0", "", { "dependencies": { "inherits": "^2.0.1", "readable-stream": "^2.0.0" } }, "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g=="],
-
- "fs-extra": ["fs-extra@11.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw=="],
-
- "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
-
- "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
-
- "function.prototype.name": ["function.prototype.name@1.2.0", "", { "dependencies": { "call-bind": "^1.0.9", "call-bound": "^1.0.4", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", "has-property-descriptors": "^1.0.2", "hasown": "^2.0.4", "is-callable": "^1.2.7", "is-document.all": "^1.0.0" } }, "sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew=="],
-
- "functions-have-names": ["functions-have-names@1.2.3", "", {}, "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="],
-
- "fuzzy": ["fuzzy@0.1.3", "", {}, "sha512-/gZffu4ykarLrCiP3Ygsa86UAo1E5vEVlvTrpkKywXSbP9Xhln3oSp9QSV57gEq3JFFpGJ4GZ+5zdEp3FcUh4w=="],
-
- "generator-function": ["generator-function@2.0.1", "", {}, "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g=="],
-
- "generic-pool": ["generic-pool@3.4.2", "", {}, "sha512-H7cUpwCQSiJmAHM4c/aFu6fUfrhWXW1ncyh8ftxEPMu6AiYkHw9K8br720TGPZJbk5eOH2bynjZD1yPvdDAmag=="],
-
- "get-amd-module-type": ["get-amd-module-type@6.0.2", "", { "dependencies": { "ast-module-types": "^6.0.1", "node-source-walk": "^7.0.1" } }, "sha512-7zShVYAYtMnj9S65CfN+hvpBCByfuB1OY8xID01nZEzXTZbx4YyysAfi+nMl95JSR6odt4q8TCj2W63KAoyVLQ=="],
-
- "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="],
-
- "get-east-asian-width": ["get-east-asian-width@1.5.0", "", {}, "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA=="],
-
- "get-intrinsic": ["get-intrinsic@1.3.0", "", { "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" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
-
- "get-port": ["get-port@5.1.1", "", {}, "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ=="],
-
- "get-port-please": ["get-port-please@3.2.0", "", {}, "sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A=="],
-
- "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
-
- "get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="],
-
- "get-symbol-description": ["get-symbol-description@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6" } }, "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg=="],
-
- "get-tsconfig": ["get-tsconfig@4.14.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA=="],
-
- "get-uri": ["get-uri@6.0.5", "", { "dependencies": { "basic-ftp": "^5.0.2", "data-uri-to-buffer": "^6.0.2", "debug": "^4.3.4" } }, "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg=="],
-
- "git-repo-info": ["git-repo-info@2.1.1", "", {}, "sha512-8aCohiDo4jwjOwma4FmYFd3i97urZulL8XL24nIPxuE+GZnfsAyy/g2Shqx6OjUiFKUXZM+Yy+KHnOmmA3FVcg=="],
-
- "gitconfiglocal": ["gitconfiglocal@2.1.0", "", { "dependencies": { "ini": "^1.3.2" } }, "sha512-qoerOEliJn3z+Zyn1HW2F6eoYJqKwS6MgC9cztTLUB/xLWX8gD/6T60pKn4+t/d6tP7JlybI7Z3z+I572CR/Vg=="],
-
- "glob": ["glob@13.0.6", "", { "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" } }, "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw=="],
-
- "glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
-
- "global-directory": ["global-directory@4.0.1", "", { "dependencies": { "ini": "4.1.1" } }, "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q=="],
-
- "globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="],
-
- "globby": ["globby@14.1.0", "", { "dependencies": { "@sindresorhus/merge-streams": "^2.1.0", "fast-glob": "^3.3.3", "ignore": "^7.0.3", "path-type": "^6.0.0", "slash": "^5.1.0", "unicorn-magic": "^0.3.0" } }, "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA=="],
-
- "gonzales-pe": ["gonzales-pe@4.3.0", "", { "dependencies": { "minimist": "^1.2.5" }, "bin": { "gonzales": "bin/gonzales.js" } }, "sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ=="],
-
- "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
-
- "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
-
- "h3": ["h3@1.15.11", "", { "dependencies": { "cookie-es": "^1.2.3", "crossws": "^0.3.5", "defu": "^6.1.6", "destr": "^2.0.5", "iron-webcrypto": "^1.2.1", "node-mock-http": "^1.0.4", "radix3": "^1.1.2", "ufo": "^1.6.3", "uncrypto": "^0.1.3" } }, "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg=="],
-
- "has-bigints": ["has-bigints@1.1.0", "", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="],
-
- "has-flag": ["has-flag@5.0.1", "", {}, "sha512-CsNUt5x9LUdx6hnk/E2SZLsDyvfqANZSUq4+D3D8RzDJ2M+HDTIkF60ibS1vHaK55vzgiZw1bEPFG9yH7l33wA=="],
-
- "has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="],
-
- "has-proto": ["has-proto@1.2.0", "", { "dependencies": { "dunder-proto": "^1.0.0" } }, "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ=="],
-
- "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
-
- "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
-
- "hasown": ["hasown@2.0.4", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A=="],
-
- "hosted-git-info": ["hosted-git-info@8.1.0", "", { "dependencies": { "lru-cache": "^10.0.1" } }, "sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw=="],
-
- "hot-shots": ["hot-shots@11.4.0", "", { "optionalDependencies": { "unix-dgram": "2.x" } }, "sha512-IoyXi0eBFLbaARl6IjQxNzxqNxH3h1XX026PP5uru/RwVVLOt9Qr9f87GW+4L/sj7sNyMGzmIsh++0z5ibkP5w=="],
-
- "html-entities": ["html-entities@2.6.0", "", {}, "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ=="],
-
- "htmlparser2": ["htmlparser2@10.1.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", "entities": "^7.0.1" } }, "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ=="],
-
- "http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="],
-
- "http-proxy": ["http-proxy@1.18.1", "", { "dependencies": { "eventemitter3": "^4.0.0", "follow-redirects": "^1.0.0", "requires-port": "^1.0.0" } }, "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ=="],
-
- "http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="],
-
- "http-proxy-middleware": ["http-proxy-middleware@3.0.7", "", { "dependencies": { "@types/http-proxy": "^1.17.15", "debug": "^4.3.6", "http-proxy": "^1.18.1", "is-glob": "^4.0.3", "is-plain-object": "^5.0.0", "micromatch": "^4.0.8" } }, "sha512-iwbQltVlx8bCrqePUM8C+hllHvdawVhQJaLrj1X7qllkvFQdXFsr16pW/mo9+JDVjN+QO2XUx9jd8SmoFkE5qw=="],
-
- "http-shutdown": ["http-shutdown@1.2.2", "", {}, "sha512-S9wWkJ/VSY9/k4qcjG318bqJNruzE4HySUhFYknwmu6LBP97KLLfwNf+n4V1BHurvFNkSKLFnK/RsuUnRTf9Vw=="],
-
- "https-proxy-agent": ["https-proxy-agent@8.0.0", "", { "dependencies": { "agent-base": "8.0.0", "debug": "^4.3.4" } }, "sha512-YYeW+iCnAS3xhvj2dvVoWgsbca3RfQy/IlaNHHOtDmU0jMqPI9euIq3Y9BJETdxk16h9NHHCKqp/KB9nIMStCQ=="],
-
- "human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="],
-
- "iconv-lite": ["iconv-lite@0.7.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ=="],
-
- "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
-
- "ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
-
- "image-meta": ["image-meta@0.2.2", "", {}, "sha512-3MOLanc3sb3LNGWQl1RlQlNWURE5g32aUphrDyFeCsxBTk08iE3VNe4CwsUZ0Qs1X+EfX0+r29Sxdpza4B+yRA=="],
-
- "image-size": ["image-size@2.0.2", "", { "bin": { "image-size": "bin/image-size.js" } }, "sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w=="],
-
- "import-in-the-middle": ["import-in-the-middle@3.3.0", "", { "dependencies": { "cjs-module-lexer": "^2.2.0", "es-module-lexer": "^2.2.0", "module-details-from-path": "^1.0.4" } }, "sha512-v4+k+PLt4z6g2ydEcXhfinTJxfgKJDWOW0v0GZNA5n2qToHFObpH4nGLhJdwnXnX4T14oREx/M62dS9GkpmSbQ=="],
-
- "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
-
- "indent-string": ["indent-string@5.0.0", "", {}, "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg=="],
-
- "index-to-position": ["index-to-position@1.2.0", "", {}, "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw=="],
-
- "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
-
- "ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="],
-
- "inquirer": ["inquirer@8.2.7", "", { "dependencies": { "@inquirer/external-editor": "^1.0.0", "ansi-escapes": "^4.2.1", "chalk": "^4.1.1", "cli-cursor": "^3.1.0", "cli-width": "^3.0.0", "figures": "^3.0.0", "lodash": "^4.17.21", "mute-stream": "0.0.8", "ora": "^5.4.1", "run-async": "^2.4.0", "rxjs": "^7.5.5", "string-width": "^4.1.0", "strip-ansi": "^6.0.0", "through": "^2.3.6", "wrap-ansi": "^6.0.1" } }, "sha512-UjOaSel/iddGZJ5xP/Eixh6dY1XghiBw4XK13rCCIJcJfyhhoul/7KhLLUGtebEj6GDYM6Vnx/mVsjx2L/mFIA=="],
-
- "inquirer-autocomplete-prompt": ["inquirer-autocomplete-prompt@1.4.0", "", { "dependencies": { "ansi-escapes": "^4.3.1", "chalk": "^4.0.0", "figures": "^3.2.0", "run-async": "^2.4.0", "rxjs": "^6.6.2" }, "peerDependencies": { "inquirer": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-qHgHyJmbULt4hI+kCmwX92MnSxDs/Yhdt4wPA30qnoa01OF6uTXV8yvH4hKXgdaTNmkZ9D01MHjqKYEuJN+ONw=="],
-
- "internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="],
-
- "ip-address": ["ip-address@10.2.0", "", {}, "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA=="],
-
- "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="],
-
- "ipx": ["ipx@3.1.1", "", { "dependencies": { "@fastify/accept-negotiator": "^2.0.1", "citty": "^0.1.6", "consola": "^3.4.2", "defu": "^6.1.4", "destr": "^2.0.5", "etag": "^1.8.1", "h3": "^1.15.3", "image-meta": "^0.2.1", "listhen": "^1.9.0", "ofetch": "^1.4.1", "pathe": "^2.0.3", "sharp": "^0.34.3", "svgo": "^4.0.0", "ufo": "^1.6.1", "unstorage": "^1.16.1", "xss": "^1.0.15" }, "bin": { "ipx": "bin/ipx.mjs" } }, "sha512-7Xnt54Dco7uYkfdAw0r2vCly3z0rSaVhEXMzPvl3FndsTVm5p26j+PO+gyinkYmcsEUvX2Rh7OGK7KzYWRu6BA=="],
-
- "iron-webcrypto": ["iron-webcrypto@1.2.1", "", {}, "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg=="],
-
- "is-array-buffer": ["is-array-buffer@3.0.5", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="],
-
- "is-async-function": ["is-async-function@2.1.1", "", { "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" } }, "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ=="],
-
- "is-bigint": ["is-bigint@1.1.0", "", { "dependencies": { "has-bigints": "^1.0.2" } }, "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ=="],
-
- "is-boolean-object": ["is-boolean-object@1.2.2", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A=="],
-
- "is-buffer": ["is-buffer@2.0.5", "", {}, "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ=="],
-
- "is-callable": ["is-callable@1.2.7", "", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="],
-
- "is-core-module": ["is-core-module@2.16.2", "", { "dependencies": { "hasown": "^2.0.3" } }, "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA=="],
-
- "is-data-view": ["is-data-view@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "is-typed-array": "^1.1.13" } }, "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw=="],
-
- "is-date-object": ["is-date-object@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg=="],
-
- "is-docker": ["is-docker@4.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-LHE+wROyG/Y/0ZnbktRCoTix2c1RhgWaZraMZ8o1Q7zCh0VSrICJQO5oqIIISrcSBtrXv0o233w1IYwsWCjTzA=="],
-
- "is-document.all": ["is-document.all@1.0.0", "", { "dependencies": { "call-bound": "^1.0.4" } }, "sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g=="],
-
- "is-error-instance": ["is-error-instance@2.0.0", "", {}, "sha512-5RuM+oFY0P5MRa1nXJo6IcTx9m2VyXYhRtb4h0olsi2GHci4bqZ6akHk+GmCYvDrAR9yInbiYdr2pnoqiOMw/Q=="],
-
- "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
-
- "is-finalizationregistry": ["is-finalizationregistry@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg=="],
-
- "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
-
- "is-generator-function": ["is-generator-function@1.1.2", "", { "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" } }, "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA=="],
-
- "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
-
- "is-in-ci": ["is-in-ci@1.0.0", "", { "bin": { "is-in-ci": "cli.js" } }, "sha512-eUuAjybVTHMYWm/U+vBO1sY/JOCgoPCXRxzdju0K+K0BiGW0SChEL1MLC0PoCIR1OlPo5YAp8HuQoUlsWEICwg=="],
-
- "is-in-ssh": ["is-in-ssh@1.0.0", "", {}, "sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw=="],
-
- "is-inside-container": ["is-inside-container@1.0.0", "", { "dependencies": { "is-docker": "^3.0.0" }, "bin": { "is-inside-container": "cli.js" } }, "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA=="],
-
- "is-installed-globally": ["is-installed-globally@1.0.0", "", { "dependencies": { "global-directory": "^4.0.1", "is-path-inside": "^4.0.0" } }, "sha512-K55T22lfpQ63N4KEN57jZUAaAYqYHEe8veb/TycJRk9DdSCLLcovXz/mL6mOnhQaZsQGwPhuFopdQIlqGSEjiQ=="],
-
- "is-interactive": ["is-interactive@1.0.0", "", {}, "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w=="],
-
- "is-map": ["is-map@2.0.3", "", {}, "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw=="],
-
- "is-negative-zero": ["is-negative-zero@2.0.3", "", {}, "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw=="],
-
- "is-network-error": ["is-network-error@1.3.2", "", {}, "sha512-PhBY86zaxNZUuWP6h13Vu5oFe0XY6/UlKzQnYFELzGVHygP3MxmvTfYSG7GN3aIab/iWudSMgjSnG9Dq+nHrgA=="],
-
- "is-node-process": ["is-node-process@1.2.0", "", {}, "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw=="],
-
- "is-npm": ["is-npm@6.1.0", "", {}, "sha512-O2z4/kNgyjhQwVR1Wpkbfc19JIhggF97NZNCpWTnjH7kVcZMUrnut9XSN7txI7VdyIYk5ZatOq3zvSuWpU8hoA=="],
-
- "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
-
- "is-number-object": ["is-number-object@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw=="],
-
- "is-path-inside": ["is-path-inside@4.0.0", "", {}, "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA=="],
-
- "is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="],
-
- "is-plain-object": ["is-plain-object@5.0.0", "", {}, "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="],
-
- "is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="],
-
- "is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="],
-
- "is-safe-filename": ["is-safe-filename@0.1.1", "", {}, "sha512-4SrR7AdnY11LHfDKTZY1u6Ga3RuxZdl3YKWWShO5iyuG5h8QS4GD2tOb04peBJ5I7pXbR+CGBNEhTcwK+FzN3g=="],
-
- "is-set": ["is-set@2.0.3", "", {}, "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg=="],
-
- "is-shared-array-buffer": ["is-shared-array-buffer@1.0.4", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A=="],
-
- "is-stream": ["is-stream@4.0.1", "", {}, "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A=="],
-
- "is-string": ["is-string@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA=="],
-
- "is-symbol": ["is-symbol@1.1.1", "", { "dependencies": { "call-bound": "^1.0.2", "has-symbols": "^1.1.0", "safe-regex-test": "^1.1.0" } }, "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w=="],
-
- "is-typed-array": ["is-typed-array@1.1.15", "", { "dependencies": { "which-typed-array": "^1.1.16" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="],
-
- "is-unicode-supported": ["is-unicode-supported@2.1.0", "", {}, "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ=="],
-
- "is-url-superb": ["is-url-superb@4.0.0", "", {}, "sha512-GI+WjezhPPcbM+tqE9LnmsY5qqjwHzTvjJ36wxYX5ujNXefSUJ/T17r5bqDV8yLhcgB59KTPNOc9O9cmHTPWsA=="],
-
- "is-weakmap": ["is-weakmap@2.0.2", "", {}, "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w=="],
-
- "is-weakref": ["is-weakref@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew=="],
-
- "is-weakset": ["is-weakset@2.0.4", "", { "dependencies": { "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ=="],
-
- "is-wsl": ["is-wsl@3.1.1", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw=="],
-
- "isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],
-
- "iserror": ["iserror@0.0.2", "", {}, "sha512-oKGGrFVaWwETimP3SiWwjDeY27ovZoyZPHtxblC4hCq9fXxed/jasx+ATWFFjCVSRZng8VTMsN1nDnGo6zMBSw=="],
-
- "isexe": ["isexe@4.0.0", "", {}, "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw=="],
-
- "jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="],
-
- "jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="],
-
- "jose": ["jose@5.9.6", "", {}, "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ=="],
-
- "jpeg-js": ["jpeg-js@0.4.4", "", {}, "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg=="],
-
- "js-image-generator": ["js-image-generator@1.0.4", "", { "dependencies": { "jpeg-js": "^0.4.2" } }, "sha512-ckb7kyVojGAnArouVR+5lBIuwU1fcrn7E/YYSd0FK7oIngAkMmRvHASLro9Zt5SQdWToaI66NybG+OGxPw/HlQ=="],
-
- "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
-
- "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
-
- "json-schema-ref-resolver": ["json-schema-ref-resolver@3.0.0", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-hOrZIVL5jyYFjzk7+y7n5JDzGlU8rfWDuYyHwGa2WA8/pcmMHezp2xsVwxrebD/Q9t8Nc5DboieySDpCp4WG4A=="],
-
- "json-schema-to-ts": ["json-schema-to-ts@1.6.4", "", { "dependencies": { "@types/json-schema": "^7.0.6", "ts-toolbelt": "^6.15.5" } }, "sha512-pR4yQ9DHz6itqswtHCm26mw45FSNfQ9rEQjosaZErhn5J3J2sIViQiz8rDaezjKAhFGpmsoczYVBgGHzFw/stA=="],
-
- "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
-
- "json-with-bigint": ["json-with-bigint@3.5.8", "", {}, "sha512-eq/4KP6K34kwa7TcFdtvnftvHCD9KvHOGGICWwMFc4dOOKF5t4iYqnfLK8otCRCRv06FXOzGGyqE8h8ElMvvdw=="],
-
- "jsonfile": ["jsonfile@6.2.1", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q=="],
-
- "jsonlines": ["jsonlines@0.1.1", "", {}, "sha512-ekDrAGso79Cvf+dtm+mL8OBI2bmAOt3gssYs833De/C9NmIpWDWyUO4zPgB5x2/OhY366dkhgfPMYfwZF7yOZA=="],
-
- "jsonpointer": ["jsonpointer@5.0.1", "", {}, "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ=="],
-
- "jsonwebtoken": ["jsonwebtoken@9.0.3", "", { "dependencies": { "jws": "^4.0.1", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", "lodash.isnumber": "^3.0.3", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^7.5.4" } }, "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g=="],
-
- "junk": ["junk@4.0.1", "", {}, "sha512-Qush0uP+G8ZScpGMZvHUiRfI0YBWuB3gVBYlI0v0vvOJt5FLicco+IkP0a50LqTTQhmts/m6tP5SWE+USyIvcQ=="],
-
- "jwa": ["jwa@2.0.1", "", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg=="],
-
- "jws": ["jws@4.0.1", "", { "dependencies": { "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA=="],
-
- "jwt-decode": ["jwt-decode@4.0.0", "", {}, "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA=="],
-
- "keep-func-props": ["keep-func-props@6.0.0", "", { "dependencies": { "mimic-fn": "^4.0.0" } }, "sha512-XDYA44ccm6W2MXZeQcDZykS5srkTpPf6Z59AEuOFbfuqdQ5TVxhAjxgzAEFBpr8XpsCEgr/XeCBFAmc9x6wRmQ=="],
-
- "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
-
- "kuler": ["kuler@2.0.0", "", {}, "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A=="],
-
- "ky": ["ky@1.14.3", "", {}, "sha512-9zy9lkjac+TR1c2tG+mkNSVlyOpInnWdSMiue4F+kq8TwJSgv6o8jhLRg8Ho6SnZ9wOYUq/yozts9qQCfk7bIw=="],
-
- "lambda-local": ["lambda-local@2.2.0", "", { "dependencies": { "commander": "^10.0.1", "dotenv": "^16.3.1", "winston": "^3.10.0" }, "bin": { "lambda-local": "build/cli.js" } }, "sha512-bPcgpIXbHnVGfI/omZIlgucDqlf4LrsunwoKue5JdZeGybt8L6KyJz2Zu19ffuZwIwLj2NAI2ZyaqNT6/cetcg=="],
-
- "latest-version": ["latest-version@9.0.0", "", { "dependencies": { "package-json": "^10.0.0" } }, "sha512-7W0vV3rqv5tokqkBAFV1LbR7HPOWzXQDpDgEuib/aJ1jsZZx6x3c2mBI+TJhJzOhkGeaLbCKEHXEXLfirtG2JA=="],
-
- "launder": ["launder@1.7.1", "", { "dependencies": { "dayjs": "^1.11.7" } }, "sha512-mU6WRz5EusL9ZZuiZ5SO4Y6C0P9PAUR9iwdb6bzj4KDihm28DiHFw+/yk9DBH4f+Pv1wuzQ4e2jV3oQ7mkIqvw=="],
-
- "lazystream": ["lazystream@1.0.1", "", { "dependencies": { "readable-stream": "^2.0.5" } }, "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw=="],
-
- "leven": ["leven@3.1.0", "", {}, "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A=="],
-
- "light-my-request": ["light-my-request@6.6.0", "", { "dependencies": { "cookie": "^1.0.1", "process-warning": "^4.0.0", "set-cookie-parser": "^2.6.0" } }, "sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A=="],
-
- "listhen": ["listhen@1.10.0", "", { "dependencies": { "@parcel/watcher": "^2.5.6", "@parcel/watcher-wasm": "^2.5.6", "citty": "^0.2.2", "consola": "^3.4.2", "crossws": ">=0.2.0 <0.5.0", "defu": "^6.1.7", "get-port-please": "^3.2.0", "h3": "^1.15.11", "http-shutdown": "^1.2.2", "jiti": "^2.6.1", "mlly": "^1.8.2", "node-forge": "^1.4.0", "pathe": "^2.0.3", "std-env": "^4.1.0", "tinyclip": "^0.1.12", "ufo": "^1.6.4", "untun": "^0.1.3", "uqr": "^0.1.3" }, "bin": { "listen": "bin/listhen.mjs", "listhen": "bin/listhen.mjs" } }, "sha512-kfz4C0OrC6IpaVMtYDJtf6PFjurxe9NBBoDAh/o2p587INryFOO4DQ9OetbCdDrWFt1m1CJKvYrzkGsuPHw8nQ=="],
-
- "locate-path": ["locate-path@8.0.0", "", { "dependencies": { "p-locate": "^6.0.0" } }, "sha512-XT9ewWAC43tiAV7xDAPflMkG0qOPn2QjHqlgX8FOqmWa/rxnyYDulF9T0F7tRy1u+TVTmK/M//6VIOye+2zDXg=="],
-
- "lodash": ["lodash@4.18.1", "", {}, "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q=="],
-
- "lodash.includes": ["lodash.includes@4.3.0", "", {}, "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="],
-
- "lodash.isboolean": ["lodash.isboolean@3.0.3", "", {}, "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="],
-
- "lodash.isempty": ["lodash.isempty@4.4.0", "", {}, "sha512-oKMuF3xEeqDltrGMfDxAPGIVMSSRv8tbRSODbrs4KGsRRLEhrW8N8Rd4DRgB2+621hY8A8XwwrTVhXWpxFvMzg=="],
-
- "lodash.isinteger": ["lodash.isinteger@4.0.4", "", {}, "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="],
-
- "lodash.isnumber": ["lodash.isnumber@3.0.3", "", {}, "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="],
-
- "lodash.isplainobject": ["lodash.isplainobject@4.0.6", "", {}, "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="],
-
- "lodash.isstring": ["lodash.isstring@4.0.1", "", {}, "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="],
-
- "lodash.once": ["lodash.once@4.1.1", "", {}, "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="],
-
- "lodash.transform": ["lodash.transform@4.6.0", "", {}, "sha512-LO37ZnhmBVx0GvOU/caQuipEh4GN82TcWv3yHlebGDgOxbxiwwzW5Pcx2AcvpIv2WmvmSMoC492yQFNhy/l/UQ=="],
-
- "log-process-errors": ["log-process-errors@11.0.1", "", { "dependencies": { "is-error-instance": "^2.0.0", "is-plain-obj": "^4.1.0", "normalize-exception": "^3.0.0", "set-error-message": "^2.0.1" } }, "sha512-HXYU83z3kH0VHfJgGyv9ZP9z7uNEayssgvpeQwSzh60mvpNqUBCPyXLSzCDSMxfGvAUUa0Kw06wJjVR46Ohd3A=="],
-
- "log-symbols": ["log-symbols@4.1.0", "", { "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" } }, "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg=="],
-
- "log-update": ["log-update@7.2.0", "", { "dependencies": { "ansi-escapes": "^7.3.0", "cli-cursor": "^5.0.0", "slice-ansi": "^8.0.0", "strip-ansi": "^7.2.0", "wrap-ansi": "^10.0.0" } }, "sha512-iLs7dGSyjZiUgvrUvuD3FndAxVJk+TywBkkkwUSm9HdYoskJalWg5qVsEiXeufPvRVPbCUmNQewg798rx+sPXg=="],
-
- "logform": ["logform@2.7.0", "", { "dependencies": { "@colors/colors": "1.6.0", "@types/triple-beam": "^1.3.2", "fecha": "^4.2.0", "ms": "^2.1.1", "safe-stable-stringify": "^2.3.1", "triple-beam": "^1.3.0" } }, "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ=="],
-
- "lru-cache": ["lru-cache@11.3.5", "", {}, "sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw=="],
-
- "luxon": ["luxon@3.7.2", "", {}, "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew=="],
-
- "macos-release": ["macos-release@3.5.1", "", {}, "sha512-Lci/1in+elqZ589PXnfP/iwZXpwQifTM94WJRQwG2tZSdfY7NfB/aUaTARHrohWCgHlXoabeaeXRDOnF5X9JQw=="],
-
- "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
-
- "make-error": ["make-error@1.3.6", "", {}, "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="],
-
- "map-obj": ["map-obj@5.0.2", "", {}, "sha512-K6K2NgKnTXimT3779/4KxSvobxOtMmx1LBZ3NwRxT/MDIR3Br/fQ4Q+WCX5QxjyUR8zg5+RV9Tbf2c5pAWTD2A=="],
-
- "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
-
- "maxstache": ["maxstache@1.0.7", "", {}, "sha512-53ZBxHrZM+W//5AcRVewiLpDunHnucfdzZUGz54Fnvo4tE+J3p8EL66kBrs2UhBXvYKTWckWYYWBqJqoTcenqg=="],
-
- "maxstache-stream": ["maxstache-stream@1.0.4", "", { "dependencies": { "maxstache": "^1.0.0", "pump": "^1.0.0", "split2": "^1.0.0", "through2": "^2.0.0" } }, "sha512-v8qlfPN0pSp7bdSoLo1NTjG43GXGqk5W2NWFnOCq2GlmFFqebGzPCjLKSbShuqIOVorOtZSAy7O/S1OCCRONUw=="],
-
- "mdn-data": ["mdn-data@2.27.1", "", {}, "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ=="],
-
- "media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="],
-
- "memoirist": ["memoirist@0.4.0", "", {}, "sha512-zxTgA0mSYELa66DimuNQDvyLq36AwDlTuVRbnQtB+VuTcKWm5Qc4z3WkSpgsFWHNhexqkIooqpv4hdcqrX5Nmg=="],
-
- "memoize-one": ["memoize-one@6.0.0", "", {}, "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="],
-
- "merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="],
-
- "merge-options": ["merge-options@3.0.4", "", { "dependencies": { "is-plain-obj": "^2.1.0" } }, "sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ=="],
-
- "merge-stream": ["merge-stream@2.0.0", "", {}, "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="],
-
- "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
-
- "micro": ["micro@9.3.5-canary.3", "", { "dependencies": { "arg": "4.1.0", "content-type": "1.0.4", "raw-body": "2.4.1" }, "bin": { "micro": "./bin/micro.js" } }, "sha512-viYIo9PefV+w9dvoIBh1gI44Mvx1BOk67B4BpC2QK77qdY0xZF0Q+vWLt/BII6cLkIc8rLmSIcJaB/OrXXKe1g=="],
-
- "micro-memoize": ["micro-memoize@5.1.1", "", { "dependencies": { "fast-equals": "^5.3.3", "fast-stringify": "^4.0.0" } }, "sha512-QDwluos8YeMijiKxZGwaV4f4tzj0soS6+xcsJhJ3+4wdEIHMyKbIKVUziebOgWX3e6yiijdoaHo+9tyhbnaWXA=="],
-
- "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
-
- "mime": ["mime@3.0.0", "", { "bin": { "mime": "cli.js" } }, "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A=="],
-
- "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
-
- "mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="],
-
- "mimic-fn": ["mimic-fn@4.0.0", "", {}, "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw=="],
-
- "mimic-function": ["mimic-function@5.0.1", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="],
-
- "miniflare": ["miniflare@4.20260701.0", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "sharp": "0.34.5", "undici": "7.28.0", "workerd": "1.20260701.1", "ws": "8.21.0", "youch": "4.1.0-beta.10" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-L6eAAi6IKtyb/7J6L+YsH2vb1yBrJWKRXI293JYDiMl70+6nncdAgigex58w6WBd+CwvdMsqOyNyGs95Op5gWQ=="],
-
- "minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="],
-
- "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
-
- "minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="],
-
- "minizlib": ["minizlib@3.1.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw=="],
-
- "mkdirp": ["mkdirp@1.0.4", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="],
-
- "mlly": ["mlly@1.8.2", "", { "dependencies": { "acorn": "^8.16.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.3" } }, "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA=="],
-
- "modern-tar": ["modern-tar@0.7.6", "", {}, "sha512-sweCIVXzx1aIGTCdzcMlSZt1h8k5Tmk08VNAuRk3IU28XamGiOH5ypi11g6De2CH7PhYqSSnGy2A/EFhbWnVKg=="],
-
- "module-definition": ["module-definition@6.0.2", "", { "dependencies": { "ast-module-types": "^6.0.1", "node-source-walk": "^7.0.1" }, "bin": { "module-definition": "bin/cli.js" } }, "sha512-SvAU3lB0+Yjbq55yHY3wkRZBOh+fhU1SnIF3IFbTewv6mtAh7yUT8ACHAJ2mGIJ7tCes2QuCL/cl6m0JSZ/ArA=="],
-
- "module-details-from-path": ["module-details-from-path@1.0.4", "", {}, "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w=="],
-
- "move-file": ["move-file@3.1.0", "", { "dependencies": { "path-exists": "^5.0.0" } }, "sha512-4aE3U7CCBWgrQlQDMq8da4woBWDGHioJFiOZ8Ie6Yq2uwYQ9V2kGhTz4x3u6Wc+OU17nw0yc3rJ/lQ4jIiPe3A=="],
-
- "mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="],
-
- "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
-
- "multiparty": ["multiparty@4.3.0", "", { "dependencies": { "http-errors": "2.0.0", "safe-buffer": "5.2.1", "uid-safe": "2.1.5" } }, "sha512-LD3YDFI9KrDoOGHsPM+hNraPDQQDPLe8Un/kvJfsZCsHKriA4mphg6Ctc2Cuup/59DtHMdAPm6ICXlUmhwTiug=="],
-
- "mute-stream": ["mute-stream@0.0.8", "", {}, "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA=="],
-
- "nan": ["nan@2.28.0", "", {}, "sha512-fTsDz99OTq2sVePhGdp4qQhggZFtKr64ZNVyVajRKtMOkJxYekplBh577PiJB12v/D3s2E5cGtOI45LWp6rnLQ=="],
-
- "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
-
- "nanospinner": ["nanospinner@1.2.2", "", { "dependencies": { "picocolors": "^1.1.1" } }, "sha512-Zt/AmG6qRU3e+WnzGGLuMCEAO/dAu45stNbHY223tUxldaDAeE+FxSPsd9Q+j+paejmm0ZbrNVs5Sraqy3dRxA=="],
-
- "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
-
- "netlify-cli": ["netlify-cli@26.1.0", "", { "dependencies": { "@fastify/static": "^9.1.1", "@netlify/ai": "^0.4.1", "@netlify/api": "^14.0.19", "@netlify/blobs": "^10.7.7", "@netlify/build": "^35.13.9", "@netlify/build-info": "^10.5.1", "@netlify/config": "^24.6.0", "@netlify/dev": "^4.18.4", "@netlify/dev-utils": "^4.4.3", "@netlify/edge-bundler": "^14.10.3", "@netlify/edge-functions": "^3.0.6", "@netlify/edge-functions-bootstrap": "^2.17.1", "@netlify/headers-parser": "^9.0.3", "@netlify/images": "^1.3.7", "@netlify/local-functions-proxy": "^2.0.3", "@netlify/redirect-parser": "^15.0.4", "@netlify/zip-it-and-ship-it": "^14.7.0", "@octokit/rest": "^22.0.0", "@opentelemetry/api": "^1.8.0", "@pnpm/tabtab": "^0.5.4", "ansi-escapes": "^7.3.0", "ansi-to-html": "^0.7.2", "ascii-table": "^0.0.9", "backoff": "^2.5.0", "boxen": "^8.0.1", "chalk": "^5.6.2", "chokidar": "^4.0.3", "ci-info": "^4.4.0", "clean-deep": "^3.4.0", "commander": "^12.1.0", "comment-json": "^4.6.2", "content-type": "^1.0.5", "cookie": "^1.1.1", "cron-parser": "^4.9.0", "debug": "^4.4.3", "decache": "^4.6.2", "dot-prop": "^10.1.0", "dotenv": "^17.3.1", "env-paths": "^4.0.0", "envinfo": "^7.21.0", "etag": "^1.8.1", "execa": "^5.1.1", "express": "^5.2.1", "express-logging": "^1.1.1", "fastest-levenshtein": "^1.0.16", "fastify": "^5.8.5", "find-up": "^8.0.0", "folder-walker": "^3.2.0", "fuzzy": "^0.1.3", "get-port": "^5.1.1", "git-repo-info": "^2.1.1", "gitconfiglocal": "^2.1.0", "http-proxy": "^1.18.1", "http-proxy-middleware": "^3.0.5", "https-proxy-agent": "^8.0.0", "inquirer": "^8.2.7", "inquirer-autocomplete-prompt": "^1.4.0", "is-docker": "^4.0.0", "is-stream": "^4.0.1", "is-wsl": "^3.1.0", "isexe": "^4.0.0", "jsonwebtoken": "^9.0.3", "jwt-decode": "^4.0.0", "lambda-local": "^2.2.0", "locate-path": "^8.0.0", "lodash": "^4.18.1", "log-update": "^7.2.0", "maxstache": "^1.0.7", "maxstache-stream": "^1.0.4", "modern-tar": "^0.7.5", "multiparty": "^4.2.3", "nanospinner": "^1.2.2", "netlify-redirector": "^0.5.0", "node-fetch": "^3.3.2", "normalize-package-data": "^7.0.1", "open": "^11.0.0", "p-filter": "^4.1.0", "p-map": "^7.0.3", "p-wait-for": "^6.0.0", "parallel-transform": "^1.2.0", "parse-duration": "^2.1.6", "parse-github-url": "^1.0.3", "pg": "^8.20.0", "prettyjson": "^1.2.5", "raw-body": "^3.0.1", "read-package-up": "^12.0.0", "readdirp": "^4.1.2", "semver": "^7.7.2", "source-map-support": "^0.5.21", "terminal-link": "^5.0.0", "toml": "^3.0.0", "tomlify-j0.4": "^3.0.0", "ulid": "^3.0.1", "update-notifier": "^7.3.1", "write-file-atomic": "^5.0.1", "ws": "^8.19.0", "yauzl": "^3.3.1" }, "bin": { "ntl": "bin/run.js", "netlify": "bin/run.js" } }, "sha512-Yq+x1xc/bHENhNUXUYjYfpgVf+4MinmoaHY0zaPQAQENWO4MrqPjHjbSnJYvTDiv2BPDQCmFgtEVBhguvr8/cg=="],
-
- "netlify-redirector": ["netlify-redirector@0.5.0", "", {}, "sha512-4zdzIP+6muqPCuE8avnrgDJ6KW/2+UpHTRcTbMXCIRxiRmyrX+IZ4WSJGZdHPWF3WmQpXpy603XxecZ9iygN7w=="],
-
- "netmask": ["netmask@2.1.1", "", {}, "sha512-eonl3sLUha+S1GzTPxychyhnUzKyeQkZ7jLjKrBagJgPla13F+uQ71HgpFefyHgqrjEbCPkDArxYsjY8/+gLKA=="],
-
- "node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="],
-
- "node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
-
- "node-exports-info": ["node-exports-info@1.6.2", "", { "dependencies": { "array.prototype.flatmap": "^1.3.3", "es-errors": "^1.3.0", "object.entries": "^1.1.9", "semver": "^6.3.1" } }, "sha512-kXs9Go0cah0qHVV2v389IXQLdLCeE1xfFtjOAF+iobu0OIoG1pje8At2vMHyaPMiPMnG/LWP50twML21eMcAag=="],
-
- "node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
-
- "node-fetch-native": ["node-fetch-native@1.6.7", "", {}, "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q=="],
-
- "node-forge": ["node-forge@1.4.0", "", {}, "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ=="],
-
- "node-gyp-build": ["node-gyp-build@4.8.4", "", { "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", "node-gyp-build-test": "build-test.js" } }, "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ=="],
-
- "node-mock-http": ["node-mock-http@1.0.4", "", {}, "sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ=="],
-
- "node-source-walk": ["node-source-walk@7.0.2", "", { "dependencies": { "@babel/parser": "^7.29.0" } }, "sha512-71kFFjYaSshDTA8/a2HiTYPLdASWjLJxUyJxGE+ffxU+KhxSBtM9kiLUX+R2yooFdSFKMFpi4n3PFtDy6qXv8A=="],
-
- "node-stream-zip": ["node-stream-zip@1.15.0", "", {}, "sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw=="],
-
- "nopt": ["nopt@8.1.0", "", { "dependencies": { "abbrev": "^3.0.0" }, "bin": { "nopt": "bin/nopt.js" } }, "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A=="],
-
- "normalize-exception": ["normalize-exception@3.0.0", "", { "dependencies": { "is-error-instance": "^2.0.0", "is-plain-obj": "^4.1.0" } }, "sha512-SMZtWSLjls45KBgwvS2jWyXLtOI9j90JyQ6tJstl91Gti4W7QwZyF/nWwlFRz/Cx4Gy70DAtLT0EzXYXcPJJUw=="],
-
- "normalize-package-data": ["normalize-package-data@7.0.1", "", { "dependencies": { "hosted-git-info": "^8.0.0", "semver": "^7.3.5", "validate-npm-package-license": "^3.0.4" } }, "sha512-linxNAT6M0ebEYZOx2tO6vBEFsVgnPpv+AVjk0wJHfaUIbq31Jm3T6vvZaarnOeWDh8ShnwXuaAyM7WT3RzErA=="],
-
- "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
-
- "npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="],
-
- "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="],
-
- "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="],
-
- "object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="],
-
- "object.assign": ["object.assign@4.1.7", "", { "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" } }, "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw=="],
-
- "object.entries": ["object.entries@1.1.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-object-atoms": "^1.1.1" } }, "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw=="],
-
- "ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="],
-
- "omit.js": ["omit.js@2.0.2", "", {}, "sha512-hJmu9D+bNB40YpL9jYebQl4lsTW6yEHRTroJzNLqQJYHm7c+NQnJGfZmIWh8S3q3KoaxV1aLhV6B3+0N0/kyJg=="],
-
- "on-exit-leak-free": ["on-exit-leak-free@2.1.2", "", {}, "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA=="],
-
- "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="],
-
- "on-headers": ["on-headers@1.1.0", "", {}, "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A=="],
-
- "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
-
- "one-time": ["one-time@1.0.0", "", { "dependencies": { "fn.name": "1.x.x" } }, "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g=="],
-
- "onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="],
-
- "open": ["open@11.0.0", "", { "dependencies": { "default-browser": "^5.4.0", "define-lazy-prop": "^3.0.0", "is-in-ssh": "^1.0.0", "is-inside-container": "^1.0.0", "powershell-utils": "^0.1.0", "wsl-utils": "^0.3.0" } }, "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw=="],
-
- "openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="],
-
- "ora": ["ora@5.4.1", "", { "dependencies": { "bl": "^4.1.0", "chalk": "^4.1.0", "cli-cursor": "^3.1.0", "cli-spinners": "^2.5.0", "is-interactive": "^1.0.0", "is-unicode-supported": "^0.1.0", "log-symbols": "^4.1.0", "strip-ansi": "^6.0.0", "wcwidth": "^1.0.1" } }, "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ=="],
-
- "os-name": ["os-name@6.1.0", "", { "dependencies": { "macos-release": "^3.3.0", "windows-release": "^6.1.0" } }, "sha512-zBd1G8HkewNd2A8oQ8c6BN/f/c9EId7rSUueOLGu28govmUctXmM+3765GwsByv9nYUdrLqHphXlYIc86saYsg=="],
-
- "os-paths": ["os-paths@4.4.0", "", {}, "sha512-wrAwOeXp1RRMFfQY8Sy7VaGVmPocaLwSFOYCGKSyo8qmJ+/yaafCl5BCA1IQZWqFSRBrKDYFeR9d/VyQzfH/jg=="],
-
- "own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="],
-
- "oxc-transform": ["oxc-transform@0.111.0", "", { "optionalDependencies": { "@oxc-transform/binding-android-arm-eabi": "0.111.0", "@oxc-transform/binding-android-arm64": "0.111.0", "@oxc-transform/binding-darwin-arm64": "0.111.0", "@oxc-transform/binding-darwin-x64": "0.111.0", "@oxc-transform/binding-freebsd-x64": "0.111.0", "@oxc-transform/binding-linux-arm-gnueabihf": "0.111.0", "@oxc-transform/binding-linux-arm-musleabihf": "0.111.0", "@oxc-transform/binding-linux-arm64-gnu": "0.111.0", "@oxc-transform/binding-linux-arm64-musl": "0.111.0", "@oxc-transform/binding-linux-ppc64-gnu": "0.111.0", "@oxc-transform/binding-linux-riscv64-gnu": "0.111.0", "@oxc-transform/binding-linux-riscv64-musl": "0.111.0", "@oxc-transform/binding-linux-s390x-gnu": "0.111.0", "@oxc-transform/binding-linux-x64-gnu": "0.111.0", "@oxc-transform/binding-linux-x64-musl": "0.111.0", "@oxc-transform/binding-openharmony-arm64": "0.111.0", "@oxc-transform/binding-wasm32-wasi": "0.111.0", "@oxc-transform/binding-win32-arm64-msvc": "0.111.0", "@oxc-transform/binding-win32-ia32-msvc": "0.111.0", "@oxc-transform/binding-win32-x64-msvc": "0.111.0" } }, "sha512-oa5KKSDNLHZGaiqIGAbCWXeN9IJUAz9MElWcQX90epDxdKc9Hrt/BsLj3K4gDqfAYa5dwdH+ZCFJG9hR74fiGg=="],
-
- "p-event": ["p-event@6.0.1", "", { "dependencies": { "p-timeout": "^6.1.2" } }, "sha512-Q6Bekk5wpzW5qIyUP4gdMEujObYstZl6DMMOSenwBvV0BlE5LkDwkjs5yHbZmdCEq2o4RJx4tE1vwxFVf2FG1w=="],
-
- "p-filter": ["p-filter@4.1.0", "", { "dependencies": { "p-map": "^7.0.1" } }, "sha512-37/tPdZ3oJwHaS3gNJdenCDB3Tz26i9sjhnguBtvN0vYlRIiDNnvTWkuh+0hETV9rLPdJ3rlL3yVOYPIAnM8rw=="],
-
- "p-finally": ["p-finally@2.0.1", "", {}, "sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw=="],
-
- "p-limit": ["p-limit@4.0.0", "", { "dependencies": { "yocto-queue": "^1.0.0" } }, "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ=="],
-
- "p-locate": ["p-locate@6.0.0", "", { "dependencies": { "p-limit": "^4.0.0" } }, "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw=="],
-
- "p-map": ["p-map@7.0.5", "", {}, "sha512-e8vJF4XdVkzqqSHguEMz41mQO1wKwxKm5ENrUJQUu9kLDCtn83cxbyHZcszr4QC5zEA7WffRRC4gsTecC7J9oA=="],
-
- "p-reduce": ["p-reduce@3.0.0", "", {}, "sha512-xsrIUgI0Kn6iyDYm9StOpOeK29XM1aboGji26+QEortiFST1hGZaUQOLhtEbqHErPpGW/aSz6allwK2qcptp0Q=="],
-
- "p-retry": ["p-retry@6.2.1", "", { "dependencies": { "@types/retry": "0.12.2", "is-network-error": "^1.0.0", "retry": "^0.13.1" } }, "sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ=="],
-
- "p-timeout": ["p-timeout@6.1.4", "", {}, "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg=="],
-
- "p-wait-for": ["p-wait-for@6.0.0", "", {}, "sha512-2kKzMtjS8TVcpCOU/gr3vZ4K/WIyS1AsEFXFWapM/0lERCdyTbB6ZeuCIp+cL1aeLZfQoMdZFCBTHiK4I9UtOw=="],
-
- "pac-proxy-agent": ["pac-proxy-agent@7.2.0", "", { "dependencies": { "@tootallnate/quickjs-emscripten": "^0.23.0", "agent-base": "^7.1.2", "debug": "^4.3.4", "get-uri": "^6.0.1", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.6", "pac-resolver": "^7.0.1", "socks-proxy-agent": "^8.0.5" } }, "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA=="],
-
- "pac-resolver": ["pac-resolver@7.0.1", "", { "dependencies": { "degenerator": "^5.0.0", "netmask": "^2.0.2" } }, "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg=="],
-
- "package-directory": ["package-directory@8.2.0", "", { "dependencies": { "find-up-simple": "^1.0.0" } }, "sha512-qJSu5Mo6tHmRxCy2KCYYKYgcfBdUpy9dwReaZD/xwf608AUk/MoRtIOWzgDtUeGeC7n/55yC3MI1Q+MbSoektw=="],
-
- "package-json": ["package-json@10.0.1", "", { "dependencies": { "ky": "^1.2.0", "registry-auth-token": "^5.0.2", "registry-url": "^6.0.1", "semver": "^7.6.0" } }, "sha512-ua1L4OgXSBdsu1FPb7F3tYH0F48a6kxvod4pLUlGY9COeJAJQNX/sNH2IiEmsxw7lqYiAwrdHMjz1FctOsyDQg=="],
-
- "package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="],
-
- "parallel-transform": ["parallel-transform@1.2.0", "", { "dependencies": { "cyclist": "^1.0.1", "inherits": "^2.0.3", "readable-stream": "^2.1.5" } }, "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg=="],
-
- "parse-duration": ["parse-duration@2.1.6", "", {}, "sha512-1/A2Exg3NcJGcYdgV/dn4frR7vO2hOW/ohQ4KIgbT4W3raVcpYSszPWiL6I6cKufi4jQM5NbGRXLBj8AoLM4iQ=="],
-
- "parse-github-url": ["parse-github-url@1.0.4", "", { "bin": { "parse-github-url": "cli.js" } }, "sha512-CEtCOt55fHmd6DpBc/N7H5NC4vJpcquhzzs9Iw2mRj8bVxo1O5TQI5MXKOMO7+yBOqD+5dKCCRK4Kj1KskZc6Q=="],
-
- "parse-gitignore": ["parse-gitignore@2.0.0", "", {}, "sha512-RmVuCHWsfu0QPNW+mraxh/xjQVw/lhUCUru8Zni3Ctq3AoMhpDTq0OVdKS6iesd6Kqb7viCV3isAL43dciOSog=="],
-
- "parse-imports": ["parse-imports@2.2.1", "", { "dependencies": { "es-module-lexer": "^1.5.3", "slashes": "^3.0.12" } }, "sha512-OL/zLggRp8mFhKL0rNORUTR4yBYujK/uU+xZL+/0Rgm2QE4nLO9v8PzEweSJEbMGKmDRjJE4R3IMJlL2di4JeQ=="],
-
- "parse-json": ["parse-json@8.3.0", "", { "dependencies": { "@babel/code-frame": "^7.26.2", "index-to-position": "^1.1.0", "type-fest": "^4.39.1" } }, "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ=="],
-
- "parse-ms": ["parse-ms@4.0.0", "", {}, "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw=="],
-
- "parse-srcset": ["parse-srcset@1.0.2", "", {}, "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q=="],
-
- "parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="],
-
- "parse5-htmlparser2-tree-adapter": ["parse5-htmlparser2-tree-adapter@7.1.0", "", { "dependencies": { "domhandler": "^5.0.3", "parse5": "^7.0.0" } }, "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g=="],
-
- "parse5-parser-stream": ["parse5-parser-stream@7.1.2", "", { "dependencies": { "parse5": "^7.0.0" } }, "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow=="],
-
- "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="],
-
- "path-browserify": ["path-browserify@1.0.1", "", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="],
-
- "path-exists": ["path-exists@5.0.0", "", {}, "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ=="],
-
- "path-expression-matcher": ["path-expression-matcher@1.5.0", "", {}, "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ=="],
-
- "path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
-
- "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="],
-
- "path-scurry": ["path-scurry@2.0.2", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg=="],
-
- "path-to-regexp": ["path-to-regexp@6.3.0", "", {}, "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ=="],
-
- "path-to-regexp-updated": ["path-to-regexp@6.3.0", "", {}, "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ=="],
-
- "path-type": ["path-type@6.0.0", "", {}, "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ=="],
-
- "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
-
- "pend": ["pend@1.2.0", "", {}, "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="],
-
- "pg": ["pg@8.22.0", "", { "dependencies": { "pg-connection-string": "^2.14.0", "pg-pool": "^3.14.0", "pg-protocol": "^1.15.0", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "optionalDependencies": { "pg-cloudflare": "^1.4.0" }, "peerDependencies": { "pg-native": ">=3.0.1" }, "optionalPeers": ["pg-native"] }, "sha512-8wih1vVIBMxoUM2oB4soJsD9tDnDpLv4OXBJ+EJzFsvycD+lfyIreC2gGHq78f8jbLLt+bvlPTFdFZfJkOuzAA=="],
-
- "pg-cloudflare": ["pg-cloudflare@1.4.0", "", {}, "sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A=="],
-
- "pg-connection-string": ["pg-connection-string@2.14.0", "", {}, "sha512-XwWDGcLRGCXAR8F/AM5bG7Q+A3Wm2s6QeEjlOKZLlH3UYcguiqCWKyWXVag5TLTIjR7oOJUY8kcADaZgWPyLeg=="],
-
- "pg-gateway": ["pg-gateway@0.3.0-beta.4", "", {}, "sha512-CTjsM7Z+0Nx2/dyZ6r8zRsc3f9FScoD5UAOlfUx1Fdv/JOIWvRbF7gou6l6vP+uypXQVoYPgw8xZDXgMGvBa4Q=="],
-
- "pg-int8": ["pg-int8@1.0.1", "", {}, "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="],
-
- "pg-pool": ["pg-pool@3.14.0", "", { "peerDependencies": { "pg": ">=8.0" } }, "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw=="],
-
- "pg-protocol": ["pg-protocol@1.15.0", "", {}, "sha512-cq9sECI5s0+uPUXjbz8ioyPJni6RzsRib0US67i5IoTZKw8fNeYlVE7u8F4dG7vEJJtc5wdD1K189lCCUwqWTQ=="],
-
- "pg-types": ["pg-types@2.2.0", "", { "dependencies": { "pg-int8": "1.0.1", "postgres-array": "~2.0.0", "postgres-bytea": "~1.0.0", "postgres-date": "~1.0.4", "postgres-interval": "^1.1.0" } }, "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA=="],
-
- "pgpass": ["pgpass@1.0.5", "", { "dependencies": { "split2": "^4.1.0" } }, "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug=="],
-
- "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
-
- "picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
-
- "picoquery": ["picoquery@2.5.0", "", {}, "sha512-j1kgOFxtaCyoFCkpoYG2Oj3OdGakadO7HZ7o5CqyRazlmBekKhbDoUnNnXASE07xSY4nDImWZkrZv7toSxMi/g=="],
-
- "pino": ["pino@10.3.1", "", { "dependencies": { "@pinojs/redact": "^0.4.0", "atomic-sleep": "^1.0.0", "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "^3.0.0", "pino-std-serializers": "^7.0.0", "process-warning": "^5.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", "sonic-boom": "^4.0.1", "thread-stream": "^4.0.0" }, "bin": { "pino": "bin.js" } }, "sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg=="],
-
- "pino-abstract-transport": ["pino-abstract-transport@3.0.0", "", { "dependencies": { "split2": "^4.0.0" } }, "sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg=="],
-
- "pino-std-serializers": ["pino-std-serializers@7.1.0", "", {}, "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw=="],
-
- "pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="],
-
- "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="],
-
- "postcss": ["postcss@8.5.9", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw=="],
-
- "postcss-values-parser": ["postcss-values-parser@6.0.2", "", { "dependencies": { "color-name": "^1.1.4", "is-url-superb": "^4.0.0", "quote-unquote": "^1.0.0" }, "peerDependencies": { "postcss": "^8.2.9" } }, "sha512-YLJpK0N1brcNJrs9WatuJFtHaV9q5aAOj+S4DI5S7jgHlRfm0PIbDCAFRYMQD5SHq7Fy6xsDhyutgS0QOAs0qw=="],
-
- "postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="],
-
- "postgres-bytea": ["postgres-bytea@1.0.1", "", {}, "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ=="],
-
- "postgres-date": ["postgres-date@1.0.7", "", {}, "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="],
-
- "postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="],
-
- "powershell-utils": ["powershell-utils@0.1.0", "", {}, "sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A=="],
-
- "precinct": ["precinct@12.3.2", "", { "dependencies": { "@dependents/detective-less": "^5.0.3", "commander": "^12.1.0", "detective-amd": "^6.1.0", "detective-cjs": "^6.1.1", "detective-es6": "^5.0.2", "detective-postcss": "^8.0.3", "detective-sass": "^6.0.2", "detective-scss": "^5.0.2", "detective-stylus": "^5.0.1", "detective-typescript": "^14.1.2", "detective-vue2": "^2.3.0", "module-definition": "^6.0.2", "node-source-walk": "^7.0.2", "postcss": "^8.5.14", "typescript": "^5.9.3" }, "bin": { "precinct": "bin/cli.js" } }, "sha512-JbJevI1K80z8e/WIyDt/4vUN/4qcfBSKKqOjJA4mosPPPb7zODKRJQV7YN7apVWN3k58nZYm/vEsLgEGYmnxwg=="],
-
- "precond": ["precond@0.2.3", "", {}, "sha512-QCYG84SgGyGzqJ/vlMsxeXd/pgL/I94ixdNFyh1PusWmTCyVfPJjZ1K1jvHtsbfnXQs2TSkEP2fR7QiMZAnKFQ=="],
-
- "pretty-ms": ["pretty-ms@9.3.0", "", { "dependencies": { "parse-ms": "^4.0.0" } }, "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ=="],
-
- "prettyjson": ["prettyjson@1.2.5", "", { "dependencies": { "colors": "1.4.0", "minimist": "^1.2.0" }, "bin": { "prettyjson": "bin/prettyjson" } }, "sha512-rksPWtoZb2ZpT5OVgtmy0KHVM+Dca3iVwWY9ifwhcexfjebtgjg3wmrUt9PvJ59XIYBcknQeYHD8IAnVlh9lAw=="],
-
- "process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="],
-
- "process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="],
-
- "process-warning": ["process-warning@5.0.0", "", {}, "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA=="],
-
- "promisepipe": ["promisepipe@3.0.0", "", {}, "sha512-V6TbZDJ/ZswevgkDNpGt/YqNCiZP9ASfgU+p83uJE6NrGtvSGoOcHLiDCqkMs2+yg7F5qHdLV8d0aS8O26G/KA=="],
-
- "proto-list": ["proto-list@1.2.4", "", {}, "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA=="],
-
- "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
-
- "proxy-agent": ["proxy-agent@6.4.0", "", { "dependencies": { "agent-base": "^7.0.2", "debug": "^4.3.4", "http-proxy-agent": "^7.0.1", "https-proxy-agent": "^7.0.3", "lru-cache": "^7.14.1", "pac-proxy-agent": "^7.0.1", "proxy-from-env": "^1.1.0", "socks-proxy-agent": "^8.0.2" } }, "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ=="],
-
- "proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="],
-
- "ps-list": ["ps-list@8.1.1", "", {}, "sha512-OPS9kEJYVmiO48u/B9qneqhkMvgCxT+Tm28VCEJpheTpl8cJ0ffZRRNgS5mrQRTrX5yRTpaJ+hRDeefXYmmorQ=="],
-
- "pump": ["pump@1.0.3", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw=="],
-
- "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
-
- "pupa": ["pupa@3.3.0", "", { "dependencies": { "escape-goat": "^4.0.0" } }, "sha512-LjgDO2zPtoXP2wJpDjZrGdojii1uqO0cnwKoIoUzkfS98HDmbeiGmYiXo3lXeFlq2xvne1QFQhwYXSUCLKtEuA=="],
-
- "qs": ["qs@6.15.3", "", { "dependencies": { "es-define-property": "^1.0.1", "side-channel": "^1.1.1" } }, "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A=="],
-
- "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
-
- "quick-format-unescaped": ["quick-format-unescaped@4.0.4", "", {}, "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="],
-
- "quote-unquote": ["quote-unquote@1.0.0", "", {}, "sha512-twwRO/ilhlG/FIgYeKGFqyHhoEhqgnKVkcmqMKi2r524gz3ZbDTcyFt38E9xjJI2vT+KbRNHVbnJ/e0I25Azwg=="],
-
- "radix3": ["radix3@1.1.2", "", {}, "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA=="],
-
- "random-bytes": ["random-bytes@1.0.0", "", {}, "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ=="],
-
- "range-parser": ["range-parser@1.3.0", "", {}, "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw=="],
-
- "raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="],
-
- "rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="],
-
- "read-package-up": ["read-package-up@12.0.0", "", { "dependencies": { "find-up-simple": "^1.0.1", "read-pkg": "^10.0.0", "type-fest": "^5.2.0" } }, "sha512-Q5hMVBYur/eQNWDdbF4/Wqqr9Bjvtrw2kjGxxBbKLbx8bVCL8gcArjTy8zDUuLGQicftpMuU0riQNcAsbtOVsw=="],
-
- "read-pkg": ["read-pkg@9.0.1", "", { "dependencies": { "@types/normalize-package-data": "^2.4.3", "normalize-package-data": "^6.0.0", "parse-json": "^8.0.0", "type-fest": "^4.6.0", "unicorn-magic": "^0.1.0" } }, "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA=="],
-
- "readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
-
- "readdir-glob": ["readdir-glob@1.1.3", "", { "dependencies": { "minimatch": "^5.1.0" } }, "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA=="],
-
- "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
-
- "real-require": ["real-require@0.2.0", "", {}, "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg=="],
-
- "reflect.getprototypeof": ["reflect.getprototypeof@1.0.10", "", { "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" } }, "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw=="],
-
- "regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "", { "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" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="],
-
- "registry-auth-token": ["registry-auth-token@5.1.1", "", { "dependencies": { "@pnpm/npm-conf": "^3.0.2" } }, "sha512-P7B4+jq8DeD2nMsAcdfaqHbssgHtZ7Z5+++a5ask90fvmJ8p5je4mOa+wzu+DB4vQ5tdJV/xywY+UnVFeQLV5Q=="],
-
- "registry-url": ["registry-url@6.0.1", "", { "dependencies": { "rc": "1.2.8" } }, "sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q=="],
-
- "remove-trailing-separator": ["remove-trailing-separator@1.1.0", "", {}, "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw=="],
-
- "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="],
-
- "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="],
-
- "require-in-the-middle": ["require-in-the-middle@8.0.1", "", { "dependencies": { "debug": "^4.3.5", "module-details-from-path": "^1.0.3" } }, "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ=="],
-
- "require-package-name": ["require-package-name@2.0.1", "", {}, "sha512-uuoJ1hU/k6M0779t3VMVIYpb2VMJk05cehCaABFhXaibcbvfgR8wKiozLjVFSzJPmQMRqIcO0HMyTFqfV09V6Q=="],
-
- "requires-port": ["requires-port@1.0.0", "", {}, "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="],
-
- "resolve": ["resolve@2.0.0-next.7", "", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.2", "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" } }, "sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ=="],
-
- "resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="],
-
- "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="],
-
- "resolve.exports": ["resolve.exports@2.0.3", "", {}, "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A=="],
-
- "restore-cursor": ["restore-cursor@3.1.0", "", { "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA=="],
-
- "ret": ["ret@0.5.0", "", {}, "sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw=="],
-
- "retry": ["retry@0.13.1", "", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="],
-
- "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
-
- "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="],
-
- "rolldown": ["rolldown@1.0.0-rc.1", "", { "dependencies": { "@oxc-project/types": "=0.110.0", "@rolldown/pluginutils": "1.0.0-rc.1" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-rc.1", "@rolldown/binding-darwin-arm64": "1.0.0-rc.1", "@rolldown/binding-darwin-x64": "1.0.0-rc.1", "@rolldown/binding-freebsd-x64": "1.0.0-rc.1", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.1", "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.1", "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.1", "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.1", "@rolldown/binding-linux-x64-musl": "1.0.0-rc.1", "@rolldown/binding-openharmony-arm64": "1.0.0-rc.1", "@rolldown/binding-wasm32-wasi": "1.0.0-rc.1", "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.1", "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.1" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-M3AeZjYE6UclblEf531Hch0WfVC/NOL43Cc+WdF3J50kk5/fvouHhDumSGTh0oRjbZ8C4faaVr5r6Nx1xMqDGg=="],
-
- "router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="],
-
- "rssbook": ["rssbook@workspace:pkgs/rssbook"],
-
- "run-applescript": ["run-applescript@7.1.0", "", {}, "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q=="],
-
- "run-async": ["run-async@2.4.1", "", {}, "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ=="],
-
- "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
-
- "rxjs": ["rxjs@7.8.2", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA=="],
-
- "safe-array-concat": ["safe-array-concat@1.1.4", "", { "dependencies": { "call-bind": "^1.0.9", "call-bound": "^1.0.4", "get-intrinsic": "^1.3.0", "has-symbols": "^1.1.0", "isarray": "^2.0.5" } }, "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg=="],
-
- "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
-
- "safe-json-stringify": ["safe-json-stringify@1.2.0", "", {}, "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg=="],
-
- "safe-push-apply": ["safe-push-apply@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "isarray": "^2.0.5" } }, "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA=="],
-
- "safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="],
-
- "safe-regex2": ["safe-regex2@5.1.1", "", { "dependencies": { "ret": "~0.5.0" }, "bin": { "safe-regex2": "bin/safe-regex2.js" } }, "sha512-mOSBvHGDZMuIEZMdOz/aCEYDCv0E7nfcNsIhUF+/P+xC7Hyf3FkvymqgPbg9D1EdSGu+uKbJgy09K/RKKc7kJA=="],
-
- "safe-stable-stringify": ["safe-stable-stringify@2.5.0", "", {}, "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA=="],
-
- "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
-
- "sandbox": ["sandbox@3.1.2", "", { "dependencies": { "@vercel/sandbox": "2.1.1", "async-retry": "1.3.3", "debug": "^4.4.1", "zod": "^4.1.1" }, "bin": { "sbx": "bin/sandbox.mjs", "sandbox": "bin/sandbox.mjs" } }, "sha512-g93rma0Z9Aa6EoTktzYnGZmQvMzm7j73BekJIMwmkdWR5uryZ+6hBCADtd3JKGAB27k+ubnG7HHsZqtscAMzIQ=="],
-
- "sanitize-html": ["sanitize-html@2.17.5", "", { "dependencies": { "deepmerge": "^4.2.2", "escape-string-regexp": "^4.0.0", "htmlparser2": "^10.1.0", "is-plain-object": "^5.0.0", "launder": "^1.7.1", "parse-srcset": "^1.0.2", "postcss": "^8.3.11" } }, "sha512-ZmU1joGRrvoyctKIiuwUxqR6moLoU2Wk+2bMccN6f7UwhAmwYDvWziqPxRDDN2Qip62NqnIrVrT9akbL6Wretg=="],
-
- "sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="],
-
- "secure-json-parse": ["secure-json-parse@4.1.0", "", {}, "sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA=="],
-
- "semver": ["semver@7.8.5", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="],
-
- "send": ["send@1.2.1", "", { "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" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="],
-
- "serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="],
-
- "set-cookie-parser": ["set-cookie-parser@2.7.2", "", {}, "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw=="],
-
- "set-error-message": ["set-error-message@2.0.1", "", { "dependencies": { "normalize-exception": "^3.0.0" } }, "sha512-s/eeP0f4ed1S3fl0KbxZoy5Pbeg5D6Nbple9nut4VPwHTvEIk5r7vKq0FwjNjszdUPdlTrs4GJCOkWUqWeTeWg=="],
-
- "set-function-length": ["set-function-length@1.2.2", "", { "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" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="],
-
- "set-function-name": ["set-function-name@2.0.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", "has-property-descriptors": "^1.0.2" } }, "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ=="],
-
- "set-proto": ["set-proto@1.0.0", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0" } }, "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw=="],
-
- "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="],
-
- "sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "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" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="],
-
- "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
-
- "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
-
- "side-channel": ["side-channel@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4", "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ=="],
-
- "side-channel-list": ["side-channel-list@1.0.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4" } }, "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w=="],
-
- "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="],
-
- "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "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" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="],
-
- "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
-
- "slash": ["slash@5.1.0", "", {}, "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg=="],
-
- "slashes": ["slashes@3.0.12", "", {}, "sha512-Q9VME8WyGkc7pJf6QEkj3wE+2CnvZMI+XJhwdTPR8Z/kWQRXi7boAWLDibRPyHRTUTPx5FaU7MsyrjI3yLB4HA=="],
-
- "slice-ansi": ["slice-ansi@8.0.0", "", { "dependencies": { "ansi-styles": "^6.2.3", "is-fullwidth-code-point": "^5.1.0" } }, "sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg=="],
-
- "smart-buffer": ["smart-buffer@4.2.0", "", {}, "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="],
-
- "smol-toml": ["smol-toml@1.5.2", "", {}, "sha512-QlaZEqcAH3/RtNyet1IPIYPsEWAaYyXXv1Krsi+1L/QHppjX4Ifm8MQsBISz9vE8cHicIq3clogsheili5vhaQ=="],
-
- "socks": ["socks@2.8.9", "", { "dependencies": { "ip-address": "^10.1.1", "smart-buffer": "^4.2.0" } }, "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw=="],
-
- "socks-proxy-agent": ["socks-proxy-agent@8.0.5", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "^4.3.4", "socks": "^2.8.3" } }, "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw=="],
-
- "sonic-boom": ["sonic-boom@4.2.1", "", { "dependencies": { "atomic-sleep": "^1.0.0" } }, "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q=="],
-
- "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
-
- "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
-
- "source-map-support": ["source-map-support@0.5.21", "", { "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w=="],
-
- "spdx-correct": ["spdx-correct@3.2.0", "", { "dependencies": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" } }, "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA=="],
-
- "spdx-exceptions": ["spdx-exceptions@2.5.0", "", {}, "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w=="],
-
- "spdx-expression-parse": ["spdx-expression-parse@3.0.1", "", { "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q=="],
-
- "spdx-license-ids": ["spdx-license-ids@3.0.23", "", {}, "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw=="],
-
- "split2": ["split2@1.1.1", "", { "dependencies": { "through2": "~2.0.0" } }, "sha512-cfurE2q8LamExY+lJ9Ex3ZfBwqAPduzOKVscPDXNCLLMvyaeD3DTz1yk7fVIs6Chco+12XeD0BB6HEoYzPYbXA=="],
-
- "srvx": ["srvx@0.11.15", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-iXsux0UcOjdvs0LCMa2Ws3WwcDUozA3JN3BquNXkaFPP7TpRqgunKdEgoZ/uwb1J6xaYHfxtz9Twlh6yzwM6Tg=="],
-
- "stack-generator": ["stack-generator@2.0.10", "", { "dependencies": { "stackframe": "^1.3.4" } }, "sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ=="],
-
- "stack-trace": ["stack-trace@0.0.10", "", {}, "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg=="],
-
- "stackframe": ["stackframe@1.3.4", "", {}, "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw=="],
-
- "stat-mode": ["stat-mode@0.3.0", "", {}, "sha512-QjMLR0A3WwFY2aZdV0okfFEJB5TRjkggXZjxP3A1RsWsNHNu3YPv8btmtc6iCFZ0Rul3FE93OYogvhOUClU+ng=="],
-
- "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="],
-
- "std-env": ["std-env@4.1.0", "", {}, "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ=="],
-
- "stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="],
-
- "stream-to-array": ["stream-to-array@2.3.0", "", { "dependencies": { "any-promise": "^1.1.0" } }, "sha512-UsZtOYEn4tWU2RGLOXr/o/xjRBftZRlG3dEWoaHr8j4GuypJ3isitGbVyjQKAuMu+xbiop8q224TjiZWc4XTZA=="],
-
- "stream-to-promise": ["stream-to-promise@2.2.0", "", { "dependencies": { "any-promise": "~1.3.0", "end-of-stream": "~1.1.0", "stream-to-array": "~2.3.0" } }, "sha512-HAGUASw8NT0k8JvIVutB2Y/9iBk7gpgEyAudXwNJmZERdMITGdajOa4VJfD/kNiA3TppQpTP4J+CtcHwdzKBAw=="],
-
- "streamx": ["streamx@2.28.0", "", { "dependencies": { "events-universal": "^1.0.0", "fast-fifo": "^1.3.2", "text-decoder": "^1.1.0" } }, "sha512-1Yowhzjf0ivGMrTIkY9hav5TxobO9qIVqUE41fiCGMGgc3CLlf4MY+9AHmZqBWgDTue0fY9zWjYFVyf6Diuobw=="],
-
- "string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
-
- "string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
-
- "string.prototype.trim": ["string.prototype.trim@1.2.11", "", { "dependencies": { "call-bind": "^1.0.9", "call-bound": "^1.0.4", "define-data-property": "^1.1.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.2", "es-object-atoms": "^1.1.2", "has-property-descriptors": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w=="],
-
- "string.prototype.trimend": ["string.prototype.trimend@1.0.10", "", { "dependencies": { "call-bind": "^1.0.9", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-object-atoms": "^1.1.2" } }, "sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw=="],
-
- "string.prototype.trimstart": ["string.prototype.trimstart@1.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg=="],
-
- "string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
-
- "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
-
- "strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
-
- "strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="],
-
- "strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="],
-
- "strnum": ["strnum@2.2.3", "", {}, "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg=="],
-
- "strtok3": ["strtok3@10.3.5", "", { "dependencies": { "@tokenizer/token": "^0.3.0" } }, "sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA=="],
-
- "stubborn-fs": ["stubborn-fs@2.0.0", "", { "dependencies": { "stubborn-utils": "^1.0.1" } }, "sha512-Y0AvSwDw8y+nlSNFXMm2g6L51rBGdAQT20J3YSOqxC53Lo3bjWRtr2BKcfYoAf352WYpsZSTURrA0tqhfgudPA=="],
-
- "stubborn-utils": ["stubborn-utils@1.0.2", "", {}, "sha512-zOh9jPYI+xrNOyisSelgym4tolKTJCQd5GBhK0+0xJvcYDcwlOoxF/rnFKQ2KRZknXSG9jWAp66fwP6AxN9STg=="],
-
- "supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="],
-
- "supports-hyperlinks": ["supports-hyperlinks@4.5.0", "", { "dependencies": { "has-flag": "^5.0.1", "supports-color": "^10.2.2" } }, "sha512-ZW2OvfeCXrNTbLakPUzjQG922EeGCOteFSVoek5DKStTh898wf7zgtuFlzQN8HfZCxC3Eh02yJVrRW51hADf+w=="],
-
- "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
-
- "svgo": ["svgo@4.0.1", "", { "dependencies": { "commander": "^11.1.0", "css-select": "^5.1.0", "css-tree": "^3.0.1", "css-what": "^6.1.0", "csso": "^5.0.5", "picocolors": "^1.1.1", "sax": "^1.5.0" }, "bin": "./bin/svgo.js" }, "sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w=="],
-
- "tagged-tag": ["tagged-tag@1.0.0", "", {}, "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng=="],
-
- "tar": ["tar@7.5.19", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-4LeEWl96twnS2Q7Bz4MGqgazLqO+hJN63GZxXoIqh1T3VweYD997gbU1ItNsQafqqXTXd5WFyFdReLtwvRBNiw=="],
-
- "tar-stream": ["tar-stream@3.1.7", "", { "dependencies": { "b4a": "^1.6.4", "fast-fifo": "^1.2.0", "streamx": "^2.15.0" } }, "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ=="],
-
- "terminal-link": ["terminal-link@5.0.0", "", { "dependencies": { "ansi-escapes": "^7.0.0", "supports-hyperlinks": "^4.1.0" } }, "sha512-qFAy10MTMwjzjU8U16YS4YoZD+NQLHzLssFMNqgravjbvIPNiqkGFR4yjhJfmY9R5OFU7+yHxc6y+uGHkKwLRA=="],
-
- "text-decoder": ["text-decoder@1.2.7", "", { "dependencies": { "b4a": "^1.6.4" } }, "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ=="],
-
- "text-hex": ["text-hex@1.0.0", "", {}, "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg=="],
-
- "thread-stream": ["thread-stream@4.2.0", "", { "dependencies": { "real-require": "^1.0.0" } }, "sha512-e2zZ96wSChazBsbENf/Pcm/4swHt2cEKQ92rhUjkL9GCKiTDJIaTBenjE/m9DXi0QBmTMDkFDdOomUy20A1tDQ=="],
-
- "throttleit": ["throttleit@2.1.0", "", {}, "sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw=="],
-
- "through": ["through@2.3.8", "", {}, "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="],
-
- "through2": ["through2@2.0.5", "", { "dependencies": { "readable-stream": "~2.3.6", "xtend": "~4.0.1" } }, "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ=="],
-
- "time-span": ["time-span@4.0.0", "", { "dependencies": { "convert-hrtime": "^3.0.0" } }, "sha512-MyqZCTGLDZ77u4k+jqg4UlrzPTPZ49NDlaekU6uuFaJLzPIN1woaRXCbGeqOfxwc3Y37ZROGAJ614Rdv7Olt+g=="],
-
- "tinyclip": ["tinyclip@0.1.15", "", {}, "sha512-uo33abH+Ays0xYaDysoBt494Hb3hsEczMpcC0MwFl773pazORx4fmvKhclhR1wonUbB6vvpRsvVMwnhfqeMc+A=="],
-
- "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="],
-
- "tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="],
-
- "tmp": ["tmp@0.2.7", "", {}, "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw=="],
-
- "tmp-promise": ["tmp-promise@3.0.3", "", { "dependencies": { "tmp": "^0.2.0" } }, "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ=="],
-
- "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
-
- "toad-cache": ["toad-cache@3.7.4", "", {}, "sha512-m1TdR/rvT7kgGJZhspNtXdsdYk0fddFpJJFlG5s+UkPFo6lkLoZ3YLOaovPYjq1R75NP5JfeTlSHaOsE09peCg=="],
-
- "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
-
- "token-types": ["token-types@6.1.2", "", { "dependencies": { "@borewit/text-codec": "^0.2.1", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww=="],
-
- "toml": ["toml@3.0.0", "", {}, "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w=="],
-
- "tomlify-j0.4": ["tomlify-j0.4@3.0.0", "", {}, "sha512-2Ulkc8T7mXJ2l0W476YC/A209PR38Nw8PuaCNtk9uI3t1zzFdGQeWYGQvmj2PZkVvRC/Yoi4xQKMRnWc/N29tQ=="],
-
- "tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
-
- "tree-kill": ["tree-kill@1.2.2", "", { "bin": { "tree-kill": "cli.js" } }, "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="],
-
- "triple-beam": ["triple-beam@1.4.1", "", {}, "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg=="],
-
- "ts-api-utils": ["ts-api-utils@2.5.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA=="],
-
- "ts-morph": ["ts-morph@12.0.0", "", { "dependencies": { "@ts-morph/common": "~0.11.0", "code-block-writer": "^10.1.1" } }, "sha512-VHC8XgU2fFW7yO1f/b3mxKDje1vmyzFXHWzOYmKEkCEwcLjDtbdLgBQviqj4ZwP4MJkQtRo6Ha2I29lq/B+VxA=="],
-
- "ts-node": ["ts-node@10.9.2", "", { "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", "@tsconfig/node12": "^1.0.7", "@tsconfig/node14": "^1.0.0", "@tsconfig/node16": "^1.0.2", "acorn": "^8.4.1", "acorn-walk": "^8.1.1", "arg": "^4.1.0", "create-require": "^1.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", "v8-compile-cache-lib": "^3.0.1", "yn": "3.1.1" }, "peerDependencies": { "@swc/core": ">=1.2.50", "@swc/wasm": ">=1.2.50", "@types/node": "*", "typescript": ">=2.7" }, "optionalPeers": ["@swc/core", "@swc/wasm"], "bin": { "ts-node": "dist/bin.js", "ts-script": "dist/bin-script-deprecated.js", "ts-node-cwd": "dist/bin-cwd.js", "ts-node-esm": "dist/bin-esm.js", "ts-node-script": "dist/bin-script.js", "ts-node-transpile-only": "dist/bin-transpile.js" } }, "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ=="],
-
- "ts-toolbelt": ["ts-toolbelt@6.15.5", "", {}, "sha512-FZIXf1ksVyLcfr7M317jbB67XFJhOO1YqdTcuGaq9q5jLUoTikukZ+98TPjKiP2jC5CgmYdWWYs0s2nLSU0/1A=="],
-
- "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
-
- "tsx": ["tsx@4.23.0", "", { "dependencies": { "esbuild": "~0.28.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-eUdUIaCr963q2h5u3+QwvYp0+eqPvn+egeqZUm0hwERCqqx1E3kK5ehbGCvqSE5MQAULr67ww0cA3jKc3YkM1w=="],
-
- "type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="],
-
- "type-is": ["type-is@2.1.0", "", { "dependencies": { "content-type": "^2.0.0", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA=="],
-
- "typed-array-buffer": ["typed-array-buffer@1.0.3", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="],
-
- "typed-array-byte-length": ["typed-array-byte-length@1.0.3", "", { "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" } }, "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg=="],
-
- "typed-array-byte-offset": ["typed-array-byte-offset@1.0.4", "", { "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" } }, "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ=="],
-
- "typed-array-length": ["typed-array-length@1.0.8", "", { "dependencies": { "call-bind": "^1.0.9", "for-each": "^0.3.5", "gopd": "^1.2.0", "is-typed-array": "^1.1.15", "possible-typed-array-names": "^1.1.0", "reflect.getprototypeof": "^1.0.10" } }, "sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g=="],
-
- "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
-
- "ufo": ["ufo@1.6.3", "", {}, "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q=="],
-
- "uid-promise": ["uid-promise@1.0.0", "", {}, "sha512-R8375j0qwXyIu/7R0tjdF06/sElHqbmdmWC9M2qQHpEVbvE4I5+38KJI7LUUmQMp7NVq4tKHiBMkT0NFM453Ig=="],
-
- "uid-safe": ["uid-safe@2.1.5", "", { "dependencies": { "random-bytes": "~1.0.0" } }, "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA=="],
-
- "uint8array-extras": ["uint8array-extras@1.5.0", "", {}, "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A=="],
-
- "ulid": ["ulid@3.0.2", "", { "bin": { "ulid": "dist/cli.js" } }, "sha512-yu26mwteFYzBAot7KVMqFGCVpsF6g8wXfJzQUHvu1no3+rRRSFcSV2nKeYvNPLD2J4b08jYBDhHUjeH0ygIl9w=="],
-
- "unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="],
-
- "uncrypto": ["uncrypto@0.1.3", "", {}, "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q=="],
-
- "undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="],
-
- "undici-types": ["undici-types@7.19.2", "", {}, "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg=="],
-
- "unenv": ["unenv@2.0.0-rc.24", "", { "dependencies": { "pathe": "^2.0.3" } }, "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw=="],
-
- "unicorn-magic": ["unicorn-magic@0.3.0", "", {}, "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA=="],
-
- "universal-user-agent": ["universal-user-agent@7.0.3", "", {}, "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A=="],
-
- "universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="],
-
- "unix-dgram": ["unix-dgram@2.0.7", "", { "dependencies": { "bindings": "^1.5.0", "nan": "^2.20.0" } }, "sha512-pWaQorcdxEUBFIKjCqqIlQaOoNVmchyoaNAJ/1LwyyfK2XSxcBhgJNiSE8ZRhR0xkNGyk4xInt1G03QPoKXY5A=="],
-
- "unixify": ["unixify@1.0.0", "", { "dependencies": { "normalize-path": "^2.1.1" } }, "sha512-6bc58dPYhCMHHuwxldQxO3RRNZ4eCogZ/st++0+fcC1nr0jiGUtAdBJ2qzmLQWSxbtz42pWt4QQMiZ9HvZf5cg=="],
-
- "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
-
- "unstorage": ["unstorage@1.17.5", "", { "dependencies": { "anymatch": "^3.1.3", "chokidar": "^5.0.0", "destr": "^2.0.5", "h3": "^1.15.10", "lru-cache": "^11.2.7", "node-fetch-native": "^1.6.7", "ofetch": "^1.5.1", "ufo": "^1.6.3" }, "peerDependencies": { "@azure/app-configuration": "^1.8.0", "@azure/cosmos": "^4.2.0", "@azure/data-tables": "^13.3.0", "@azure/identity": "^4.6.0", "@azure/keyvault-secrets": "^4.9.0", "@azure/storage-blob": "^12.26.0", "@capacitor/preferences": "^6 || ^7 || ^8", "@deno/kv": ">=0.9.0", "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.34.3", "@vercel/blob": ">=0.27.1", "@vercel/functions": "^2.2.12 || ^3.0.0", "@vercel/kv": "^1 || ^2 || ^3", "aws4fetch": "^1.0.20", "db0": ">=0.2.1", "idb-keyval": "^6.2.1", "ioredis": "^5.4.2", "uploadthing": "^7.4.4" }, "optionalPeers": ["@azure/app-configuration", "@azure/cosmos", "@azure/data-tables", "@azure/identity", "@azure/keyvault-secrets", "@azure/storage-blob", "@capacitor/preferences", "@deno/kv", "@netlify/blobs", "@planetscale/database", "@upstash/redis", "@vercel/blob", "@vercel/functions", "@vercel/kv", "aws4fetch", "db0", "idb-keyval", "ioredis", "uploadthing"] }, "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg=="],
-
- "untildify": ["untildify@4.0.0", "", {}, "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw=="],
-
- "untun": ["untun@0.1.3", "", { "dependencies": { "citty": "^0.1.5", "consola": "^3.2.3", "pathe": "^1.1.1" }, "bin": { "untun": "bin/untun.mjs" } }, "sha512-4luGP9LMYszMRZwsvyUd9MrxgEGZdZuZgpVQHEEX0lCYFESasVRvZd0EYpCkOIbJKHMuv0LskpXc/8Un+MJzEQ=="],
-
- "update-notifier": ["update-notifier@7.3.1", "", { "dependencies": { "boxen": "^8.0.1", "chalk": "^5.3.0", "configstore": "^7.0.0", "is-in-ci": "^1.0.0", "is-installed-globally": "^1.0.0", "is-npm": "^6.0.0", "latest-version": "^9.0.0", "pupa": "^3.1.0", "semver": "^7.6.3", "xdg-basedir": "^5.1.0" } }, "sha512-+dwUY4L35XFYEzE+OAL3sarJdUioVovq+8f7lcIJ7wnmnYQV5UD1Y/lcwaMSyaQ6Bj3JMj1XSTjZbNLHn/19yA=="],
-
- "uqr": ["uqr@0.1.3", "", {}, "sha512-0rjE8iEJe4YmT9TOhwsZtqCMRLc5DXZUI2UEYUUg63ikBkqqE5EYWaI0etFe/5KUcmcYwLih2RND1kq+hrUJXA=="],
-
- "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
-
- "urlpattern-polyfill": ["urlpattern-polyfill@8.0.2", "", {}, "sha512-Qp95D4TPJl1kC9SKigDcqgyM2VDVO4RiJc2d4qe5GrYm+zbIQCWWKAFaJNQ4BhdFeDGwBmAxqJBwWSJDb9T3BQ=="],
-
- "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
-
- "utils-feed-helper": ["utils-feed-helper@workspace:pkgs/utils-feed-helper"],
-
- "uuid": ["uuid@13.0.2", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-vzi9uRZ926x4XV73S/4qQaTwPXM2JBj6/6lI/byHH1jOpCzb0zDbfytgA9LcN/hzb2l7WQSQnxITOVx5un/wGw=="],
-
- "v8-compile-cache-lib": ["v8-compile-cache-lib@3.0.1", "", {}, "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg=="],
-
- "validate-npm-package-license": ["validate-npm-package-license@3.0.4", "", { "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" } }, "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew=="],
-
- "validate-npm-package-name": ["validate-npm-package-name@5.0.1", "", {}, "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ=="],
-
- "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
-
- "vercel": ["vercel@54.20.1", "", { "dependencies": { "@vercel/backends": "0.8.21", "@vercel/blob": "2.4.0", "@vercel/build-utils": "13.32.2", "@vercel/cli-auth": "0.3.0", "@vercel/cli-config": "0.2.0", "@vercel/container": "0.0.4", "@vercel/detect-agent": "1.2.3", "@vercel/elysia": "0.1.98", "@vercel/express": "0.1.112", "@vercel/fastify": "0.1.101", "@vercel/fun": "1.3.0", "@vercel/go": "3.10.2", "@vercel/h3": "0.1.107", "@vercel/hono": "0.2.101", "@vercel/hydrogen": "1.4.0", "@vercel/koa": "0.1.81", "@vercel/nestjs": "0.2.102", "@vercel/next": "4.20.2", "@vercel/node": "5.8.22", "@vercel/prepare-flags-definitions": "0.3.0", "@vercel/python": "6.47.3", "@vercel/redwood": "2.5.0", "@vercel/remix-builder": "5.9.1", "@vercel/ruby": "2.5.1", "@vercel/rust": "1.4.0", "@vercel/static-build": "2.11.4", "chokidar": "4.0.0", "esbuild": "0.27.0", "jose": "5.9.6", "luxon": "^3.4.0", "proxy-agent": "6.4.0", "sandbox": "3.1.2", "smol-toml": "1.5.2", "undici": "5.29.0", "zod": "4.1.11" }, "bin": { "vc": "dist/vc.js", "vercel": "dist/vc.js" } }, "sha512-Mssq8tAwjTstAi25XkFMG+nCjwCqvVLmRgcGQwSxwpptrLN6KBPDd+hAcaMXZtlxoocqdr3aa+bRqw0P9H2Y3g=="],
-
- "wcwidth": ["wcwidth@1.0.1", "", { "dependencies": { "defaults": "^1.0.3" } }, "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg=="],
-
- "web-streams-polyfill": ["web-streams-polyfill@3.3.3", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="],
-
- "web-vitals": ["web-vitals@0.2.4", "", {}, "sha512-6BjspCO9VriYy12z356nL6JBS0GYeEcA457YyRzD+dD6XYCQ75NKhcOHUMHentOE7OcVCIXXDvOm0jKFfQG2Gg=="],
-
- "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
-
- "whatwg-encoding": ["whatwg-encoding@3.1.1", "", { "dependencies": { "iconv-lite": "0.6.3" } }, "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ=="],
-
- "whatwg-mimetype": ["whatwg-mimetype@4.0.0", "", {}, "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="],
-
- "whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="],
-
- "when-exit": ["when-exit@2.1.5", "", {}, "sha512-VGkKJ564kzt6Ms1dbgPP/yuIoQCrsFAnRbptpC5wOEsDaNsbCB2bnfnaA8i/vRs5tjUSEOtIuvl9/MyVsvQZCg=="],
-
- "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
-
- "which-boxed-primitive": ["which-boxed-primitive@1.1.1", "", { "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" } }, "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA=="],
-
- "which-builtin-type": ["which-builtin-type@1.2.1", "", { "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" } }, "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q=="],
-
- "which-collection": ["which-collection@1.0.2", "", { "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", "is-weakmap": "^2.0.2", "is-weakset": "^2.0.3" } }, "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw=="],
-
- "which-typed-array": ["which-typed-array@1.1.22", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.9", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw=="],
-
- "widest-line": ["widest-line@5.0.0", "", { "dependencies": { "string-width": "^7.0.0" } }, "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA=="],
-
- "windows-release": ["windows-release@6.1.0", "", { "dependencies": { "execa": "^8.0.1" } }, "sha512-1lOb3qdzw6OFmOzoY0nauhLG72TpWtb5qgYPiSh/62rjc1XidBSDio2qw0pwHh17VINF217ebIkZJdFLZFn9SA=="],
-
- "winston": ["winston@3.19.0", "", { "dependencies": { "@colors/colors": "^1.6.0", "@dabh/diagnostics": "^2.0.8", "async": "^3.2.3", "is-stream": "^2.0.0", "logform": "^2.7.0", "one-time": "^1.0.0", "readable-stream": "^3.4.0", "safe-stable-stringify": "^2.3.1", "stack-trace": "0.0.x", "triple-beam": "^1.3.0", "winston-transport": "^4.9.0" } }, "sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA=="],
-
- "winston-transport": ["winston-transport@4.9.0", "", { "dependencies": { "logform": "^2.7.0", "readable-stream": "^3.6.2", "triple-beam": "^1.3.0" } }, "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A=="],
-
- "workerd": ["workerd@1.20260701.1", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20260701.1", "@cloudflare/workerd-darwin-arm64": "1.20260701.1", "@cloudflare/workerd-linux-64": "1.20260701.1", "@cloudflare/workerd-linux-arm64": "1.20260701.1", "@cloudflare/workerd-windows-64": "1.20260701.1" }, "bin": { "workerd": "bin/workerd" } }, "sha512-uF813NG09JwNRRUfJ0zBomyTslSPM810dMj9LVvkQ7RAkLrQLzAlPU8Xh/3dIqZDo2bfd7tChbf2PtqLRARRJQ=="],
-
- "wrangler": ["wrangler@4.107.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.5.0", "@cloudflare/unenv-preset": "2.16.1", "blake3-wasm": "2.1.5", "esbuild": "0.28.1", "miniflare": "4.20260701.0", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20260701.1" }, "optionalDependencies": { "fsevents": "2.3.3" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20260701.1" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "cf-wrangler": "bin/cf-wrangler.js", "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-fw69ThymNitZ0oIEBU2yNeq3kK59UKz/jyA3udwRrQIAIsxX57q5qLOpPTN7qc5t8n9pnUeofe0uxtMuhQZW8w=="],
-
- "wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="],
-
- "wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
-
- "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
-
- "write-file-atomic": ["write-file-atomic@5.0.1", "", { "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^4.0.1" } }, "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw=="],
-
- "ws": ["ws@8.20.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA=="],
-
- "wsl-utils": ["wsl-utils@0.3.1", "", { "dependencies": { "is-wsl": "^3.1.0", "powershell-utils": "^0.1.0" } }, "sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg=="],
-
- "xdg-app-paths": ["xdg-app-paths@5.1.0", "", { "dependencies": { "xdg-portable": "^7.0.0" } }, "sha512-RAQ3WkPf4KTU1A8RtFx3gWywzVKe00tfOPFfl2NDGqbIFENQO4kqAJp7mhQjNj/33W5x5hiWWUdyfPq/5SU3QA=="],
-
- "xdg-basedir": ["xdg-basedir@5.1.0", "", {}, "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ=="],
-
- "xdg-portable": ["xdg-portable@7.3.0", "", { "dependencies": { "os-paths": "^4.0.1" } }, "sha512-sqMMuL1rc0FmMBOzCpd0yuy9trqF2yTTVe+E9ogwCSWQCdDEtQUwrZPT6AxqtsFGRNxycgncbP/xmOOSPw5ZUw=="],
-
- "xml-js": ["xml-js@1.6.11", "", { "dependencies": { "sax": "^1.2.4" }, "bin": { "xml-js": "./bin/cli.js" } }, "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g=="],
-
- "xss": ["xss@1.0.15", "", { "dependencies": { "commander": "^2.20.3", "cssfilter": "0.0.10" }, "bin": { "xss": "bin/xss" } }, "sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg=="],
-
- "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="],
-
- "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="],
-
- "yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="],
-
- "yaml": ["yaml@2.9.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA=="],
-
- "yargs": ["yargs@18.0.0", "", { "dependencies": { "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" } }, "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg=="],
-
- "yargs-parser": ["yargs-parser@22.0.0", "", {}, "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw=="],
-
- "yauzl": ["yauzl@3.4.0", "", { "dependencies": { "pend": "~1.2.0" } }, "sha512-jIH9yLR9wqr0wOS0TpBvo/g/2UgZH5qePVbjgRliiF0BYvOZyaBknKsF+x9Iht0O6sqgnB93rCICdOZFecJuDw=="],
-
- "yauzl-clone": ["yauzl-clone@1.0.4", "", { "dependencies": { "events-intercept": "^2.0.0" } }, "sha512-igM2RRCf3k8TvZoxR2oguuw4z1xasOnA31joCqHIyLkeWrvAc2Jgay5ISQ2ZplinkoGaJ6orCz56Ey456c5ESA=="],
-
- "yauzl-promise": ["yauzl-promise@2.1.3", "", { "dependencies": { "yauzl": "^2.9.1", "yauzl-clone": "^1.0.4" } }, "sha512-A1pf6fzh6eYkK0L4Qp7g9jzJSDrM6nN0bOn5T0IbY4Yo3w+YkWlHFkJP7mzknMXjqusHFHlKsK2N+4OLsK2MRA=="],
-
- "yn": ["yn@3.1.1", "", {}, "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q=="],
-
- "yocto-queue": ["yocto-queue@1.2.2", "", {}, "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ=="],
-
- "youch": ["youch@4.1.0-beta.10", "", { "dependencies": { "@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" } }, "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ=="],
-
- "youch-core": ["youch-core@0.3.3", "", { "dependencies": { "@poppinss/exception": "^1.2.2", "error-stack-parser-es": "^1.0.5" } }, "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA=="],
-
- "zip-stream": ["zip-stream@6.0.1", "", { "dependencies": { "archiver-utils": "^5.0.0", "compress-commons": "^6.0.2", "readable-stream": "^4.0.0" } }, "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA=="],
-
- "zod": ["zod@4.1.11", "", {}, "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg=="],
-
- "@bugsnag/node/pump": ["pump@3.0.4", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA=="],
-
- "@fastify/proxy-addr/ipaddr.js": ["ipaddr.js@2.4.0", "", {}, "sha512-9VGk3HGanVE6JoZXHiCpnGy5X0jYDnN4EA4lntFPj+1vIWlFhIylq2CrrCOJH9EAhc5CYhq18F2Av2tgoAPsYQ=="],
-
- "@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
-
- "@isaacs/cliui/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="],
-
- "@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
-
- "@mapbox/node-pre-gyp/https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
-
- "@mapbox/node-pre-gyp/node-fetch": ["node-fetch@2.6.9", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg=="],
-
- "@mapbox/node-pre-gyp/tar": ["tar@7.5.7", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ=="],
-
- "@netlify/api/p-wait-for": ["p-wait-for@5.0.2", "", { "dependencies": { "p-timeout": "^6.0.0" } }, "sha512-lwx6u1CotQYPVju77R+D0vFomni/AqRfqLmqQ8hekklqZ6gAY9rONh7lBQ0uxWMkC2AuX9b2DVAl8To0NyP1JA=="],
-
- "@netlify/build/execa": ["execa@8.0.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" } }, "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg=="],
-
- "@netlify/build/read-package-up": ["read-package-up@11.0.0", "", { "dependencies": { "find-up-simple": "^1.0.0", "read-pkg": "^9.0.0", "type-fest": "^4.6.0" } }, "sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ=="],
-
- "@netlify/build/terminal-link": ["terminal-link@4.0.0", "", { "dependencies": { "ansi-escapes": "^7.0.0", "supports-hyperlinks": "^3.2.0" } }, "sha512-lk+vH+MccxNqgVqSnkMVKx4VLJfnLjDBGzH16JVZjKE2DoxP57s6/vt6JmXV5I3jBcfGrxNrYtC+mPtU7WJztA=="],
-
- "@netlify/build/yargs": ["yargs@17.7.3", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g=="],
-
- "@netlify/build/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
-
- "@netlify/build-info/dot-prop": ["dot-prop@9.0.0", "", { "dependencies": { "type-fest": "^4.18.2" } }, "sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ=="],
-
- "@netlify/build-info/find-up": ["find-up@7.0.0", "", { "dependencies": { "locate-path": "^7.2.0", "path-exists": "^5.0.0", "unicorn-magic": "^0.1.0" } }, "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g=="],
-
- "@netlify/build-info/yargs": ["yargs@17.7.3", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g=="],
-
- "@netlify/cache-utils/get-stream": ["get-stream@9.0.1", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="],
-
- "@netlify/cache-utils/locate-path": ["locate-path@7.2.0", "", { "dependencies": { "p-locate": "^6.0.0" } }, "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA=="],
-
- "@netlify/config/dot-prop": ["dot-prop@9.0.0", "", { "dependencies": { "type-fest": "^4.18.2" } }, "sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ=="],
-
- "@netlify/config/execa": ["execa@8.0.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" } }, "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg=="],
-
- "@netlify/config/find-up": ["find-up@7.0.0", "", { "dependencies": { "locate-path": "^7.2.0", "path-exists": "^5.0.0", "unicorn-magic": "^0.1.0" } }, "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g=="],
-
- "@netlify/config/read-package-up": ["read-package-up@11.0.0", "", { "dependencies": { "find-up-simple": "^1.0.0", "read-pkg": "^9.0.0", "type-fest": "^4.6.0" } }, "sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ=="],
-
- "@netlify/config/yargs": ["yargs@17.7.3", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g=="],
-
- "@netlify/dev-utils/dot-prop": ["dot-prop@9.0.0", "", { "dependencies": { "type-fest": "^4.18.2" } }, "sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ=="],
-
- "@netlify/dev-utils/env-paths": ["env-paths@3.0.0", "", {}, "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A=="],
-
- "@netlify/edge-bundler/env-paths": ["env-paths@3.0.0", "", {}, "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A=="],
-
- "@netlify/edge-bundler/esbuild": ["esbuild@0.28.0", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.0", "@esbuild/android-arm": "0.28.0", "@esbuild/android-arm64": "0.28.0", "@esbuild/android-x64": "0.28.0", "@esbuild/darwin-arm64": "0.28.0", "@esbuild/darwin-x64": "0.28.0", "@esbuild/freebsd-arm64": "0.28.0", "@esbuild/freebsd-x64": "0.28.0", "@esbuild/linux-arm": "0.28.0", "@esbuild/linux-arm64": "0.28.0", "@esbuild/linux-ia32": "0.28.0", "@esbuild/linux-loong64": "0.28.0", "@esbuild/linux-mips64el": "0.28.0", "@esbuild/linux-ppc64": "0.28.0", "@esbuild/linux-riscv64": "0.28.0", "@esbuild/linux-s390x": "0.28.0", "@esbuild/linux-x64": "0.28.0", "@esbuild/netbsd-arm64": "0.28.0", "@esbuild/netbsd-x64": "0.28.0", "@esbuild/openbsd-arm64": "0.28.0", "@esbuild/openbsd-x64": "0.28.0", "@esbuild/openharmony-arm64": "0.28.0", "@esbuild/sunos-x64": "0.28.0", "@esbuild/win32-arm64": "0.28.0", "@esbuild/win32-ia32": "0.28.0", "@esbuild/win32-x64": "0.28.0" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw=="],
-
- "@netlify/edge-bundler/execa": ["execa@8.0.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" } }, "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg=="],
-
- "@netlify/edge-bundler/find-up": ["find-up@7.0.0", "", { "dependencies": { "locate-path": "^7.2.0", "path-exists": "^5.0.0", "unicorn-magic": "^0.1.0" } }, "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g=="],
-
- "@netlify/edge-bundler/get-port": ["get-port@7.2.0", "", {}, "sha512-afP4W205ONCuMoPBqcR6PSXnzX35KTcJygfJfcp+QY+uwm3p20p1YczWXhlICIzGMCxYBQcySEcOgsJcrkyobg=="],
-
- "@netlify/edge-bundler/p-wait-for": ["p-wait-for@5.0.2", "", { "dependencies": { "p-timeout": "^6.0.0" } }, "sha512-lwx6u1CotQYPVju77R+D0vFomni/AqRfqLmqQ8hekklqZ6gAY9rONh7lBQ0uxWMkC2AuX9b2DVAl8To0NyP1JA=="],
-
- "@netlify/edge-functions-dev/@netlify/edge-functions-bootstrap": ["@netlify/edge-functions-bootstrap@2.16.0", "", {}, "sha512-v8QQihSbBHj3JxtJsHoepXALpNumD9M7egHoc8z62FYl5it34dWczkaJoFFopEyhiBVKi4K/n0ZYpdzwfujd6g=="],
-
- "@netlify/edge-functions-dev/get-port": ["get-port@7.2.0", "", {}, "sha512-afP4W205ONCuMoPBqcR6PSXnzX35KTcJygfJfcp+QY+uwm3p20p1YczWXhlICIzGMCxYBQcySEcOgsJcrkyobg=="],
-
- "@netlify/functions-dev/read-package-up": ["read-package-up@11.0.0", "", { "dependencies": { "find-up-simple": "^1.0.0", "read-pkg": "^9.0.0", "type-fest": "^4.6.0" } }, "sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ=="],
-
- "@netlify/git-utils/execa": ["execa@8.0.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" } }, "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg=="],
-
- "@netlify/headers-parser/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
-
- "@netlify/otel/@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
-
- "@netlify/run-utils/execa": ["execa@8.0.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" } }, "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg=="],
-
- "@netlify/zip-it-and-ship-it/esbuild": ["esbuild@0.28.0", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.0", "@esbuild/android-arm": "0.28.0", "@esbuild/android-arm64": "0.28.0", "@esbuild/android-x64": "0.28.0", "@esbuild/darwin-arm64": "0.28.0", "@esbuild/darwin-x64": "0.28.0", "@esbuild/freebsd-arm64": "0.28.0", "@esbuild/freebsd-x64": "0.28.0", "@esbuild/linux-arm": "0.28.0", "@esbuild/linux-arm64": "0.28.0", "@esbuild/linux-ia32": "0.28.0", "@esbuild/linux-loong64": "0.28.0", "@esbuild/linux-mips64el": "0.28.0", "@esbuild/linux-ppc64": "0.28.0", "@esbuild/linux-riscv64": "0.28.0", "@esbuild/linux-s390x": "0.28.0", "@esbuild/linux-x64": "0.28.0", "@esbuild/netbsd-arm64": "0.28.0", "@esbuild/netbsd-x64": "0.28.0", "@esbuild/openbsd-arm64": "0.28.0", "@esbuild/openbsd-x64": "0.28.0", "@esbuild/openharmony-arm64": "0.28.0", "@esbuild/sunos-x64": "0.28.0", "@esbuild/win32-arm64": "0.28.0", "@esbuild/win32-ia32": "0.28.0", "@esbuild/win32-x64": "0.28.0" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw=="],
-
- "@netlify/zip-it-and-ship-it/execa": ["execa@8.0.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" } }, "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg=="],
-
- "@netlify/zip-it-and-ship-it/find-up": ["find-up@7.0.0", "", { "dependencies": { "locate-path": "^7.2.0", "path-exists": "^5.0.0", "unicorn-magic": "^0.1.0" } }, "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g=="],
-
- "@netlify/zip-it-and-ship-it/locate-path": ["locate-path@7.2.0", "", { "dependencies": { "p-locate": "^6.0.0" } }, "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA=="],
-
- "@netlify/zip-it-and-ship-it/yargs": ["yargs@17.7.3", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g=="],
-
- "@netlify/zip-it-and-ship-it/zod": ["zod@3.24.4", "", {}, "sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg=="],
-
- "@octokit/request/content-type": ["content-type@2.0.0", "", {}, "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="],
-
- "@parcel/watcher/picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="],
-
- "@parcel/watcher-wasm/napi-wasm": ["napi-wasm@1.1.3", "", { "bundled": true }, "sha512-h/4nMGsHjZDCYmQVNODIrYACVJ+I9KItbG+0si6W/jSjdA9JbWDoU4LLeMXVcEQGHjttI2tuXqDrbGF7qkUHHg=="],
-
- "@parcel/watcher-wasm/picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="],
-
- "@pnpm/network.ca-file/graceful-fs": ["graceful-fs@4.2.10", "", {}, "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="],
-
- "@rollup/pluginutils/picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="],
-
- "@sindresorhus/slugify/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
-
- "@sindresorhus/transliterate/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
-
- "@ts-morph/common/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="],
-
- "@vercel/backends/@vercel/nft": ["@vercel/nft@1.10.0", "", { "dependencies": { "@mapbox/node-pre-gyp": "^2.0.0", "@rollup/pluginutils": "^5.1.3", "acorn": "^8.6.0", "acorn-import-attributes": "^1.9.5", "async-sema": "^3.1.1", "bindings": "^1.4.0", "estree-walker": "2.0.2", "glob": "^13.0.0", "graceful-fs": "^4.2.9", "node-gyp-build": "^4.2.2", "picomatch": "^4.0.2", "resolve-from": "^5.0.0" }, "bin": { "nft": "out/cli.js" } }, "sha512-iLOW4fcsgkipfOh2Bw3wB38YDfxTlxr7+j4uFeui2OswkNT28jIitS/aMce7tS0mef1YPQ8zLIDYr3a0aahNrA=="],
-
- "@vercel/backends/execa": ["execa@3.2.0", "", { "dependencies": { "cross-spawn": "^7.0.0", "get-stream": "^5.0.0", "human-signals": "^1.1.1", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.0", "onetime": "^5.1.0", "p-finally": "^2.0.0", "signal-exit": "^3.0.2", "strip-final-newline": "^2.0.0" } }, "sha512-kJJfVbI/lZE1PZYDI5VPxp8zXPO9rtxOkhpZ0jMKha56AI9y2gGVC6bkukStQf0ka5Rh15BA5m7cCCH4jmHqkw=="],
-
- "@vercel/backends/path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="],
-
- "@vercel/backends/srvx": ["srvx@0.11.16", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-bp07zRuycfTY43IjAvvTFnmnJi8ikW0VFiHwOhhYcVW/L4xQ1XY4PAd4Nuum1rsA17C39zL7x+CDhrn5AL32Rw=="],
-
- "@vercel/backends/tsx": ["tsx@4.21.0", "", { "dependencies": { "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw=="],
-
- "@vercel/backends/zod": ["zod@3.22.4", "", {}, "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg=="],
-
- "@vercel/blob/undici": ["undici@6.27.0", "", {}, "sha512-YmfV3YnEDzXRC5lZ2jWtWWHKGUm1zIt8AhesR1tens+HTNv+YZlN/dp6G727LOvMJ8xjP9Be7Y2Sdr96LDm+pg=="],
-
- "@vercel/build-utils/es-module-lexer": ["es-module-lexer@1.5.0", "", {}, "sha512-pqrTKmwEIgafsYZAGw9kszYzmagcE/n4dbgwGWLEXg7J4QFJVQRBld8j3Q3GNez79jzxZshq0bcT962QHOghjw=="],
-
- "@vercel/cli-auth/open": ["open@8.4.0", "", { "dependencies": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", "is-wsl": "^2.2.0" } }, "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q=="],
-
- "@vercel/express/@vercel/nft": ["@vercel/nft@1.10.0", "", { "dependencies": { "@mapbox/node-pre-gyp": "^2.0.0", "@rollup/pluginutils": "^5.1.3", "acorn": "^8.6.0", "acorn-import-attributes": "^1.9.5", "async-sema": "^3.1.1", "bindings": "^1.4.0", "estree-walker": "2.0.2", "glob": "^13.0.0", "graceful-fs": "^4.2.9", "node-gyp-build": "^4.2.2", "picomatch": "^4.0.2", "resolve-from": "^5.0.0" }, "bin": { "nft": "out/cli.js" } }, "sha512-iLOW4fcsgkipfOh2Bw3wB38YDfxTlxr7+j4uFeui2OswkNT28jIitS/aMce7tS0mef1YPQ8zLIDYr3a0aahNrA=="],
-
- "@vercel/express/path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="],
-
- "@vercel/express/zod": ["zod@3.22.4", "", {}, "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg=="],
-
- "@vercel/fun/async-listen": ["async-listen@1.2.0", "", {}, "sha512-CcEtRh/oc9Jc4uWeUwdpG/+Mb2YUHKmdaTf0gUr7Wa+bfp4xx70HOb3RuSTJMvqKNB1TkdTfjLdrcz2X4rkkZA=="],
-
- "@vercel/fun/debug": ["debug@4.3.4", "", { "dependencies": { "ms": "2.1.2" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ=="],
-
- "@vercel/fun/ms": ["ms@2.1.1", "", {}, "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="],
-
- "@vercel/fun/node-fetch": ["node-fetch@2.6.7", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ=="],
-
- "@vercel/fun/path-to-regexp": ["path-to-regexp@8.2.0", "", {}, "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ=="],
-
- "@vercel/fun/semver": ["semver@7.5.4", "", { "dependencies": { "lru-cache": "^6.0.0" }, "bin": { "semver": "bin/semver.js" } }, "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA=="],
-
- "@vercel/fun/tar": ["tar@7.5.7", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ=="],
-
- "@vercel/gatsby-plugin-vercel-builder/@sinclair/typebox": ["@sinclair/typebox@0.25.24", "", {}, "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ=="],
-
- "@vercel/gatsby-plugin-vercel-builder/esbuild": ["esbuild@0.27.0", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.0", "@esbuild/android-arm": "0.27.0", "@esbuild/android-arm64": "0.27.0", "@esbuild/android-x64": "0.27.0", "@esbuild/darwin-arm64": "0.27.0", "@esbuild/darwin-x64": "0.27.0", "@esbuild/freebsd-arm64": "0.27.0", "@esbuild/freebsd-x64": "0.27.0", "@esbuild/linux-arm": "0.27.0", "@esbuild/linux-arm64": "0.27.0", "@esbuild/linux-ia32": "0.27.0", "@esbuild/linux-loong64": "0.27.0", "@esbuild/linux-mips64el": "0.27.0", "@esbuild/linux-ppc64": "0.27.0", "@esbuild/linux-riscv64": "0.27.0", "@esbuild/linux-s390x": "0.27.0", "@esbuild/linux-x64": "0.27.0", "@esbuild/netbsd-arm64": "0.27.0", "@esbuild/netbsd-x64": "0.27.0", "@esbuild/openbsd-arm64": "0.27.0", "@esbuild/openbsd-x64": "0.27.0", "@esbuild/openharmony-arm64": "0.27.0", "@esbuild/sunos-x64": "0.27.0", "@esbuild/win32-arm64": "0.27.0", "@esbuild/win32-ia32": "0.27.0", "@esbuild/win32-x64": "0.27.0" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA=="],
-
- "@vercel/hono/@vercel/nft": ["@vercel/nft@1.10.0", "", { "dependencies": { "@mapbox/node-pre-gyp": "^2.0.0", "@rollup/pluginutils": "^5.1.3", "acorn": "^8.6.0", "acorn-import-attributes": "^1.9.5", "async-sema": "^3.1.1", "bindings": "^1.4.0", "estree-walker": "2.0.2", "glob": "^13.0.0", "graceful-fs": "^4.2.9", "node-gyp-build": "^4.2.2", "picomatch": "^4.0.2", "resolve-from": "^5.0.0" }, "bin": { "nft": "out/cli.js" } }, "sha512-iLOW4fcsgkipfOh2Bw3wB38YDfxTlxr7+j4uFeui2OswkNT28jIitS/aMce7tS0mef1YPQ8zLIDYr3a0aahNrA=="],
-
- "@vercel/hono/path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="],
-
- "@vercel/hono/zod": ["zod@3.22.4", "", {}, "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg=="],
-
- "@vercel/next/@vercel/nft": ["@vercel/nft@1.10.0", "", { "dependencies": { "@mapbox/node-pre-gyp": "^2.0.0", "@rollup/pluginutils": "^5.1.3", "acorn": "^8.6.0", "acorn-import-attributes": "^1.9.5", "async-sema": "^3.1.1", "bindings": "^1.4.0", "estree-walker": "2.0.2", "glob": "^13.0.0", "graceful-fs": "^4.2.9", "node-gyp-build": "^4.2.2", "picomatch": "^4.0.2", "resolve-from": "^5.0.0" }, "bin": { "nft": "out/cli.js" } }, "sha512-iLOW4fcsgkipfOh2Bw3wB38YDfxTlxr7+j4uFeui2OswkNT28jIitS/aMce7tS0mef1YPQ8zLIDYr3a0aahNrA=="],
-
- "@vercel/nft/glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="],
-
- "@vercel/nft/picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="],
-
- "@vercel/node/@types/node": ["@types/node@20.11.0", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-o9bjXmDNcF7GbM4CNQpmi+TutCgap/K3w1JyKgxAjqx41zp9qlIAVFi0IhCNsJcXolEqLWhbFbEeL0PvYm4pcQ=="],
-
- "@vercel/node/@vercel/nft": ["@vercel/nft@1.10.0", "", { "dependencies": { "@mapbox/node-pre-gyp": "^2.0.0", "@rollup/pluginutils": "^5.1.3", "acorn": "^8.6.0", "acorn-import-attributes": "^1.9.5", "async-sema": "^3.1.1", "bindings": "^1.4.0", "estree-walker": "2.0.2", "glob": "^13.0.0", "graceful-fs": "^4.2.9", "node-gyp-build": "^4.2.2", "picomatch": "^4.0.2", "resolve-from": "^5.0.0" }, "bin": { "nft": "out/cli.js" } }, "sha512-iLOW4fcsgkipfOh2Bw3wB38YDfxTlxr7+j4uFeui2OswkNT28jIitS/aMce7tS0mef1YPQ8zLIDYr3a0aahNrA=="],
-
- "@vercel/node/es-module-lexer": ["es-module-lexer@1.4.1", "", {}, "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w=="],
-
- "@vercel/node/esbuild": ["esbuild@0.27.0", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.0", "@esbuild/android-arm": "0.27.0", "@esbuild/android-arm64": "0.27.0", "@esbuild/android-x64": "0.27.0", "@esbuild/darwin-arm64": "0.27.0", "@esbuild/darwin-x64": "0.27.0", "@esbuild/freebsd-arm64": "0.27.0", "@esbuild/freebsd-x64": "0.27.0", "@esbuild/linux-arm": "0.27.0", "@esbuild/linux-arm64": "0.27.0", "@esbuild/linux-ia32": "0.27.0", "@esbuild/linux-loong64": "0.27.0", "@esbuild/linux-mips64el": "0.27.0", "@esbuild/linux-ppc64": "0.27.0", "@esbuild/linux-riscv64": "0.27.0", "@esbuild/linux-s390x": "0.27.0", "@esbuild/linux-x64": "0.27.0", "@esbuild/netbsd-arm64": "0.27.0", "@esbuild/netbsd-x64": "0.27.0", "@esbuild/openbsd-arm64": "0.27.0", "@esbuild/openbsd-x64": "0.27.0", "@esbuild/openharmony-arm64": "0.27.0", "@esbuild/sunos-x64": "0.27.0", "@esbuild/win32-arm64": "0.27.0", "@esbuild/win32-ia32": "0.27.0", "@esbuild/win32-x64": "0.27.0" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA=="],
-
- "@vercel/node/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
-
- "@vercel/node/node-fetch": ["node-fetch@2.6.9", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg=="],
-
- "@vercel/node/path-to-regexp": ["path-to-regexp@6.1.0", "", {}, "sha512-h9DqehX3zZZDCEm+xbfU0ZmwCGFCAAraPJWMXJ4+v32NjZJilVg3k1TcKsRgIb8IQ/izZSaydDc1OhJCZvs2Dw=="],
-
- "@vercel/node/tsx": ["tsx@4.21.0", "", { "dependencies": { "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw=="],
-
- "@vercel/node/undici": ["undici@5.28.4", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g=="],
-
- "@vercel/python-analysis/fs-extra": ["fs-extra@11.1.1", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ=="],
-
- "@vercel/python-analysis/minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="],
-
- "@vercel/python-analysis/zod": ["zod@3.22.4", "", {}, "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg=="],
-
- "@vercel/redwood/@vercel/nft": ["@vercel/nft@1.10.0", "", { "dependencies": { "@mapbox/node-pre-gyp": "^2.0.0", "@rollup/pluginutils": "^5.1.3", "acorn": "^8.6.0", "acorn-import-attributes": "^1.9.5", "async-sema": "^3.1.1", "bindings": "^1.4.0", "estree-walker": "2.0.2", "glob": "^13.0.0", "graceful-fs": "^4.2.9", "node-gyp-build": "^4.2.2", "picomatch": "^4.0.2", "resolve-from": "^5.0.0" }, "bin": { "nft": "out/cli.js" } }, "sha512-iLOW4fcsgkipfOh2Bw3wB38YDfxTlxr7+j4uFeui2OswkNT28jIitS/aMce7tS0mef1YPQ8zLIDYr3a0aahNrA=="],
-
- "@vercel/redwood/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
-
- "@vercel/remix-builder/@vercel/nft": ["@vercel/nft@1.10.0", "", { "dependencies": { "@mapbox/node-pre-gyp": "^2.0.0", "@rollup/pluginutils": "^5.1.3", "acorn": "^8.6.0", "acorn-import-attributes": "^1.9.5", "async-sema": "^3.1.1", "bindings": "^1.4.0", "estree-walker": "2.0.2", "glob": "^13.0.0", "graceful-fs": "^4.2.9", "node-gyp-build": "^4.2.2", "picomatch": "^4.0.2", "resolve-from": "^5.0.0" }, "bin": { "nft": "out/cli.js" } }, "sha512-iLOW4fcsgkipfOh2Bw3wB38YDfxTlxr7+j4uFeui2OswkNT28jIitS/aMce7tS0mef1YPQ8zLIDYr3a0aahNrA=="],
-
- "@vercel/remix-builder/path-to-regexp": ["path-to-regexp@6.1.0", "", {}, "sha512-h9DqehX3zZZDCEm+xbfU0ZmwCGFCAAraPJWMXJ4+v32NjZJilVg3k1TcKsRgIb8IQ/izZSaydDc1OhJCZvs2Dw=="],
-
- "@vercel/sandbox/jose": ["jose@6.2.3", "", {}, "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw=="],
-
- "@vercel/sandbox/undici": ["undici@7.28.0", "", {}, "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA=="],
-
- "@vercel/sandbox/zod": ["zod@3.24.4", "", {}, "sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg=="],
-
- "@vercel/static-config/ajv": ["ajv@8.6.3", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", "uri-js": "^4.2.2" } }, "sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw=="],
-
- "@vue/compiler-sfc/postcss": ["postcss@8.5.16", "", { "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg=="],
-
- "@whatwg-node/fetch/urlpattern-polyfill": ["urlpattern-polyfill@10.1.0", "", {}, "sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw=="],
-
- "@whatwg-node/node-fetch/@fastify/busboy": ["@fastify/busboy@3.2.0", "", {}, "sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA=="],
-
- "ansi-align/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
-
- "ansi-to-html/entities": ["entities@2.2.0", "", {}, "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="],
-
- "archiver/readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="],
-
- "archiver-utils/glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="],
-
- "archiver-utils/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
-
- "archiver-utils/readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="],
-
- "better-ajv-errors/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
-
- "bl/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
-
- "bl/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
-
- "body-parser/content-type": ["content-type@2.0.0", "", {}, "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="],
-
- "cheerio/undici": ["undici@7.25.0", "", {}, "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ=="],
-
- "clean-stack/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
-
- "cliui/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="],
-
- "color/color-convert": ["color-convert@3.1.3", "", { "dependencies": { "color-name": "^2.0.0" } }, "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg=="],
-
- "color-string/color-name": ["color-name@2.1.0", "", {}, "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg=="],
-
- "compress-commons/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
-
- "compress-commons/readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="],
-
- "configstore/dot-prop": ["dot-prop@9.0.0", "", { "dependencies": { "type-fest": "^4.18.2" } }, "sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ=="],
-
- "crc32-stream/readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="],
-
- "cross-spawn/path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
-
- "csso/css-tree": ["css-tree@2.2.1", "", { "dependencies": { "mdn-data": "2.0.28", "source-map-js": "^1.0.1" } }, "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA=="],
-
- "dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
-
- "dot-prop/type-fest": ["type-fest@5.7.0", "", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg=="],
-
- "edge-runtime/async-listen": ["async-listen@3.0.1", "", {}, "sha512-cWMaNwUJnf37C/S5TfCkk/15MwbPRwVYALA2jtjkbHjCmAPiDXyNJy2q3p1KAZzDLHAWyarUWSujUoHR4pEgrA=="],
-
- "edge-runtime/picocolors": ["picocolors@1.0.0", "", {}, "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="],
-
- "edge-runtime/pretty-ms": ["pretty-ms@7.0.1", "", { "dependencies": { "parse-ms": "^2.1.0" } }, "sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q=="],
-
- "edge-runtime/signal-exit": ["signal-exit@4.0.2", "", {}, "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q=="],
-
- "encoding-sniffer/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
-
- "end-of-stream/once": ["once@1.3.3", "", { "dependencies": { "wrappy": "1" } }, "sha512-6vaNInhu+CHxtONf3zw3vq4SP2DOQhjBvIa3rNcG0+P7eKWlYH6Peu7rHizSloRU2EwMz6GraLieis9Ac9+p1w=="],
-
- "execa/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
-
- "express/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
-
- "extract-zip/get-stream": ["get-stream@5.2.0", "", { "dependencies": { "pump": "^3.0.0" } }, "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA=="],
-
- "extract-zip/yauzl": ["yauzl@2.10.0", "", { "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } }, "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g=="],
-
- "foreground-child/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
-
- "get-uri/data-uri-to-buffer": ["data-uri-to-buffer@6.0.2", "", {}, "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw=="],
-
- "global-directory/ini": ["ini@4.1.1", "", {}, "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g=="],
-
- "h3/crossws": ["crossws@0.3.5", "", { "dependencies": { "uncrypto": "^0.1.3" } }, "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA=="],
-
- "hosted-git-info/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
-
- "http-proxy-agent/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
-
- "import-in-the-middle/cjs-module-lexer": ["cjs-module-lexer@2.2.0", "", {}, "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ=="],
-
- "import-in-the-middle/es-module-lexer": ["es-module-lexer@2.3.0", "", {}, "sha512-KLdwQm2NvGLDkQDCGvmiQrhkd0JbMzXthwQAUgWjQuQdBLFa3eiBP5arXZyA+f8x+x7OXgud6bq2rxjGtHV2tw=="],
-
- "inquirer/ansi-escapes": ["ansi-escapes@4.3.2", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="],
-
- "inquirer/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
-
- "inquirer/figures": ["figures@3.2.0", "", { "dependencies": { "escape-string-regexp": "^1.0.5" } }, "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg=="],
-
- "inquirer/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
-
- "inquirer/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
-
- "inquirer-autocomplete-prompt/ansi-escapes": ["ansi-escapes@4.3.2", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="],
-
- "inquirer-autocomplete-prompt/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
-
- "inquirer-autocomplete-prompt/figures": ["figures@3.2.0", "", { "dependencies": { "escape-string-regexp": "^1.0.5" } }, "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg=="],
-
- "inquirer-autocomplete-prompt/rxjs": ["rxjs@6.6.7", "", { "dependencies": { "tslib": "^1.9.0" } }, "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ=="],
-
- "is-inside-container/is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="],
-
- "lambda-local/commander": ["commander@10.0.1", "", {}, "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug=="],
-
- "lambda-local/dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="],
-
- "light-my-request/process-warning": ["process-warning@4.0.1", "", {}, "sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q=="],
-
- "listhen/citty": ["citty@0.2.2", "", {}, "sha512-+6vJA3L98yv+IdfKGZHBNiGW5KHn22e/JwID0Strsz8h4S/csAu/OuICwxrg44k5MRiZHWIo8XXuJgQTriRP4w=="],
-
- "listhen/ufo": ["ufo@1.6.4", "", {}, "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA=="],
-
- "log-symbols/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
-
- "log-symbols/is-unicode-supported": ["is-unicode-supported@0.1.0", "", {}, "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw=="],
-
- "log-update/cli-cursor": ["cli-cursor@5.0.0", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="],
-
- "log-update/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="],
-
- "log-update/wrap-ansi": ["wrap-ansi@10.0.0", "", { "dependencies": { "ansi-styles": "^6.2.3", "string-width": "^8.2.0", "strip-ansi": "^7.1.2" } }, "sha512-SGcvg80f0wUy2/fXES19feHMz8E0JoXv2uNgHOu4Dgi2OrCy1lqwFYEJz1BLbDI0exjPMe/ZdzZ/YpGECBG/aQ=="],
-
- "merge-options/is-plain-obj": ["is-plain-obj@2.1.0", "", {}, "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA=="],
-
- "micro/content-type": ["content-type@1.0.4", "", {}, "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="],
-
- "micro/raw-body": ["raw-body@2.4.1", "", { "dependencies": { "bytes": "3.1.0", "http-errors": "1.7.3", "iconv-lite": "0.4.24", "unpipe": "1.0.0" } }, "sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA=="],
-
- "miniflare/undici": ["undici@7.28.0", "", {}, "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA=="],
-
- "miniflare/ws": ["ws@8.21.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="],
-
- "mlly/ufo": ["ufo@1.6.4", "", {}, "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA=="],
-
- "multiparty/http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="],
-
- "node-exports-info/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
-
- "npm-run-path/path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
-
- "onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
-
- "ora/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
-
- "ora/is-unicode-supported": ["is-unicode-supported@0.1.0", "", {}, "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw=="],
-
- "pac-proxy-agent/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
-
- "pac-proxy-agent/https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
-
- "parse5/entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="],
-
- "pgpass/split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="],
-
- "pino-abstract-transport/split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="],
-
- "precinct/postcss": ["postcss@8.5.16", "", { "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg=="],
-
- "proxy-agent/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
-
- "proxy-agent/https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
-
- "proxy-agent/lru-cache": ["lru-cache@7.18.3", "", {}, "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA=="],
-
- "read-package-up/read-pkg": ["read-pkg@10.1.0", "", { "dependencies": { "@types/normalize-package-data": "^2.4.4", "normalize-package-data": "^8.0.0", "parse-json": "^8.3.0", "type-fest": "^5.4.4", "unicorn-magic": "^0.4.0" } }, "sha512-I8g2lArQiP78ll51UeMZojewtYgIRCKCWqZEgOO8c/uefTI+XDXvCSXu3+YNUaTNvZzobrL5+SqHjBrByRRTdg=="],
-
- "read-package-up/type-fest": ["type-fest@5.7.0", "", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg=="],
-
- "read-pkg/normalize-package-data": ["normalize-package-data@6.0.2", "", { "dependencies": { "hosted-git-info": "^7.0.0", "semver": "^7.3.5", "validate-npm-package-license": "^3.0.4" } }, "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g=="],
-
- "read-pkg/unicorn-magic": ["unicorn-magic@0.1.0", "", {}, "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ=="],
-
- "readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
-
- "readdir-glob/minimatch": ["minimatch@5.1.9", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw=="],
-
- "router/path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="],
-
- "safe-array-concat/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
-
- "safe-push-apply/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
-
- "slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="],
-
- "socks-proxy-agent/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
-
- "string-width/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="],
-
- "string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
-
- "string_decoder/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
-
- "svgo/commander": ["commander@11.1.0", "", {}, "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ=="],
-
- "thread-stream/real-require": ["real-require@1.0.0", "", {}, "sha512-P4nbQYQfePJxRSmY+v/KINxVucm4NF3p3s7pJveMTtom52FR4YGltUQLB8idDXwDDWW+eYrWDFbuzUnjoWHF7g=="],
-
- "tinyglobby/picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="],
-
- "type-is/content-type": ["content-type@2.0.0", "", {}, "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="],
-
- "unixify/normalize-path": ["normalize-path@2.1.1", "", { "dependencies": { "remove-trailing-separator": "^1.0.1" } }, "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w=="],
-
- "unstorage/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="],
-
- "untun/pathe": ["pathe@1.1.2", "", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="],
-
- "vercel/chokidar": ["chokidar@4.0.0", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-mxIojEAQcuEvT/lyXq+jf/3cO/KoA6z4CeNDGGevTybECPOMFCnQy3OPahluUkbqgPNGw5Bi78UC7Po6Lhy+NA=="],
-
- "vercel/esbuild": ["esbuild@0.27.0", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.0", "@esbuild/android-arm": "0.27.0", "@esbuild/android-arm64": "0.27.0", "@esbuild/android-x64": "0.27.0", "@esbuild/darwin-arm64": "0.27.0", "@esbuild/darwin-x64": "0.27.0", "@esbuild/freebsd-arm64": "0.27.0", "@esbuild/freebsd-x64": "0.27.0", "@esbuild/linux-arm": "0.27.0", "@esbuild/linux-arm64": "0.27.0", "@esbuild/linux-ia32": "0.27.0", "@esbuild/linux-loong64": "0.27.0", "@esbuild/linux-mips64el": "0.27.0", "@esbuild/linux-ppc64": "0.27.0", "@esbuild/linux-riscv64": "0.27.0", "@esbuild/linux-s390x": "0.27.0", "@esbuild/linux-x64": "0.27.0", "@esbuild/netbsd-arm64": "0.27.0", "@esbuild/netbsd-x64": "0.27.0", "@esbuild/openbsd-arm64": "0.27.0", "@esbuild/openbsd-x64": "0.27.0", "@esbuild/openharmony-arm64": "0.27.0", "@esbuild/sunos-x64": "0.27.0", "@esbuild/win32-arm64": "0.27.0", "@esbuild/win32-ia32": "0.27.0", "@esbuild/win32-x64": "0.27.0" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA=="],
-
- "whatwg-encoding/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
-
- "which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
-
- "which-builtin-type/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
-
- "windows-release/execa": ["execa@8.0.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" } }, "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg=="],
-
- "winston/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
-
- "winston/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
-
- "winston-transport/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
-
- "wrap-ansi/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="],
-
- "wrap-ansi-cjs/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
-
- "wrap-ansi-cjs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
-
- "write-file-atomic/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
-
- "xss/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="],
-
- "yauzl-promise/yauzl": ["yauzl@2.10.0", "", { "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } }, "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g=="],
-
- "zip-stream/readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="],
-
- "@bugsnag/node/pump/once": ["once@1.3.3", "", { "dependencies": { "wrappy": "1" } }, "sha512-6vaNInhu+CHxtONf3zw3vq4SP2DOQhjBvIa3rNcG0+P7eKWlYH6Peu7rHizSloRU2EwMz6GraLieis9Ac9+p1w=="],
-
- "@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
-
- "@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
-
- "@mapbox/node-pre-gyp/https-proxy-agent/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
-
- "@netlify/build-info/find-up/locate-path": ["locate-path@7.2.0", "", { "dependencies": { "p-locate": "^6.0.0" } }, "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA=="],
-
- "@netlify/build-info/find-up/unicorn-magic": ["unicorn-magic@0.1.0", "", {}, "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ=="],
-
- "@netlify/build-info/yargs/cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
-
- "@netlify/build-info/yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
-
- "@netlify/build-info/yargs/yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
-
- "@netlify/build/execa/get-stream": ["get-stream@8.0.1", "", {}, "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA=="],
-
- "@netlify/build/execa/human-signals": ["human-signals@5.0.0", "", {}, "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ=="],
-
- "@netlify/build/execa/is-stream": ["is-stream@3.0.0", "", {}, "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA=="],
-
- "@netlify/build/execa/npm-run-path": ["npm-run-path@5.3.0", "", { "dependencies": { "path-key": "^4.0.0" } }, "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ=="],
-
- "@netlify/build/execa/onetime": ["onetime@6.0.0", "", { "dependencies": { "mimic-fn": "^4.0.0" } }, "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ=="],
-
- "@netlify/build/execa/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
-
- "@netlify/build/execa/strip-final-newline": ["strip-final-newline@3.0.0", "", {}, "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="],
-
- "@netlify/build/terminal-link/supports-hyperlinks": ["supports-hyperlinks@3.2.0", "", { "dependencies": { "has-flag": "^4.0.0", "supports-color": "^7.0.0" } }, "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig=="],
-
- "@netlify/build/yargs/cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
-
- "@netlify/build/yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
-
- "@netlify/build/yargs/yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
-
- "@netlify/config/execa/get-stream": ["get-stream@8.0.1", "", {}, "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA=="],
-
- "@netlify/config/execa/human-signals": ["human-signals@5.0.0", "", {}, "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ=="],
-
- "@netlify/config/execa/is-stream": ["is-stream@3.0.0", "", {}, "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA=="],
-
- "@netlify/config/execa/npm-run-path": ["npm-run-path@5.3.0", "", { "dependencies": { "path-key": "^4.0.0" } }, "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ=="],
-
- "@netlify/config/execa/onetime": ["onetime@6.0.0", "", { "dependencies": { "mimic-fn": "^4.0.0" } }, "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ=="],
-
- "@netlify/config/execa/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
-
- "@netlify/config/execa/strip-final-newline": ["strip-final-newline@3.0.0", "", {}, "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="],
-
- "@netlify/config/find-up/locate-path": ["locate-path@7.2.0", "", { "dependencies": { "p-locate": "^6.0.0" } }, "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA=="],
-
- "@netlify/config/find-up/unicorn-magic": ["unicorn-magic@0.1.0", "", {}, "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ=="],
-
- "@netlify/config/yargs/cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
-
- "@netlify/config/yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
-
- "@netlify/config/yargs/yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
-
- "@netlify/edge-bundler/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.0", "", { "os": "aix", "cpu": "ppc64" }, "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA=="],
-
- "@netlify/edge-bundler/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.28.0", "", { "os": "android", "cpu": "arm" }, "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ=="],
-
- "@netlify/edge-bundler/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.28.0", "", { "os": "android", "cpu": "arm64" }, "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw=="],
-
- "@netlify/edge-bundler/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.28.0", "", { "os": "android", "cpu": "x64" }, "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA=="],
-
- "@netlify/edge-bundler/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.28.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q=="],
-
- "@netlify/edge-bundler/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.28.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ=="],
-
- "@netlify/edge-bundler/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.28.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q=="],
-
- "@netlify/edge-bundler/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.28.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw=="],
-
- "@netlify/edge-bundler/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.28.0", "", { "os": "linux", "cpu": "arm" }, "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw=="],
-
- "@netlify/edge-bundler/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.28.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A=="],
-
- "@netlify/edge-bundler/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.28.0", "", { "os": "linux", "cpu": "ia32" }, "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ=="],
-
- "@netlify/edge-bundler/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.28.0", "", { "os": "linux", "cpu": "none" }, "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg=="],
-
- "@netlify/edge-bundler/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.28.0", "", { "os": "linux", "cpu": "none" }, "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w=="],
-
- "@netlify/edge-bundler/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.28.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg=="],
-
- "@netlify/edge-bundler/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.28.0", "", { "os": "linux", "cpu": "none" }, "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ=="],
-
- "@netlify/edge-bundler/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.28.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q=="],
-
- "@netlify/edge-bundler/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.28.0", "", { "os": "linux", "cpu": "x64" }, "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ=="],
-
- "@netlify/edge-bundler/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.28.0", "", { "os": "none", "cpu": "arm64" }, "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw=="],
-
- "@netlify/edge-bundler/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.28.0", "", { "os": "none", "cpu": "x64" }, "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw=="],
-
- "@netlify/edge-bundler/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.28.0", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g=="],
-
- "@netlify/edge-bundler/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.28.0", "", { "os": "openbsd", "cpu": "x64" }, "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA=="],
-
- "@netlify/edge-bundler/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.28.0", "", { "os": "none", "cpu": "arm64" }, "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w=="],
-
- "@netlify/edge-bundler/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.28.0", "", { "os": "sunos", "cpu": "x64" }, "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw=="],
-
- "@netlify/edge-bundler/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.28.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA=="],
-
- "@netlify/edge-bundler/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.28.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA=="],
-
- "@netlify/edge-bundler/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.0", "", { "os": "win32", "cpu": "x64" }, "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw=="],
-
- "@netlify/edge-bundler/execa/get-stream": ["get-stream@8.0.1", "", {}, "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA=="],
-
- "@netlify/edge-bundler/execa/human-signals": ["human-signals@5.0.0", "", {}, "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ=="],
-
- "@netlify/edge-bundler/execa/is-stream": ["is-stream@3.0.0", "", {}, "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA=="],
-
- "@netlify/edge-bundler/execa/npm-run-path": ["npm-run-path@5.3.0", "", { "dependencies": { "path-key": "^4.0.0" } }, "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ=="],
-
- "@netlify/edge-bundler/execa/onetime": ["onetime@6.0.0", "", { "dependencies": { "mimic-fn": "^4.0.0" } }, "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ=="],
-
- "@netlify/edge-bundler/execa/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
-
- "@netlify/edge-bundler/execa/strip-final-newline": ["strip-final-newline@3.0.0", "", {}, "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="],
-
- "@netlify/edge-bundler/find-up/locate-path": ["locate-path@7.2.0", "", { "dependencies": { "p-locate": "^6.0.0" } }, "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA=="],
-
- "@netlify/edge-bundler/find-up/unicorn-magic": ["unicorn-magic@0.1.0", "", {}, "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ=="],
-
- "@netlify/git-utils/execa/get-stream": ["get-stream@8.0.1", "", {}, "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA=="],
-
- "@netlify/git-utils/execa/human-signals": ["human-signals@5.0.0", "", {}, "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ=="],
-
- "@netlify/git-utils/execa/is-stream": ["is-stream@3.0.0", "", {}, "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA=="],
-
- "@netlify/git-utils/execa/npm-run-path": ["npm-run-path@5.3.0", "", { "dependencies": { "path-key": "^4.0.0" } }, "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ=="],
-
- "@netlify/git-utils/execa/onetime": ["onetime@6.0.0", "", { "dependencies": { "mimic-fn": "^4.0.0" } }, "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ=="],
-
- "@netlify/git-utils/execa/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
-
- "@netlify/git-utils/execa/strip-final-newline": ["strip-final-newline@3.0.0", "", {}, "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="],
-
- "@netlify/run-utils/execa/get-stream": ["get-stream@8.0.1", "", {}, "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA=="],
-
- "@netlify/run-utils/execa/human-signals": ["human-signals@5.0.0", "", {}, "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ=="],
-
- "@netlify/run-utils/execa/is-stream": ["is-stream@3.0.0", "", {}, "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA=="],
-
- "@netlify/run-utils/execa/npm-run-path": ["npm-run-path@5.3.0", "", { "dependencies": { "path-key": "^4.0.0" } }, "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ=="],
-
- "@netlify/run-utils/execa/onetime": ["onetime@6.0.0", "", { "dependencies": { "mimic-fn": "^4.0.0" } }, "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ=="],
-
- "@netlify/run-utils/execa/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
-
- "@netlify/run-utils/execa/strip-final-newline": ["strip-final-newline@3.0.0", "", {}, "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="],
-
- "@netlify/zip-it-and-ship-it/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.0", "", { "os": "aix", "cpu": "ppc64" }, "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA=="],
-
- "@netlify/zip-it-and-ship-it/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.28.0", "", { "os": "android", "cpu": "arm" }, "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ=="],
-
- "@netlify/zip-it-and-ship-it/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.28.0", "", { "os": "android", "cpu": "arm64" }, "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw=="],
-
- "@netlify/zip-it-and-ship-it/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.28.0", "", { "os": "android", "cpu": "x64" }, "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA=="],
-
- "@netlify/zip-it-and-ship-it/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.28.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q=="],
-
- "@netlify/zip-it-and-ship-it/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.28.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ=="],
-
- "@netlify/zip-it-and-ship-it/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.28.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q=="],
-
- "@netlify/zip-it-and-ship-it/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.28.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw=="],
-
- "@netlify/zip-it-and-ship-it/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.28.0", "", { "os": "linux", "cpu": "arm" }, "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw=="],
-
- "@netlify/zip-it-and-ship-it/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.28.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A=="],
-
- "@netlify/zip-it-and-ship-it/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.28.0", "", { "os": "linux", "cpu": "ia32" }, "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ=="],
-
- "@netlify/zip-it-and-ship-it/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.28.0", "", { "os": "linux", "cpu": "none" }, "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg=="],
-
- "@netlify/zip-it-and-ship-it/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.28.0", "", { "os": "linux", "cpu": "none" }, "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w=="],
-
- "@netlify/zip-it-and-ship-it/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.28.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg=="],
-
- "@netlify/zip-it-and-ship-it/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.28.0", "", { "os": "linux", "cpu": "none" }, "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ=="],
-
- "@netlify/zip-it-and-ship-it/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.28.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q=="],
-
- "@netlify/zip-it-and-ship-it/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.28.0", "", { "os": "linux", "cpu": "x64" }, "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ=="],
-
- "@netlify/zip-it-and-ship-it/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.28.0", "", { "os": "none", "cpu": "arm64" }, "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw=="],
-
- "@netlify/zip-it-and-ship-it/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.28.0", "", { "os": "none", "cpu": "x64" }, "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw=="],
-
- "@netlify/zip-it-and-ship-it/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.28.0", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g=="],
-
- "@netlify/zip-it-and-ship-it/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.28.0", "", { "os": "openbsd", "cpu": "x64" }, "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA=="],
-
- "@netlify/zip-it-and-ship-it/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.28.0", "", { "os": "none", "cpu": "arm64" }, "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w=="],
-
- "@netlify/zip-it-and-ship-it/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.28.0", "", { "os": "sunos", "cpu": "x64" }, "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw=="],
-
- "@netlify/zip-it-and-ship-it/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.28.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA=="],
-
- "@netlify/zip-it-and-ship-it/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.28.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA=="],
-
- "@netlify/zip-it-and-ship-it/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.0", "", { "os": "win32", "cpu": "x64" }, "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw=="],
-
- "@netlify/zip-it-and-ship-it/execa/get-stream": ["get-stream@8.0.1", "", {}, "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA=="],
-
- "@netlify/zip-it-and-ship-it/execa/human-signals": ["human-signals@5.0.0", "", {}, "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ=="],
-
- "@netlify/zip-it-and-ship-it/execa/is-stream": ["is-stream@3.0.0", "", {}, "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA=="],
-
- "@netlify/zip-it-and-ship-it/execa/npm-run-path": ["npm-run-path@5.3.0", "", { "dependencies": { "path-key": "^4.0.0" } }, "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ=="],
-
- "@netlify/zip-it-and-ship-it/execa/onetime": ["onetime@6.0.0", "", { "dependencies": { "mimic-fn": "^4.0.0" } }, "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ=="],
-
- "@netlify/zip-it-and-ship-it/execa/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
-
- "@netlify/zip-it-and-ship-it/execa/strip-final-newline": ["strip-final-newline@3.0.0", "", {}, "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="],
-
- "@netlify/zip-it-and-ship-it/find-up/unicorn-magic": ["unicorn-magic@0.1.0", "", {}, "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ=="],
-
- "@netlify/zip-it-and-ship-it/yargs/cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
-
- "@netlify/zip-it-and-ship-it/yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
-
- "@netlify/zip-it-and-ship-it/yargs/yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
-
- "@ts-morph/common/minimatch/brace-expansion": ["brace-expansion@1.1.15", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg=="],
-
- "@vercel/backends/@vercel/nft/picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="],
-
- "@vercel/backends/execa/get-stream": ["get-stream@5.2.0", "", { "dependencies": { "pump": "^3.0.0" } }, "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA=="],
-
- "@vercel/backends/execa/human-signals": ["human-signals@1.1.1", "", {}, "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw=="],
-
- "@vercel/backends/execa/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
-
- "@vercel/backends/tsx/esbuild": ["esbuild@0.27.0", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.0", "@esbuild/android-arm": "0.27.0", "@esbuild/android-arm64": "0.27.0", "@esbuild/android-x64": "0.27.0", "@esbuild/darwin-arm64": "0.27.0", "@esbuild/darwin-x64": "0.27.0", "@esbuild/freebsd-arm64": "0.27.0", "@esbuild/freebsd-x64": "0.27.0", "@esbuild/linux-arm": "0.27.0", "@esbuild/linux-arm64": "0.27.0", "@esbuild/linux-ia32": "0.27.0", "@esbuild/linux-loong64": "0.27.0", "@esbuild/linux-mips64el": "0.27.0", "@esbuild/linux-ppc64": "0.27.0", "@esbuild/linux-riscv64": "0.27.0", "@esbuild/linux-s390x": "0.27.0", "@esbuild/linux-x64": "0.27.0", "@esbuild/netbsd-arm64": "0.27.0", "@esbuild/netbsd-x64": "0.27.0", "@esbuild/openbsd-arm64": "0.27.0", "@esbuild/openbsd-x64": "0.27.0", "@esbuild/openharmony-arm64": "0.27.0", "@esbuild/sunos-x64": "0.27.0", "@esbuild/win32-arm64": "0.27.0", "@esbuild/win32-ia32": "0.27.0", "@esbuild/win32-x64": "0.27.0" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA=="],
-
- "@vercel/cli-auth/open/define-lazy-prop": ["define-lazy-prop@2.0.0", "", {}, "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og=="],
-
- "@vercel/cli-auth/open/is-docker": ["is-docker@2.2.1", "", { "bin": { "is-docker": "cli.js" } }, "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="],
-
- "@vercel/cli-auth/open/is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="],
-
- "@vercel/express/@vercel/nft/picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="],
-
- "@vercel/fun/debug/ms": ["ms@2.1.2", "", {}, "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="],
-
- "@vercel/fun/semver/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="],
-
- "@vercel/gatsby-plugin-vercel-builder/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.0", "", { "os": "aix", "cpu": "ppc64" }, "sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A=="],
-
- "@vercel/gatsby-plugin-vercel-builder/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.0", "", { "os": "android", "cpu": "arm" }, "sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ=="],
-
- "@vercel/gatsby-plugin-vercel-builder/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.0", "", { "os": "android", "cpu": "arm64" }, "sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ=="],
-
- "@vercel/gatsby-plugin-vercel-builder/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.27.0", "", { "os": "android", "cpu": "x64" }, "sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q=="],
-
- "@vercel/gatsby-plugin-vercel-builder/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg=="],
-
- "@vercel/gatsby-plugin-vercel-builder/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g=="],
-
- "@vercel/gatsby-plugin-vercel-builder/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw=="],
-
- "@vercel/gatsby-plugin-vercel-builder/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g=="],
-
- "@vercel/gatsby-plugin-vercel-builder/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.0", "", { "os": "linux", "cpu": "arm" }, "sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ=="],
-
- "@vercel/gatsby-plugin-vercel-builder/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ=="],
-
- "@vercel/gatsby-plugin-vercel-builder/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.0", "", { "os": "linux", "cpu": "ia32" }, "sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw=="],
-
- "@vercel/gatsby-plugin-vercel-builder/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.0", "", { "os": "linux", "cpu": "none" }, "sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg=="],
-
- "@vercel/gatsby-plugin-vercel-builder/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.0", "", { "os": "linux", "cpu": "none" }, "sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg=="],
-
- "@vercel/gatsby-plugin-vercel-builder/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA=="],
-
- "@vercel/gatsby-plugin-vercel-builder/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.0", "", { "os": "linux", "cpu": "none" }, "sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ=="],
-
- "@vercel/gatsby-plugin-vercel-builder/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w=="],
-
- "@vercel/gatsby-plugin-vercel-builder/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.0", "", { "os": "linux", "cpu": "x64" }, "sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw=="],
-
- "@vercel/gatsby-plugin-vercel-builder/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.0", "", { "os": "none", "cpu": "arm64" }, "sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w=="],
-
- "@vercel/gatsby-plugin-vercel-builder/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.0", "", { "os": "none", "cpu": "x64" }, "sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA=="],
-
- "@vercel/gatsby-plugin-vercel-builder/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.0", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ=="],
-
- "@vercel/gatsby-plugin-vercel-builder/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.0", "", { "os": "openbsd", "cpu": "x64" }, "sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A=="],
-
- "@vercel/gatsby-plugin-vercel-builder/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.0", "", { "os": "none", "cpu": "arm64" }, "sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA=="],
-
- "@vercel/gatsby-plugin-vercel-builder/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.0", "", { "os": "sunos", "cpu": "x64" }, "sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA=="],
-
- "@vercel/gatsby-plugin-vercel-builder/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg=="],
-
- "@vercel/gatsby-plugin-vercel-builder/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ=="],
-
- "@vercel/gatsby-plugin-vercel-builder/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.0", "", { "os": "win32", "cpu": "x64" }, "sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg=="],
-
- "@vercel/hono/@vercel/nft/picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="],
-
- "@vercel/next/@vercel/nft/picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="],
-
- "@vercel/nft/glob/minimatch": ["minimatch@9.0.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="],
-
- "@vercel/nft/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
-
- "@vercel/node/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
-
- "@vercel/node/@vercel/nft/picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="],
-
- "@vercel/node/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.0", "", { "os": "aix", "cpu": "ppc64" }, "sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A=="],
-
- "@vercel/node/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.0", "", { "os": "android", "cpu": "arm" }, "sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ=="],
-
- "@vercel/node/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.0", "", { "os": "android", "cpu": "arm64" }, "sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ=="],
-
- "@vercel/node/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.27.0", "", { "os": "android", "cpu": "x64" }, "sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q=="],
-
- "@vercel/node/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg=="],
-
- "@vercel/node/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g=="],
-
- "@vercel/node/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw=="],
-
- "@vercel/node/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g=="],
-
- "@vercel/node/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.0", "", { "os": "linux", "cpu": "arm" }, "sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ=="],
-
- "@vercel/node/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ=="],
-
- "@vercel/node/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.0", "", { "os": "linux", "cpu": "ia32" }, "sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw=="],
-
- "@vercel/node/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.0", "", { "os": "linux", "cpu": "none" }, "sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg=="],
-
- "@vercel/node/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.0", "", { "os": "linux", "cpu": "none" }, "sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg=="],
-
- "@vercel/node/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA=="],
-
- "@vercel/node/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.0", "", { "os": "linux", "cpu": "none" }, "sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ=="],
-
- "@vercel/node/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w=="],
-
- "@vercel/node/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.0", "", { "os": "linux", "cpu": "x64" }, "sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw=="],
-
- "@vercel/node/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.0", "", { "os": "none", "cpu": "arm64" }, "sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w=="],
-
- "@vercel/node/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.0", "", { "os": "none", "cpu": "x64" }, "sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA=="],
-
- "@vercel/node/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.0", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ=="],
-
- "@vercel/node/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.0", "", { "os": "openbsd", "cpu": "x64" }, "sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A=="],
-
- "@vercel/node/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.0", "", { "os": "none", "cpu": "arm64" }, "sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA=="],
-
- "@vercel/node/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.0", "", { "os": "sunos", "cpu": "x64" }, "sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA=="],
-
- "@vercel/node/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg=="],
-
- "@vercel/node/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ=="],
-
- "@vercel/node/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.0", "", { "os": "win32", "cpu": "x64" }, "sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg=="],
-
- "@vercel/node/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
-
- "@vercel/redwood/@vercel/nft/picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="],
-
- "@vercel/remix-builder/@vercel/nft/picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="],
-
- "@vue/compiler-sfc/postcss/nanoid": ["nanoid@3.3.15", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA=="],
-
- "ansi-align/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
-
- "archiver-utils/glob/minimatch": ["minimatch@9.0.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="],
-
- "archiver-utils/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
-
- "archiver-utils/readable-stream/string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
-
- "archiver/readable-stream/string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
-
- "better-ajv-errors/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
-
- "better-ajv-errors/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
-
- "bl/readable-stream/string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
-
- "cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
-
- "color/color-convert/color-name": ["color-name@2.1.0", "", {}, "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg=="],
-
- "compress-commons/readable-stream/string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
-
- "crc32-stream/readable-stream/string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
-
- "csso/css-tree/mdn-data": ["mdn-data@2.0.28", "", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="],
-
- "edge-runtime/pretty-ms/parse-ms": ["parse-ms@2.1.0", "", {}, "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA=="],
-
- "extract-zip/get-stream/pump": ["pump@3.0.4", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA=="],
-
- "extract-zip/yauzl/buffer-crc32": ["buffer-crc32@0.2.13", "", {}, "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ=="],
-
- "inquirer-autocomplete-prompt/ansi-escapes/type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="],
-
- "inquirer-autocomplete-prompt/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
-
- "inquirer-autocomplete-prompt/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
-
- "inquirer-autocomplete-prompt/figures/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="],
-
- "inquirer-autocomplete-prompt/rxjs/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="],
-
- "inquirer/ansi-escapes/type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="],
-
- "inquirer/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
-
- "inquirer/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
-
- "inquirer/figures/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="],
-
- "inquirer/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
-
- "inquirer/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
-
- "log-symbols/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
-
- "log-symbols/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
-
- "log-update/cli-cursor/restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="],
-
- "log-update/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
-
- "log-update/wrap-ansi/string-width": ["string-width@8.2.1", "", { "dependencies": { "get-east-asian-width": "^1.5.0", "strip-ansi": "^7.1.2" } }, "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA=="],
-
- "micro/raw-body/bytes": ["bytes@3.1.0", "", {}, "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="],
-
- "micro/raw-body/http-errors": ["http-errors@1.7.3", "", { "dependencies": { "depd": "~1.1.2", "inherits": "2.0.4", "setprototypeof": "1.1.1", "statuses": ">= 1.5.0 < 2", "toidentifier": "1.0.0" } }, "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw=="],
-
- "micro/raw-body/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
-
- "multiparty/http-errors/statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="],
-
- "ora/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
-
- "ora/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
-
- "precinct/postcss/nanoid": ["nanoid@3.3.15", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA=="],
-
- "read-package-up/read-pkg/normalize-package-data": ["normalize-package-data@8.0.0", "", { "dependencies": { "hosted-git-info": "^9.0.0", "semver": "^7.3.5", "validate-npm-package-license": "^3.0.4" } }, "sha512-RWk+PI433eESQ7ounYxIp67CYuVsS1uYSonX3kA6ps/3LWfjVQa/ptEg6Y3T6uAMq1mWpX9PQ+qx+QaHpsc7gQ=="],
-
- "read-package-up/read-pkg/unicorn-magic": ["unicorn-magic@0.4.0", "", {}, "sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw=="],
-
- "read-pkg/normalize-package-data/hosted-git-info": ["hosted-git-info@7.0.2", "", { "dependencies": { "lru-cache": "^10.0.1" } }, "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w=="],
-
- "readdir-glob/minimatch/brace-expansion": ["brace-expansion@2.1.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA=="],
-
- "string-width/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
-
- "unstorage/chokidar/readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="],
-
- "vercel/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.0", "", { "os": "aix", "cpu": "ppc64" }, "sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A=="],
-
- "vercel/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.0", "", { "os": "android", "cpu": "arm" }, "sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ=="],
-
- "vercel/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.0", "", { "os": "android", "cpu": "arm64" }, "sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ=="],
-
- "vercel/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.27.0", "", { "os": "android", "cpu": "x64" }, "sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q=="],
-
- "vercel/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg=="],
-
- "vercel/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g=="],
-
- "vercel/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw=="],
-
- "vercel/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g=="],
-
- "vercel/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.0", "", { "os": "linux", "cpu": "arm" }, "sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ=="],
-
- "vercel/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ=="],
-
- "vercel/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.0", "", { "os": "linux", "cpu": "ia32" }, "sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw=="],
-
- "vercel/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.0", "", { "os": "linux", "cpu": "none" }, "sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg=="],
-
- "vercel/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.0", "", { "os": "linux", "cpu": "none" }, "sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg=="],
-
- "vercel/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA=="],
-
- "vercel/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.0", "", { "os": "linux", "cpu": "none" }, "sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ=="],
-
- "vercel/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w=="],
-
- "vercel/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.0", "", { "os": "linux", "cpu": "x64" }, "sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw=="],
-
- "vercel/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.0", "", { "os": "none", "cpu": "arm64" }, "sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w=="],
-
- "vercel/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.0", "", { "os": "none", "cpu": "x64" }, "sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA=="],
-
- "vercel/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.0", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ=="],
-
- "vercel/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.0", "", { "os": "openbsd", "cpu": "x64" }, "sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A=="],
-
- "vercel/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.0", "", { "os": "none", "cpu": "arm64" }, "sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA=="],
-
- "vercel/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.0", "", { "os": "sunos", "cpu": "x64" }, "sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA=="],
-
- "vercel/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg=="],
-
- "vercel/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ=="],
-
- "vercel/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.0", "", { "os": "win32", "cpu": "x64" }, "sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg=="],
-
- "windows-release/execa/get-stream": ["get-stream@8.0.1", "", {}, "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA=="],
-
- "windows-release/execa/human-signals": ["human-signals@5.0.0", "", {}, "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ=="],
-
- "windows-release/execa/is-stream": ["is-stream@3.0.0", "", {}, "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA=="],
-
- "windows-release/execa/npm-run-path": ["npm-run-path@5.3.0", "", { "dependencies": { "path-key": "^4.0.0" } }, "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ=="],
-
- "windows-release/execa/onetime": ["onetime@6.0.0", "", { "dependencies": { "mimic-fn": "^4.0.0" } }, "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ=="],
-
- "windows-release/execa/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
-
- "windows-release/execa/strip-final-newline": ["strip-final-newline@3.0.0", "", {}, "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="],
-
- "winston-transport/readable-stream/string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
-
- "winston/readable-stream/string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
-
- "wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
-
- "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
-
- "yauzl-promise/yauzl/buffer-crc32": ["buffer-crc32@0.2.13", "", {}, "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ=="],
-
- "zip-stream/readable-stream/string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
-
- "@netlify/build-info/yargs/cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
-
- "@netlify/build-info/yargs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
-
- "@netlify/build/terminal-link/supports-hyperlinks/has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
-
- "@netlify/build/terminal-link/supports-hyperlinks/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
-
- "@netlify/build/yargs/cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
-
- "@netlify/build/yargs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
-
- "@netlify/config/yargs/cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
-
- "@netlify/config/yargs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
-
- "@netlify/zip-it-and-ship-it/yargs/cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
-
- "@netlify/zip-it-and-ship-it/yargs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
-
- "@ts-morph/common/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
-
- "@vercel/backends/execa/get-stream/pump": ["pump@3.0.4", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA=="],
-
- "@vercel/backends/tsx/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.0", "", { "os": "aix", "cpu": "ppc64" }, "sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A=="],
-
- "@vercel/backends/tsx/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.0", "", { "os": "android", "cpu": "arm" }, "sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ=="],
-
- "@vercel/backends/tsx/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.0", "", { "os": "android", "cpu": "arm64" }, "sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ=="],
-
- "@vercel/backends/tsx/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.27.0", "", { "os": "android", "cpu": "x64" }, "sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q=="],
-
- "@vercel/backends/tsx/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg=="],
-
- "@vercel/backends/tsx/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g=="],
-
- "@vercel/backends/tsx/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw=="],
-
- "@vercel/backends/tsx/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g=="],
-
- "@vercel/backends/tsx/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.0", "", { "os": "linux", "cpu": "arm" }, "sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ=="],
-
- "@vercel/backends/tsx/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ=="],
-
- "@vercel/backends/tsx/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.0", "", { "os": "linux", "cpu": "ia32" }, "sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw=="],
-
- "@vercel/backends/tsx/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.0", "", { "os": "linux", "cpu": "none" }, "sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg=="],
-
- "@vercel/backends/tsx/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.0", "", { "os": "linux", "cpu": "none" }, "sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg=="],
-
- "@vercel/backends/tsx/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA=="],
-
- "@vercel/backends/tsx/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.0", "", { "os": "linux", "cpu": "none" }, "sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ=="],
-
- "@vercel/backends/tsx/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w=="],
-
- "@vercel/backends/tsx/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.0", "", { "os": "linux", "cpu": "x64" }, "sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw=="],
-
- "@vercel/backends/tsx/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.0", "", { "os": "none", "cpu": "arm64" }, "sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w=="],
-
- "@vercel/backends/tsx/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.0", "", { "os": "none", "cpu": "x64" }, "sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA=="],
-
- "@vercel/backends/tsx/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.0", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ=="],
-
- "@vercel/backends/tsx/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.0", "", { "os": "openbsd", "cpu": "x64" }, "sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A=="],
-
- "@vercel/backends/tsx/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.0", "", { "os": "none", "cpu": "arm64" }, "sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA=="],
-
- "@vercel/backends/tsx/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.0", "", { "os": "sunos", "cpu": "x64" }, "sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA=="],
-
- "@vercel/backends/tsx/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg=="],
+ "tinyglobby/picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="],
- "@vercel/backends/tsx/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ=="],
+ "tsup/chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
- "@vercel/backends/tsx/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.0", "", { "os": "win32", "cpu": "x64" }, "sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg=="],
+ "tsup/esbuild": ["esbuild@0.27.7", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.7", "@esbuild/android-arm": "0.27.7", "@esbuild/android-arm64": "0.27.7", "@esbuild/android-x64": "0.27.7", "@esbuild/darwin-arm64": "0.27.7", "@esbuild/darwin-x64": "0.27.7", "@esbuild/freebsd-arm64": "0.27.7", "@esbuild/freebsd-x64": "0.27.7", "@esbuild/linux-arm": "0.27.7", "@esbuild/linux-arm64": "0.27.7", "@esbuild/linux-ia32": "0.27.7", "@esbuild/linux-loong64": "0.27.7", "@esbuild/linux-mips64el": "0.27.7", "@esbuild/linux-ppc64": "0.27.7", "@esbuild/linux-riscv64": "0.27.7", "@esbuild/linux-s390x": "0.27.7", "@esbuild/linux-x64": "0.27.7", "@esbuild/netbsd-arm64": "0.27.7", "@esbuild/netbsd-x64": "0.27.7", "@esbuild/openbsd-arm64": "0.27.7", "@esbuild/openbsd-x64": "0.27.7", "@esbuild/openharmony-arm64": "0.27.7", "@esbuild/sunos-x64": "0.27.7", "@esbuild/win32-arm64": "0.27.7", "@esbuild/win32-ia32": "0.27.7", "@esbuild/win32-x64": "0.27.7" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w=="],
- "@vercel/fun/semver/lru-cache/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
+ "tsup/chokidar/readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
- "@vercel/nft/glob/minimatch/brace-expansion": ["brace-expansion@2.1.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA=="],
+ "tsup/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.7", "", { "os": "aix", "cpu": "ppc64" }, "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg=="],
- "@vercel/nft/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
+ "tsup/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.7", "", { "os": "android", "cpu": "arm" }, "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ=="],
- "archiver-utils/glob/minimatch/brace-expansion": ["brace-expansion@2.1.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA=="],
+ "tsup/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.7", "", { "os": "android", "cpu": "arm64" }, "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ=="],
- "archiver-utils/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
+ "tsup/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.27.7", "", { "os": "android", "cpu": "x64" }, "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg=="],
- "better-ajv-errors/chalk/supports-color/has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
+ "tsup/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw=="],
- "extract-zip/get-stream/pump/once": ["once@1.3.3", "", { "dependencies": { "wrappy": "1" } }, "sha512-6vaNInhu+CHxtONf3zw3vq4SP2DOQhjBvIa3rNcG0+P7eKWlYH6Peu7rHizSloRU2EwMz6GraLieis9Ac9+p1w=="],
+ "tsup/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ=="],
- "inquirer-autocomplete-prompt/chalk/supports-color/has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
+ "tsup/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.7", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w=="],
- "inquirer/chalk/supports-color/has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
+ "tsup/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.7", "", { "os": "freebsd", "cpu": "x64" }, "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ=="],
- "log-symbols/chalk/supports-color/has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
+ "tsup/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.7", "", { "os": "linux", "cpu": "arm" }, "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA=="],
- "log-update/cli-cursor/restore-cursor/onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="],
+ "tsup/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A=="],
- "log-update/cli-cursor/restore-cursor/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
+ "tsup/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.7", "", { "os": "linux", "cpu": "ia32" }, "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg=="],
- "micro/raw-body/http-errors/depd": ["depd@1.1.2", "", {}, "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ=="],
+ "tsup/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q=="],
- "micro/raw-body/http-errors/setprototypeof": ["setprototypeof@1.1.1", "", {}, "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="],
+ "tsup/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw=="],
- "micro/raw-body/http-errors/statuses": ["statuses@1.5.0", "", {}, "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA=="],
+ "tsup/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.7", "", { "os": "linux", "cpu": "ppc64" }, "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ=="],
- "micro/raw-body/http-errors/toidentifier": ["toidentifier@1.0.0", "", {}, "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="],
+ "tsup/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ=="],
- "ora/chalk/supports-color/has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
+ "tsup/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.7", "", { "os": "linux", "cpu": "s390x" }, "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw=="],
- "read-package-up/read-pkg/normalize-package-data/hosted-git-info": ["hosted-git-info@9.0.3", "", { "dependencies": { "lru-cache": "^11.1.0" } }, "sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg=="],
+ "tsup/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.7", "", { "os": "linux", "cpu": "x64" }, "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA=="],
- "read-pkg/normalize-package-data/hosted-git-info/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
+ "tsup/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.7", "", { "os": "none", "cpu": "arm64" }, "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w=="],
- "readdir-glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
+ "tsup/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.7", "", { "os": "none", "cpu": "x64" }, "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw=="],
- "@netlify/build-info/yargs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
+ "tsup/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.7", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A=="],
- "@netlify/build/yargs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
+ "tsup/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.7", "", { "os": "openbsd", "cpu": "x64" }, "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg=="],
- "@netlify/config/yargs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
+ "tsup/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.7", "", { "os": "none", "cpu": "arm64" }, "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw=="],
- "@netlify/zip-it-and-ship-it/yargs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
+ "tsup/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.7", "", { "os": "sunos", "cpu": "x64" }, "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA=="],
- "@vercel/backends/execa/get-stream/pump/once": ["once@1.3.3", "", { "dependencies": { "wrappy": "1" } }, "sha512-6vaNInhu+CHxtONf3zw3vq4SP2DOQhjBvIa3rNcG0+P7eKWlYH6Peu7rHizSloRU2EwMz6GraLieis9Ac9+p1w=="],
+ "tsup/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.7", "", { "os": "win32", "cpu": "arm64" }, "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA=="],
- "@vercel/nft/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
+ "tsup/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.7", "", { "os": "win32", "cpu": "ia32" }, "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw=="],
- "archiver-utils/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
+ "tsup/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.7", "", { "os": "win32", "cpu": "x64" }, "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg=="],
}
}
diff --git a/bunfig.toml b/bunfig.toml
deleted file mode 100644
index 81321dc..0000000
--- a/bunfig.toml
+++ /dev/null
@@ -1,3 +0,0 @@
-[test]
-coverage = true
-coverageThreshold = 0.9
\ No newline at end of file
diff --git a/package.json b/package.json
index 584a7ea..99ee5de 100644
--- a/package.json
+++ b/package.json
@@ -15,22 +15,22 @@
"bun-types": "latest",
"cheerio": "^1.2.0",
"dayjs": "^1.11.21",
+ "domhandler": "^5.0.3",
"elysia": "^1.4.29",
"feed": "^5.2.1",
- "netlify-cli": "latest",
"ofetch": "^1.5.1",
"sanitize-html": "^2.17.5",
+ "tsup": "^8.5.1",
"tsx": "latest",
"unstorage": "1.17.5",
- "uuid": "^13.0.2",
- "vercel": "latest",
- "wrangler": "latest"
+ "uuid": "^13.0.2"
},
"devDependencies": {
"@biomejs/biome": "catalog:",
"@kitajs/ts-html-plugin": "catalog:",
"@types/bun": "catalog:",
- "@types/sanitize-html": "catalog:"
+ "@types/sanitize-html": "catalog:",
+ "typescript": "^6.0.3"
},
"name": "rssbook-monorepo",
"packageManager": "bun@1.3.2",
@@ -43,12 +43,16 @@
"check:type:packages": "bunx tsc -p pkgs/rssbook/tsconfig.json --noEmit && bunx tsc -p pkgs/utils-feed-helper/tsconfig.json --noEmit && bunx tsc -p platform/cloudflare/tsconfig.json --noEmit && bunx tsc -p platform/deno/tsconfig.json --noEmit && bunx tsc -p platform/netlify/tsconfig.json --noEmit && bunx tsc -p platform/node/tsconfig.json --noEmit && bunx tsc -p platform/vercel/tsconfig.json --noEmit",
"check:type:root": "bunx tsc --noEmit",
"dev": "bun run --cwd pkgs/rssbook dev",
+ "fix": "bunx biome check --write .",
+ "release:major": "cd pkgs/rssbook && npm version major && git push --follow-tags",
+ "release:minor": "cd pkgs/rssbook && npm version minor && git push --follow-tags",
+ "release:patch": "cd pkgs/rssbook && npm version patch && git push --follow-tags",
"source:new": "bun run scripts/add.source.ts",
"source:test": "bun run scripts/test.source.ts",
- "source:test:all": "bun test ./pkgs/rssbook/src/routers",
+ "source:test:all": "bun test ./pkgs/rssbook/src/routers/feeds",
"start": "bun run --cwd pkgs/rssbook start",
- "test": "xss-scan && bun test ./pkgs/rssbook/src/tests",
- "test:cov": "bun test --coverage ./pkgs/rssbook/src/tests"
+ "test": "xss-scan && bun test ./pkgs/rssbook/src --path-ignore-patterns='**/routers/feeds/**'",
+ "test:cov": "bun test --coverage ./pkgs/rssbook/src --path-ignore-patterns='**/routers/feeds/**'"
},
"workspaces": [
"pkgs/*",
diff --git a/pkgs/rssbook/package.json b/pkgs/rssbook/package.json
index a40afbb..b5ff7bc 100644
--- a/pkgs/rssbook/package.json
+++ b/pkgs/rssbook/package.json
@@ -13,6 +13,7 @@
"@rowanmanning/feed-parser": "catalog:",
"cheerio": "catalog:",
"dayjs": "catalog:",
+ "domhandler": "catalog:",
"elysia": "catalog:",
"feed": "catalog:",
"ofetch": "catalog:",
@@ -20,27 +21,34 @@
"unstorage": "catalog:",
"uuid": "catalog:"
},
+ "devDependencies": {
+ "tsup": "catalog:"
+ },
"exports": {
- ".": "./src/RSSBookApp.ts",
- "./bun": "./src/index.ts",
- "./utils": "./src/utils/index.ts"
+ ".": {
+ "import": "./dist/index.js",
+ "types": "./dist/index.d.ts"
+ }
},
+ "files": [
+ "dist"
+ ],
"homepage": "https://github.com/HackHTU/RSSBook",
"license": "MIT",
- "main": "src/RSSBookApp.ts",
"name": "rssbook",
"repository": {
"type": "git",
"url": "git://github.com/HackHTU/RSSBook.git"
},
"scripts": {
- "build": "bun build --define process.env.NODE_ENV='\"production\"' --minify-whitespace --minify-syntax --outfile ../../dist/bun/index.js --target bun src/index.ts",
+ "build": "rm -rf dist && bun build src/index.ts --target=bun --format=esm --packages=external --sourcemap=linked --outdir=dist && bunx tsup && mkdir -p dist/public/xsl && cp src/public/favicon.ico src/public/favicon.svg src/public/robots.txt dist/public/ && cp src/public/xsl/atom.xsl src/public/xsl/rss.xsl dist/public/xsl/",
+ "check": "tsc --noEmit",
"dev": "bun --watch src/index.ts",
- "start": "bun ../../dist/bun/index.js",
"version:major": "npm version major",
"version:minor": "npm version minor",
"version:patch": "npm version patch"
},
"type": "module",
+ "types": "./dist/index.d.ts",
"version": "0.0.1"
}
diff --git a/pkgs/rssbook/src/RSSBookApp.ts b/pkgs/rssbook/src/RSSBookApp.ts
index 634039d..29d727c 100644
--- a/pkgs/rssbook/src/RSSBookApp.ts
+++ b/pkgs/rssbook/src/RSSBookApp.ts
@@ -7,50 +7,53 @@ import {
initPlugin,
loggerPlugin,
openAPIPlugin,
- type RSSBookInitConfig,
+ type RSSBookBookConfig,
} from "@/plugins";
import { routePlugin } from "@/routers";
+import type { Cache } from "@/utils";
-type RSSBookAppConfig = RSSBookInitConfig & {
- enableFetchOnlineServer?: boolean;
- staticPlugin?: boolean;
-
+/**
+ * Configuration options for creating an RSSBook application.
+ */
+export interface RSSBookAppConfig {
adapter?: ElysiaAdapter;
-};
+ book?: RSSBookBookConfig;
+ cache?: Cache;
+ openapi?: {
+ /** Enable the online fetch server for proxying requests */
+ enableFetchOnlineServer?: boolean;
+ };
+ /** Enable the static file serving plugin */
+ static?: boolean;
+}
/**
- * RSSBook Main App
+ * Create RSSBook main app
* @param init RSSBook initialization config
* @return Elysia app instance
*/
-export const RSSBookApp = (init?: RSSBookAppConfig) => {
- if (init) {
- return (
- new Elysia({
- adapter: init?.adapter,
- name: "RSSBook/App",
- })
- // error boundary plugin
- .use(errorHandlerPlugin)
- // server timing plugin
- .use(serverTiming())
- // request logging
- .use(loggerPlugin)
- // generate openapi doc
- .use(openAPIPlugin(init?.enableFetchOnlineServer))
-
- // init RSSBook instance
- .use(initPlugin(init))
- // book plugin
- .use(bookPlugin)
- // sign routes plugin
- .use(routePlugin)
- // static assets plugin
- .use(assetsPlugin(init?.staticPlugin))
- );
- } else {
- return new Elysia({
+export const createRSSBookApp = (init?: RSSBookAppConfig) => {
+ return (
+ new Elysia({
+ adapter: init?.adapter,
name: "RSSBook/App",
- });
- }
+ })
+ // error boundary plugin
+ .use(errorHandlerPlugin)
+ // server timing plugin
+ .use(serverTiming())
+ // request logging
+ .use(loggerPlugin)
+ // generate openapi doc
+ .use(openAPIPlugin(init?.openapi?.enableFetchOnlineServer))
+
+ // init RSSBook instance
+ .use(initPlugin({ book: init?.book, cache: init?.cache }))
+ // book plugin
+ .use(bookPlugin)
+ // sign routes plugin
+ .use(routePlugin)
+ // static assets plugin
+ .use(assetsPlugin(init?.static))
+ );
};
diff --git a/pkgs/rssbook/src/books/index.test.ts b/pkgs/rssbook/src/books/index.test.ts
new file mode 100644
index 0000000..60b98a4
--- /dev/null
+++ b/pkgs/rssbook/src/books/index.test.ts
@@ -0,0 +1,223 @@
+import { afterAll, beforeAll, describe, expect, test } from "bun:test";
+import { createStorage } from "unstorage";
+import lruCacheDriver from "unstorage/drivers/lru-cache";
+import { getCachedBooksData } from "@/books";
+import Cache from "@/utils/cache";
+
+let server: ReturnType | null = null;
+let baseUrl = "";
+
+const requestCounts = {
+ feedA: 0,
+ feedB: 0,
+ invalid: 0,
+};
+
+let feedA = "";
+let feedB = "";
+let invalidFeed = "not xml";
+
+function createCache() {
+ return new Cache(
+ createStorage({
+ driver: lruCacheDriver({
+ ttl: 7 * 24 * 60 * 60 * 1000,
+ }),
+ }),
+ );
+}
+
+function resetFeeds() {
+ requestCounts.feedA = 0;
+ requestCounts.feedB = 0;
+ requestCounts.invalid = 0;
+
+ feedA = createRssFeed("Feed A", [
+ {
+ category: "Tech",
+ date: "Mon, 01 Jan 2025 10:00:00 GMT",
+ path: "a-old",
+ title: "Old Article",
+ },
+ {
+ category: "News",
+ date: "Wed, 03 Jan 2025 10:00:00 GMT",
+ path: "a-new",
+ title: "New Article",
+ },
+ ]);
+ feedB = createRssFeed("Feed B", [
+ {
+ category: "Books",
+ date: "Tue, 02 Jan 2025 10:00:00 GMT",
+ path: "b-only",
+ title: "Book Article",
+ },
+ ]);
+ invalidFeed = "not xml";
+}
+
+function createRssFeed(
+ title: string,
+ items: {
+ category: string;
+ date: string;
+ path: string;
+ title: string;
+ }[],
+) {
+ return /* xml */ `
+
+
+ ${title}
+ https://example.com/${title.toLowerCase().replaceAll(" ", "-")}
+ ${title} description
+ ${items
+ .map(
+ (item) => `-
+
${item.title}
+ https://example.com/${item.path}
+ ${item.title} description
+ ${item.date}
+ ${item.category}
+ `,
+ )
+ .join("")}
+
+ `;
+}
+
+function startServer() {
+ const startPort = 13621 + Math.floor(Math.random() * 1000);
+
+ for (let offset = 0; offset < 20; offset += 1) {
+ try {
+ return Bun.serve({
+ port: startPort + offset,
+ routes: {
+ "/feed-a": () => {
+ requestCounts.feedA += 1;
+ return new Response(feedA, {
+ headers: { "content-type": "application/rss+xml" },
+ });
+ },
+ "/feed-b": () => {
+ requestCounts.feedB += 1;
+ return new Response(feedB, {
+ headers: { "content-type": "application/rss+xml" },
+ });
+ },
+ "/invalid": () => {
+ requestCounts.invalid += 1;
+ return new Response(invalidFeed, {
+ headers: { "content-type": "application/rss+xml" },
+ });
+ },
+ },
+ });
+ } catch {}
+ }
+
+ throw new Error("Failed to start books test feed server");
+}
+
+beforeAll(() => {
+ resetFeeds();
+ server = startServer();
+ baseUrl = `http://localhost:${server.port}`;
+});
+
+afterAll(() => {
+ server?.stop();
+});
+
+describe("getCachedBooksData", () => {
+ test("fetches, sorts, and extracts categories", async () => {
+ resetFeeds();
+ const cache = createCache();
+
+ const result = await getCachedBooksData([`${baseUrl}/feed-a`], cache, 10_000);
+
+ expect(result.sortedData.item?.map((item) => item.title)).toEqual([
+ "New Article",
+ "Old Article",
+ ]);
+ expect(result.categories).toEqual([{ name: "News" }, { name: "Tech" }]);
+ expect(requestCounts.feedA).toBe(1);
+ });
+
+ test("uses fresh cache before TTL expires", async () => {
+ resetFeeds();
+ const cache = createCache();
+
+ await getCachedBooksData([`${baseUrl}/feed-a`], cache, 10_000);
+ const result = await getCachedBooksData([`${baseUrl}/feed-a`], cache, 10_000);
+
+ expect(result.sortedData.item?.[0]?.title).toBe("New Article");
+ expect(requestCounts.feedA).toBe(1);
+ });
+
+ test("refreshes fresh cache after TTL expires", async () => {
+ resetFeeds();
+ const cache = createCache();
+
+ await getCachedBooksData([`${baseUrl}/feed-a`], cache, 20);
+ feedA = createRssFeed("Feed A", [
+ {
+ category: "Updated",
+ date: "Fri, 05 Jan 2025 10:00:00 GMT",
+ path: "a-updated",
+ title: "Updated Article",
+ },
+ ]);
+ await new Promise((resolve) => setTimeout(resolve, 40));
+
+ const result = await getCachedBooksData([`${baseUrl}/feed-a`], cache, 20);
+
+ expect(result.sortedData.item?.[0]?.title).toBe("Updated Article");
+ expect(result.categories).toEqual([{ name: "Updated" }]);
+ expect(requestCounts.feedA).toBe(2);
+ });
+
+ test("falls back to last successful data when all configured feeds fail after TTL", async () => {
+ resetFeeds();
+ const cache = createCache();
+
+ await getCachedBooksData([`${baseUrl}/feed-a`], cache, 20);
+ feedA = invalidFeed;
+ await new Promise((resolve) => setTimeout(resolve, 40));
+
+ const result = await getCachedBooksData([`${baseUrl}/feed-a`], cache, 20);
+
+ expect(result.sortedData.item?.[0]?.title).toBe("New Article");
+ expect(result.categories).toEqual([{ name: "News" }, { name: "Tech" }]);
+ expect(requestCounts.feedA).toBe(2);
+ });
+
+ test("caches partial data when at least one configured feed succeeds", async () => {
+ resetFeeds();
+ const cache = createCache();
+
+ const result = await getCachedBooksData(
+ [`${baseUrl}/invalid`, `${baseUrl}/feed-b`],
+ cache,
+ 10_000,
+ );
+
+ expect(result.sortedData.item?.map((item) => item.title)).toEqual(["Book Article"]);
+ expect(result.categories).toEqual([{ name: "Books" }]);
+ expect(requestCounts.invalid).toBe(1);
+ expect(requestCounts.feedB).toBe(1);
+ });
+
+ test("returns empty data when no feeds are configured", async () => {
+ resetFeeds();
+ const cache = createCache();
+
+ const result = await getCachedBooksData([], cache, 10_000);
+
+ expect(result.sortedData.title).toBe("No title");
+ expect(result.sortedData.item).toEqual([]);
+ expect(result.categories).toEqual([]);
+ });
+});
diff --git a/pkgs/rssbook/src/books/index.ts b/pkgs/rssbook/src/books/index.ts
index 88f4832..b094d10 100644
--- a/pkgs/rssbook/src/books/index.ts
+++ b/pkgs/rssbook/src/books/index.ts
@@ -1,7 +1,14 @@
import Elysia, { t } from "elysia";
import { initPlugin } from "@/plugins";
-import { type Data, type DataItem, EMPTY_DATA, feedType, type ThemeProps } from "@/types";
-import { type Cache, filter, logger, ofetch, parse, render, sort, union } from "@/utils";
+import {
+ type Data,
+ type DataItem,
+ EMPTY_DATA,
+ feedType,
+ parseData,
+ type ThemeProps,
+} from "@/types";
+import { type Cache, filter, logger, ofetch, parse, render, sort, union, uuid } from "@/utils";
const DEFAULT_META: ThemeProps["meta"] = {
atomFeed: "/books/atom",
@@ -13,6 +20,7 @@ const DEFAULT_META: ThemeProps["meta"] = {
const DEFAULT_PAGE_SIZE = 20;
const MAX_PAGE_SIZE = 100;
+const BOOKS_LAST_SUCCESS_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
interface CachedBooksData {
/** Sorted feed data */
@@ -21,7 +29,7 @@ interface CachedBooksData {
categories: ThemeProps["categories"];
}
-async function getAllFeedData(feedUrls: string[]): Promise {
+async function getAllFeedData(feedUrls: string[]): Promise {
if (feedUrls.length === 0) {
return EMPTY_DATA;
}
@@ -51,7 +59,7 @@ async function getAllFeedData(feedUrls: string[]): Promise {
.map((result) => result.value);
if (validFeeds.length === 0) {
- return EMPTY_DATA;
+ return null;
}
if (validFeeds.length === 1) {
@@ -62,29 +70,106 @@ async function getAllFeedData(feedUrls: string[]): Promise {
return union(firstFeed, restFeeds);
}
-/** Fetch and process books data with caching */
-async function getCachedBooksData(feeds: string[], cache: Cache): Promise {
- // TODO:
- return cache.tryGet("rssbook:books", async () => {
- // Fetch raw feed data
- const rawData = await getAllFeedData(feeds);
-
- // Sort data by date
- const sortedData = sort(rawData);
-
- // Extract all categories from sorted data
- const categories: ThemeProps["categories"] = Array.from(
- new Set(
- (sortedData.item || [])
- .flatMap((item) => item.category?.map((cat) => cat.name) || [])
- .filter((name): name is string => name !== undefined),
- ),
- )
- .sort((a, b) => a.localeCompare(b))
- .map((name) => ({ name }));
+function getBooksCacheKeys(feeds: string[]) {
+ const feedKey = uuid(feeds);
+ return {
+ fresh: `rssbook:books:${feedKey}:fresh`,
+ lastSuccess: `rssbook:books:${feedKey}:last-success`,
+ };
+}
+
+function processBooksData(rawData: Data): CachedBooksData {
+ const sortedData = sort(rawData);
+ const categories: ThemeProps["categories"] = Array.from(
+ new Set(
+ (sortedData.item || [])
+ .flatMap((item) => item.category?.map((cat) => cat.name) || [])
+ .filter((name): name is string => name !== undefined),
+ ),
+ )
+ .sort((a, b) => a.localeCompare(b))
+ .map((name) => ({ name }));
+
+ return { categories, sortedData };
+}
+
+function reviveBooksDate(key: string, value: unknown) {
+ if ((key === "date" || key === "published" || key === "updated") && typeof value === "string") {
+ const date = new Date(value);
+ if (!Number.isNaN(date.getTime())) {
+ return date;
+ }
+ }
+
+ return value;
+}
+
+function isRecord(value: unknown): value is Record {
+ return typeof value === "object" && value !== null;
+}
+
+function isCategory(value: unknown): value is { name: string } {
+ return isRecord(value) && typeof value.name === "string";
+}
- return { categories, sortedData };
- });
+function encodeCachedBooksData(data: CachedBooksData): string {
+ return JSON.stringify(data);
+}
+
+function decodeCachedBooksData(data: string): CachedBooksData {
+ const parsed: unknown = JSON.parse(data, reviveBooksDate);
+ if (!isRecord(parsed)) {
+ throw new Error("Invalid cached books data");
+ }
+
+ return {
+ categories: Array.isArray(parsed.categories) ? parsed.categories.filter(isCategory) : [],
+ sortedData: parseData(parsed.sortedData),
+ };
+}
+
+async function refreshBooksData(feeds: string[], cache: Cache, lastSuccessKey: string) {
+ const rawData = await getAllFeedData(feeds);
+
+ if (rawData === null) {
+ throw new Error("Unable to fetch or parse any configured books feeds");
+ }
+
+ const processedData = processBooksData(rawData);
+ const encodedData = encodeCachedBooksData(processedData);
+ await cache.set(lastSuccessKey, encodedData, BOOKS_LAST_SUCCESS_MAX_AGE_MS);
+ return encodedData;
+}
+
+/**
+ * Fetch and process books data with a feed-list-specific fresh cache.
+ * The fresh key uses the configured TTL; the last-success key is a longer-lived
+ * fallback so an expired refresh cannot replace usable data with an empty feed.
+ */
+export async function getCachedBooksData(
+ feeds: string[],
+ cache: Cache,
+ maxAgeMs: number,
+): Promise {
+ const { fresh, lastSuccess } = getBooksCacheKeys(feeds);
+
+ try {
+ const data = await cache.tryGet(
+ fresh,
+ () => refreshBooksData(feeds, cache, lastSuccess),
+ maxAgeMs,
+ );
+ return decodeCachedBooksData(data);
+ } catch (error) {
+ const fallback = await cache.get(lastSuccess);
+ if (fallback !== null) {
+ logger.error("Failed to refresh books cache; using last successful data", error);
+ return decodeCachedBooksData(fallback);
+ }
+
+ logger.error("Failed to refresh books cache and no last successful data exists", error);
+ return processBooksData(EMPTY_DATA);
+ }
}
export const bookPlugin = new Elysia({
@@ -97,12 +182,16 @@ export const bookPlugin = new Elysia({
async ({
query: { page, limit, search, category },
rssbook: {
- books: { feeds, meta, theme },
+ books: { cacheMaxAgeMs, feeds, meta, theme },
cache,
},
}) => {
// Get cached sorted data and categories
- const { sortedData, categories: allCategories } = await getCachedBooksData(feeds, cache);
+ const { sortedData, categories: allCategories } = await getCachedBooksData(
+ feeds,
+ cache,
+ cacheMaxAgeMs,
+ );
// Apply search and category filters (not cached due to dynamic params)
const filteredData = filter(sortedData, {
@@ -193,12 +282,12 @@ export const bookPlugin = new Elysia({
async ({
params: { type },
rssbook: {
- books: { feeds },
+ books: { cacheMaxAgeMs, feeds },
cache,
},
}) => {
// Reuse cached processed data
- const { sortedData } = await getCachedBooksData(feeds, cache);
+ const { sortedData } = await getCachedBooksData(feeds, cache, cacheMaxAgeMs);
return render(sortedData, type);
},
{
diff --git a/pkgs/rssbook/src/index.ts b/pkgs/rssbook/src/index.ts
index b6cf8d6..d9d7d12 100644
--- a/pkgs/rssbook/src/index.ts
+++ b/pkgs/rssbook/src/index.ts
@@ -1,24 +1,61 @@
/**
- * Bun Entry
+ * Bun Entry - Default app instance for Bun runtime
*/
+import { createRSSBookApp } from "./RSSBookApp";
-import { Cache, logger } from "@/utils";
-import { RSSBookApp } from "./RSSBookApp";
-
-export const app = RSSBookApp({
- cache: Cache.LRU_Cache,
- config: {},
- enableFetchOnlineServer: true,
- feeds: ["https://github.blog/feed/"],
- meta: {
- description: "A simple RSS feed aggregator and reader.",
- title: "RSSBook",
+export const app = createRSSBookApp({
+ book: {
+ config: {},
+ feeds: ["https://github.blog/feed/"],
+ meta: {
+ description: "A simple RSS feed aggregator and reader.",
+ title: "RSSBook",
+ },
+ },
+ openapi: {
+ enableFetchOnlineServer: true,
},
});
-export default app;
-app.listen(Number(Bun.env.PORT ?? 3000), (server) => {
- logger.info(
- `📕 RSSBook running at ${server.hostname}:${server.port}, You can visit ${server.hostname}:${server.port}/openapi to look up all routes. ✨`,
- );
-});
+// For Bun's built-in fetch server
+export default {
+ fetch: app.fetch,
+ port: 3000,
+};
+
+/**
+ * RSSBook - RSS Feed Generator, Toolkit and Blog Platform
+ * @module rssbook
+ */
+
+export type { RSSBook, RSSBookBookConfig, RSSBookInitConfig } from "@/plugins/init";
+export { createRSSBook } from "@/plugins/init";
+// Types
+export type {
+ Author,
+ Data,
+ DataItem,
+ Enclosure,
+ FeedType,
+ Language,
+} from "@/types/data";
+export type { Meta } from "@/types/meta";
+export type { RouteConfig } from "@/types/route";
+export type { Config, SourceConfigs } from "@/types/source";
+export type { Theme, ThemeProps } from "@/types/theme";
+// Classes
+export { Cache } from "@/utils/cache";
+export { Category } from "@/utils/category";
+export { detectLanguage } from "@/utils/detectLanguage";
+// Feed utilities
+export { filter, intersection, parse, render, sort, union } from "@/utils/feeds";
+export { formatHTML } from "@/utils/formatHTML";
+export { load } from "@/utils/load";
+export { logger } from "@/utils/logger";
+// Other utilities
+export { ofetch } from "@/utils/ofetch";
+export { Source } from "@/utils/source";
+export { toAbsoluteURL } from "@/utils/toAbsoluteURL";
+export type { RSSBookAppConfig } from "./RSSBookApp";
+// Core App
+export { createRSSBookApp } from "./RSSBookApp";
diff --git a/pkgs/rssbook/src/plugins/init.ts b/pkgs/rssbook/src/plugins/init.ts
index 9e32bcc..d04b7e9 100644
--- a/pkgs/rssbook/src/plugins/init.ts
+++ b/pkgs/rssbook/src/plugins/init.ts
@@ -3,34 +3,50 @@ import { defaultTheme } from "@/books/theme";
import type { Meta, Theme } from "@/types";
import { Cache } from "@/utils";
-export interface RSSBookInitConfig {
+/**
+ * Book initialization configuration.
+ * Used to configure feeds, route config values, and theming.
+ */
+export interface RSSBookBookConfig {
+ /** List of RSS feed URLs to aggregate */
feeds?: string[];
+ /** TTL in milliseconds for aggregated books feed data */
+ cacheMaxAgeMs?: number;
+ /** HTML meta tags for the rendered pages */
meta?: Meta;
- cache?: Cache;
+ /** Custom configuration key-value pairs */
config?: Record;
+ /** Theme for rendering feed pages */
theme?: Theme;
}
-export class RSSBook {
- cache: Cache;
-
+export interface RSSBook {
books: {
+ cacheMaxAgeMs: number;
feeds: string[];
meta: Meta;
theme: Theme;
};
-
+ cache: Cache;
config: Record;
+}
- constructor(init?: RSSBookInitConfig) {
- this.books = {
- feeds: init?.feeds || [],
- meta: init?.meta || {},
- theme: init?.theme || defaultTheme,
- };
- this.config = init?.config || {};
- this.cache = init?.cache || new Cache();
- }
+export interface RSSBookInitConfig {
+ book?: RSSBookBookConfig;
+ cache?: Cache;
+}
+
+export function createRSSBook(init?: RSSBookInitConfig): RSSBook {
+ return {
+ books: {
+ cacheMaxAgeMs: init?.book?.cacheMaxAgeMs || 10 * 60 * 1000,
+ feeds: init?.book?.feeds || [],
+ meta: init?.book?.meta || {},
+ theme: init?.book?.theme || defaultTheme,
+ },
+ cache: init?.cache || new Cache(),
+ config: init?.book?.config || {},
+ };
}
/**
@@ -42,5 +58,8 @@ export const initPlugin = (config?: RSSBookInitConfig) =>
new Elysia({ name: "RSSBook/Init" }).decorate(
{ as: "append" }, // Inject ONLY if not exixts
"rssbook",
- new RSSBook(config),
+ createRSSBook({
+ book: config?.book,
+ cache: config?.cache,
+ }),
);
diff --git a/pkgs/rssbook/src/plugins/openAPI.ts b/pkgs/rssbook/src/plugins/openAPI.ts
index 1da9ed7..53d6229 100644
--- a/pkgs/rssbook/src/plugins/openAPI.ts
+++ b/pkgs/rssbook/src/plugins/openAPI.ts
@@ -5,15 +5,23 @@ import { logger, ofetch } from "@/utils";
import pkg from "../../package.json" with { type: "json" };
const { version } = pkg;
+
export const openAPIPlugin = (enableFetchOnlineServer: boolean = true) => {
- // fetch public instance hosts list
- let onlineList: {
- url: string;
- description: string;
- online: boolean;
- }[] = [];
+ const servers = [
+ {
+ description: "Local Server (Default - You can switch to available online server lists.)",
+ url: "/",
+ },
+ ];
+ // fetch public instance hosts list
const updateHosts = async () => {
+ let onlineList: {
+ url: string;
+ description: string;
+ online: boolean;
+ }[] = [];
+
try {
const text = await ofetch(
"https://raw.githubusercontent.com/HackHTU/RSSBook/refs/heads/main/HOSTS",
@@ -34,6 +42,11 @@ export const openAPIPlugin = (enableFetchOnlineServer: boolean = true) => {
};
})
.filter((host) => host.url && host.online);
+ servers.splice(
+ 1,
+ servers.length,
+ ...onlineList.map(({ description, url }) => ({ description, url })),
+ );
logger.info(`Loaded ${onlineList.length} online hosts.`);
} catch {}
};
@@ -90,16 +103,7 @@ $ cd RSSBook && bun dev
title: "RSSBook OpenAPI Documentation",
version: version,
},
- servers: [
- {
- description:
- "Local Server (Default - You can switch to available online server lists.)",
- url: "/",
- },
-
- // Add online server lists if enabled
- ...onlineList,
- ],
+ servers,
tags: [
{
description:
diff --git a/pkgs/rssbook/src/routers/feeds/bbs/index.ts b/pkgs/rssbook/src/routers/feeds/bbs/index.ts
index 828bd76..610708c 100644
--- a/pkgs/rssbook/src/routers/feeds/bbs/index.ts
+++ b/pkgs/rssbook/src/routers/feeds/bbs/index.ts
@@ -1,6 +1,9 @@
import { Category } from "@/utils";
+import producthunt from "./producthunt";
export default new Category(
"bbs",
"Online **forums** for discussions, Q&A, and community interactions on various topics.",
-).use({});
+).use({
+ producthunt,
+});
diff --git a/pkgs/rssbook/src/routers/feeds/bbs/producthunt/index.test.ts b/pkgs/rssbook/src/routers/feeds/bbs/producthunt/index.test.ts
new file mode 100644
index 0000000..4b72f62
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/bbs/producthunt/index.test.ts
@@ -0,0 +1,11 @@
+import { describe, expect, test } from "bun:test";
+import { getRouteData } from "@/utils/tests/source";
+import producthunt from ".";
+
+describe("Product Hunt", () => {
+ test("fetches today feed", async () => {
+ const data = await getRouteData(producthunt, "/today");
+
+ expect(data.item?.length).toBeGreaterThan(0);
+ }, 20_000);
+});
diff --git a/pkgs/rssbook/src/routers/feeds/bbs/producthunt/index.ts b/pkgs/rssbook/src/routers/feeds/bbs/producthunt/index.ts
new file mode 100644
index 0000000..186acd5
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/bbs/producthunt/index.ts
@@ -0,0 +1,30 @@
+import type { Data } from "@/types";
+import { Source } from "@/utils";
+import { parse } from "@/utils/feeds";
+
+export default new Source({
+ description: "Daily product launch feeds from Product Hunt.",
+ domain: "producthunt.com",
+ slug: "producthunt",
+ title: "Product Hunt",
+}).feed(
+ {
+ description: "Fetch Product Hunt launches from the public RSS feed.",
+ language: "en",
+ maintainer: { name: "RSSBook" },
+ title: "Today",
+ },
+ (app) =>
+ app.get("/today", async ({ cache, ofetch }) => {
+ const feedUrl = "https://www.producthunt.com/feed";
+ return await cache.tryGet(feedUrl, async () => {
+ const xml = await ofetch(feedUrl, { responseType: "text" });
+ return {
+ ...parse(xml),
+ language: "en",
+ link: "https://www.producthunt.com",
+ title: "Product Hunt Today",
+ } satisfies Data;
+ });
+ }),
+);
diff --git a/pkgs/rssbook/src/routers/feeds/blog/index.ts b/pkgs/rssbook/src/routers/feeds/blog/index.ts
index ecf6023..b77a514 100644
--- a/pkgs/rssbook/src/routers/feeds/blog/index.ts
+++ b/pkgs/rssbook/src/routers/feeds/blog/index.ts
@@ -1,6 +1,11 @@
import { Category } from "@/utils";
+import medium from "./medium";
+import substack from "./substack";
export default new Category(
"blog",
"Personal or professional **blogs** covering various topics, stories, and experiences.",
-).use({});
+).use({
+ medium,
+ substack,
+});
diff --git a/pkgs/rssbook/src/routers/feeds/blog/medium/index.test.ts b/pkgs/rssbook/src/routers/feeds/blog/medium/index.test.ts
new file mode 100644
index 0000000..d1abeba
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/blog/medium/index.test.ts
@@ -0,0 +1,18 @@
+import { describe, expect, test } from "bun:test";
+import type { Data } from "@/types";
+import { getRouteData } from "@/utils/tests/source";
+import source from ".";
+
+function expectData(data: Data) {
+ expect(data.title.length).toBeGreaterThan(0);
+ expect(data.link).toStartWith("https://");
+ expect(data.item?.length).toBeGreaterThan(0);
+}
+
+describe("Medium", () => {
+ test("fetches user feed", async () => {
+ const data = await getRouteData(source, "/feed/zhgchgli");
+
+ expectData(data);
+ }, 20_000);
+});
diff --git a/pkgs/rssbook/src/routers/feeds/blog/medium/index.ts b/pkgs/rssbook/src/routers/feeds/blog/medium/index.ts
new file mode 100644
index 0000000..9668a8f
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/blog/medium/index.ts
@@ -0,0 +1,34 @@
+import type { Data } from "@/types";
+import { Source, t } from "@/utils";
+import { parse } from "@/utils/feeds";
+
+export default new Source({
+ description: "Medium publications and user feeds.",
+ domain: "medium.com",
+ slug: "medium",
+ title: "Medium",
+}).feed(
+ {
+ description: "Fetch a user's Medium feed from the official RSS endpoint.",
+ language: "en",
+ maintainer: { name: "RSSBook" },
+ title: "User Feed",
+ },
+ (app) =>
+ app.get(
+ "/feed/:user",
+ async ({ cache, ofetch, params: { user } }) => {
+ const feedUrl = `https://medium.com/feed/@${user}`;
+
+ return cache.tryGet(feedUrl, async () => {
+ const xml = await ofetch(feedUrl, { responseType: "text" });
+ return parse(xml);
+ });
+ },
+ {
+ params: t.Object({
+ user: t.String({ description: "Medium username." }),
+ }),
+ },
+ ),
+);
diff --git a/pkgs/rssbook/src/routers/feeds/blog/substack/index.test.ts b/pkgs/rssbook/src/routers/feeds/blog/substack/index.test.ts
new file mode 100644
index 0000000..45afba9
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/blog/substack/index.test.ts
@@ -0,0 +1,18 @@
+import { describe, expect, test } from "bun:test";
+import type { Data } from "@/types";
+import { getRouteData } from "@/utils/tests/source";
+import source from ".";
+
+function expectData(data: Data) {
+ expect(data.title.length).toBeGreaterThan(0);
+ expect(data.link).toStartWith("https://");
+ expect(data.item?.length).toBeGreaterThan(0);
+}
+
+describe("Substack", () => {
+ test("fetches newsletter feed", async () => {
+ const data = await getRouteData(source, "/subscribe/mangoread");
+
+ expectData(data);
+ }, 20_000);
+});
diff --git a/pkgs/rssbook/src/routers/feeds/blog/substack/index.ts b/pkgs/rssbook/src/routers/feeds/blog/substack/index.ts
new file mode 100644
index 0000000..f323371
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/blog/substack/index.ts
@@ -0,0 +1,34 @@
+import type { Data } from "@/types";
+import { Source, t } from "@/utils";
+import { parse } from "@/utils/feeds";
+
+export default new Source({
+ description: "Substack newsletters and publications.",
+ domain: "substack.com",
+ slug: "substack",
+ title: "Substack",
+}).feed(
+ {
+ description: "Fetch a Substack publication's RSS feed.",
+ language: "en",
+ maintainer: { name: "RSSBook" },
+ title: "Newsletter",
+ },
+ (app) =>
+ app.get(
+ "/subscribe/:user",
+ async ({ cache, ofetch, params: { user } }) => {
+ const feedUrl = `https://${user}.substack.com/feed`;
+
+ return cache.tryGet(feedUrl, async () => {
+ const xml = await ofetch(feedUrl, { responseType: "text" });
+ return parse(xml);
+ });
+ },
+ {
+ params: t.Object({
+ user: t.String({ description: "Substack publication username." }),
+ }),
+ },
+ ),
+);
diff --git a/pkgs/rssbook/src/routers/feeds/finance/cls/index.test.ts b/pkgs/rssbook/src/routers/feeds/finance/cls/index.test.ts
new file mode 100644
index 0000000..a88a7a7
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/finance/cls/index.test.ts
@@ -0,0 +1,11 @@
+import { describe, expect, test } from "bun:test";
+import { getRouteData } from "@/utils/tests/source";
+import cls from ".";
+
+describe("CLS", () => {
+ test("fetches telegraph updates", async () => {
+ const data = await getRouteData(cls, "/telegraph");
+
+ expect(data.title).toContain("CLS");
+ }, 20_000);
+});
diff --git a/pkgs/rssbook/src/routers/feeds/finance/cls/index.ts b/pkgs/rssbook/src/routers/feeds/finance/cls/index.ts
new file mode 100644
index 0000000..2c3a87a
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/finance/cls/index.ts
@@ -0,0 +1,54 @@
+import type { Data, DataItem } from "@/types";
+import { Source } from "@/utils";
+
+type CLSRollItem = {
+ content?: string;
+ ctime?: number;
+ images?: string[];
+ shareurl?: string;
+ title?: string;
+};
+
+export default new Source({
+ description: "Financial news feeds from CLS (财联社).",
+ domain: "cls.cn",
+ slug: "cls",
+ title: "CLS",
+}).feed(
+ {
+ description: "Fetch CLS telegraph updates from the public cache API.",
+ fulltext: true,
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Telegraph",
+ withImage: "If-Present",
+ },
+ (app) =>
+ app.get("/telegraph", async ({ cache, date, ofetch }) => {
+ const apiUrl =
+ "https://www.cls.cn/api/cache?appName=CailianpressWeb&os=web&sv=8.7.9&name=telegraph";
+ const item = await cache.tryGet(apiUrl, async () => {
+ const response = await ofetch<{ data?: { roll_data?: CLSRollItem[] } }>(apiUrl, {
+ responseType: "json",
+ });
+ return (response.data?.roll_data ?? []).map(
+ (entry) =>
+ ({
+ date: date((entry.ctime ?? Date.now() / 1000) * 1000),
+ description: entry.content,
+ image: entry.images?.[0],
+ link: entry.shareurl ?? "https://www.cls.cn/telegraph",
+ title: entry.title || entry.content || "CLS Telegraph",
+ }) satisfies DataItem,
+ );
+ });
+
+ return {
+ description: "CLS telegraph updates.",
+ item,
+ language: "zh-CN",
+ link: "https://www.cls.cn/telegraph",
+ title: "CLS Telegraph",
+ } satisfies Data;
+ }),
+);
diff --git a/pkgs/rssbook/src/routers/feeds/finance/gelonghui/index.test.ts b/pkgs/rssbook/src/routers/feeds/finance/gelonghui/index.test.ts
new file mode 100644
index 0000000..72092a2
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/finance/gelonghui/index.test.ts
@@ -0,0 +1,11 @@
+import { describe, expect, test } from "bun:test";
+import { getRouteData } from "@/utils/tests/source";
+import gelonghui from ".";
+
+describe("Gelonghui", () => {
+ test("fetches live updates", async () => {
+ const data = await getRouteData(gelonghui, "/live");
+
+ expect(data.title).toContain("Gelonghui");
+ }, 20_000);
+});
diff --git a/pkgs/rssbook/src/routers/feeds/finance/gelonghui/index.ts b/pkgs/rssbook/src/routers/feeds/finance/gelonghui/index.ts
new file mode 100644
index 0000000..13ba221
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/finance/gelonghui/index.ts
@@ -0,0 +1,62 @@
+import type { Data, DataItem } from "@/types";
+import { Source } from "@/utils";
+
+type GelonghuiLive = {
+ content?: string;
+ createTimestamp?: number;
+ pictures?: string[];
+ route?: string;
+ source?: string | Array | { name?: string };
+ title?: string;
+};
+
+export default new Source({
+ description: "Market news feeds from Gelonghui (格隆汇).",
+ domain: "gelonghui.com",
+ slug: "gelonghui",
+ title: "Gelonghui",
+}).feed(
+ {
+ description: "Fetch real-time market updates from Gelonghui.",
+ fulltext: true,
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Live Updates",
+ withImage: "If-Present",
+ },
+ (app) =>
+ app.get("/live", async ({ cache, date, ofetch }) => {
+ const apiUrl = "https://www.gelonghui.com/api/live-channels/all/lives/v4";
+ const item = await cache.tryGet(apiUrl, async () => {
+ const response = await ofetch<{ result?: GelonghuiLive[] }>(apiUrl, {
+ responseType: "json",
+ });
+ return (response.result ?? []).map((entry) => {
+ const rawSources = Array.isArray(entry.source)
+ ? entry.source
+ : entry.source
+ ? [entry.source]
+ : undefined;
+ const sources = rawSources
+ ?.map((source) => (typeof source === "string" ? source : source.name))
+ .filter((source): source is string => !!source);
+ return {
+ category: sources?.map((name) => ({ name })),
+ date: date((entry.createTimestamp ?? Date.now() / 1000) * 1000),
+ description: entry.content,
+ image: entry.pictures?.[0],
+ link: entry.route ?? "https://www.gelonghui.com/live",
+ title: entry.title || entry.content || "Gelonghui Live Update",
+ } satisfies DataItem;
+ });
+ });
+
+ return {
+ description: "Real-time market updates from Gelonghui.",
+ item,
+ language: "zh-CN",
+ link: "https://www.gelonghui.com/live",
+ title: "Gelonghui Live Updates",
+ } satisfies Data;
+ }),
+);
diff --git a/pkgs/rssbook/src/routers/feeds/finance/index.ts b/pkgs/rssbook/src/routers/feeds/finance/index.ts
index c57c65e..e8cdead 100644
--- a/pkgs/rssbook/src/routers/feeds/finance/index.ts
+++ b/pkgs/rssbook/src/routers/feeds/finance/index.ts
@@ -1,6 +1,13 @@
import { Category } from "@/utils";
+import cls from "./cls";
+import gelonghui from "./gelonghui";
+import wallstreetcn from "./wallstreetcn";
export default new Category(
"finance",
"Information on **financial markets, investments, and personal finance**, including news and analysis.",
-).use({});
+).use({
+ cls,
+ gelonghui,
+ wallstreetcn,
+});
diff --git a/pkgs/rssbook/src/routers/feeds/finance/wallstreetcn/index.test.ts b/pkgs/rssbook/src/routers/feeds/finance/wallstreetcn/index.test.ts
new file mode 100644
index 0000000..3d551af
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/finance/wallstreetcn/index.test.ts
@@ -0,0 +1,11 @@
+import { describe, expect, test } from "bun:test";
+import { getRouteData } from "@/utils/tests/source";
+import wallstreetcn from ".";
+
+describe("Wallstreetcn", () => {
+ test("fetches live updates", async () => {
+ const data = await getRouteData(wallstreetcn, "/live/global");
+
+ expect(data.item?.length).toBeGreaterThan(0);
+ }, 20_000);
+});
diff --git a/pkgs/rssbook/src/routers/feeds/finance/wallstreetcn/index.ts b/pkgs/rssbook/src/routers/feeds/finance/wallstreetcn/index.ts
new file mode 100644
index 0000000..9cecf66
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/finance/wallstreetcn/index.ts
@@ -0,0 +1,67 @@
+import type { Data, DataItem } from "@/types";
+import { Source, t } from "@/utils";
+
+type WSCNLiveItem = {
+ author?: { display_name?: string };
+ content?: string;
+ content_text?: string;
+ display_time?: number;
+ images?: Array<{ uri?: string }>;
+ title?: string;
+ uri?: string;
+};
+
+export default new Source({
+ description: "Market news feeds from Wallstreetcn (华尔街见闻).",
+ domain: "wallstreetcn.com",
+ slug: "wallstreetcn",
+ title: "Wallstreetcn",
+}).feed(
+ {
+ description: "Fetch live market updates from a Wallstreetcn channel.",
+ fulltext: true,
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Live Updates",
+ withImage: "If-Present",
+ },
+ (app) =>
+ app.get(
+ "/live/:category",
+ async ({ cache, date, ofetch, params: { category } }) => {
+ const apiUrl = `https://api-one.wallstcn.com/apiv1/content/lives?channel=${category}-channel&limit=50`;
+ const item = await cache.tryGet(apiUrl, async () => {
+ const response = await ofetch<{ data?: { items?: WSCNLiveItem[] } }>(apiUrl, {
+ responseType: "json",
+ });
+ return (response.data?.items ?? []).map(
+ (entry) =>
+ ({
+ author: [{ name: entry.author?.display_name }],
+ date: date((entry.display_time ?? Date.now() / 1000) * 1000),
+ description: entry.content ?? entry.content_text,
+ image: entry.images?.[0]?.uri,
+ link: entry.uri ?? `https://wallstreetcn.com/live/${category}`,
+ title: entry.title || entry.content_text || "Wallstreetcn Live Update",
+ }) satisfies DataItem,
+ );
+ });
+
+ return {
+ description: `Wallstreetcn live updates for ${category}.`,
+ item,
+ language: "zh-CN",
+ link: `https://wallstreetcn.com/live/${category}`,
+ title: `Wallstreetcn Live - ${category}`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ category: t.String({
+ default: "global",
+ description: "Live channel, for example global, shares, or forex.",
+ }),
+ }),
+ },
+ ),
+);
diff --git a/pkgs/rssbook/src/routers/feeds/multimedia/cyzone/index.test.ts b/pkgs/rssbook/src/routers/feeds/multimedia/cyzone/index.test.ts
new file mode 100644
index 0000000..15e7e91
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/multimedia/cyzone/index.test.ts
@@ -0,0 +1,30 @@
+import { describe, expect, test } from "bun:test";
+import type { Data } from "@/types";
+import { getRouteData } from "@/utils/tests/source";
+import source from ".";
+
+function expectData(data: Data) {
+ expect(data.title.length).toBeGreaterThan(0);
+ expect(data.link).toStartWith("https://");
+ expect(data.item?.length).toBeGreaterThan(0);
+}
+
+describe("Cyzone", () => {
+ test("fetches channel news", async () => {
+ const data = await getRouteData(source, "/channel/news");
+
+ expectData(data);
+ }, 20_000);
+
+ test("fetches author articles", async () => {
+ const data = await getRouteData(source, "/author/1225562");
+
+ expectData(data);
+ }, 20_000);
+
+ test("fetches label articles", async () => {
+ const data = await getRouteData(source, "/label/创业邦周报");
+
+ expectData(data);
+ }, 20_000);
+});
diff --git a/pkgs/rssbook/src/routers/feeds/multimedia/cyzone/index.ts b/pkgs/rssbook/src/routers/feeds/multimedia/cyzone/index.ts
new file mode 100644
index 0000000..1b6e23e
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/multimedia/cyzone/index.ts
@@ -0,0 +1,256 @@
+import type { Data, DataItem } from "@/types";
+import type { Cache } from "@/utils";
+import { Source, t } from "@/utils";
+
+type CyzoneItem = {
+ author?: string;
+ author_name?: string;
+ category?: string;
+ category_name?: string;
+ content?: string;
+ content_id?: string;
+ description?: string;
+ published_at?: number;
+ tags?: string;
+ title?: string;
+ url?: string;
+ votes?: number;
+};
+
+type CyzoneItemWrapper = {
+ item?: CyzoneItem;
+};
+
+const ROOT_URL = "https://www.cyzone.cn";
+const API_URL = "https://api1.cyzone.cn";
+
+async function getInfo(
+ url: string,
+ ofetch: (url: string, options?: object) => Promise,
+ load: typeof import("cheerio").load,
+): Promise> {
+ try {
+ const response = (await ofetch(url, { responseType: "text" })) as string;
+ const $ = load(response);
+ const avatar = $("img.avatar").attr("src")?.split("?")[0];
+ const icon = $("link[rel='icon']").attr("href");
+ const image = $("div.logo img").attr("src");
+
+ return {
+ author: { name: $("meta[name='app-mobile-web-app-title']").attr("content") ?? "" },
+ description: $("meta[name='description']").attr("content"),
+ favicon: icon ? new URL(icon, ROOT_URL).href : undefined,
+ image: avatar || (image ? new URL(image, ROOT_URL).href : undefined),
+ language: "zh-CN",
+ link: url,
+ title: $("title").text(),
+ };
+ } catch {
+ return {
+ language: "zh-CN",
+ link: url,
+ title: "Cyzone",
+ };
+ }
+}
+
+async function processItems(
+ apiUrl: string,
+ params: Record,
+ ofetch: (url: string, options?: object) => Promise,
+ load: (html: string) => ReturnType,
+ cache: Cache,
+): Promise {
+ const response = (await ofetch(apiUrl, {
+ query: { size: "5", ...params },
+ responseType: "json",
+ })) as {
+ data?:
+ | (CyzoneItem | CyzoneItemWrapper)[]
+ | {
+ article?: (CyzoneItem | CyzoneItemWrapper)[];
+ data?: (CyzoneItem | CyzoneItemWrapper)[];
+ };
+ };
+
+ const data = response.data ?? [];
+ const rawItems = Array.isArray(data) ? data : (data.article ?? data.data ?? []);
+
+ return await Promise.all(
+ rawItems.slice(0, 5).map(async (raw) => {
+ const item = "item" in raw && raw.item ? raw.item : (raw as CyzoneItem);
+ const contentId = item.content_id;
+ const baseItem: DataItem = {
+ category: [item.category_name, ...(item.tags?.split(",") ?? [])]
+ .filter(Boolean)
+ .map((name) => ({ name })),
+ date: new Date((item.published_at ?? Date.now() / 1000) * 1000),
+ description: item.description,
+ link: item.url?.startsWith("//")
+ ? `https:${item.url}`
+ : (item.url ?? `${ROOT_URL}/article/${contentId}.html`),
+ title: item.title ?? "Untitled",
+ };
+
+ if (!contentId) return baseItem;
+
+ return cache.tryGet(`cyzone:${contentId}`, async () => {
+ const detailResponse = (await ofetch(`${API_URL}/v2/content/app_content/show`, {
+ body: { content_id: contentId },
+ method: "POST",
+ responseType: "json",
+ })) as { data?: CyzoneItem };
+ const data = detailResponse.data ?? {};
+
+ const categories = [
+ data.category,
+ ...(baseItem.category?.map((c) => c.name) ?? []),
+ ...(data.tags?.split(",") ?? []),
+ ].filter((name): name is string => !!name);
+
+ const $content = load(data.content ?? "");
+ $content("img").each((_, el) => {
+ const $el = $content(el);
+ const src = $el.attr("src");
+ if (src) {
+ $el.attr("src", src.split("?")[0]);
+ } else {
+ $el.remove();
+ }
+ });
+
+ const contentHtml = $content("body").html() ?? $content.html() ?? data.description;
+
+ return {
+ author: [
+ { name: data.author_name ?? data.author ?? item.author_name ?? item.author ?? "" },
+ ],
+ category: [...new Set(categories)].map((name) => ({ name })),
+ date: new Date((data.published_at ?? item.published_at ?? Date.now() / 1000) * 1000),
+ description: contentHtml,
+ id: `cyzone-${contentId}`,
+ link: `${ROOT_URL}/article/${contentId}.html`,
+ title: data.title ?? item.title ?? "Untitled",
+ } satisfies DataItem;
+ });
+ }),
+ );
+}
+
+export default new Source({
+ description: "News feeds from Cyzone (创业邦).",
+ domain: "cyzone.cn",
+ slug: "cyzone",
+ title: "Cyzone",
+})
+ .feed(
+ {
+ description: "Fetch articles from a Cyzone channel.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Channel",
+ },
+ (app) =>
+ app.get(
+ "/channel/:id",
+ async ({ cache, load, ofetch, params: { id = "news" } }) => {
+ const apiPath =
+ id === "news" ? "v2/content/channel/getArticle" : "v2/content/channel/detail";
+ const apiUrl = `${API_URL}/${apiPath}`;
+ const currentUrl = `${ROOT_URL}/channel/${id}`;
+ const params: Record = id === "news" ? {} : { channel_id: id };
+
+ const item = await cache.tryGet(apiUrl + id, async () =>
+ processItems(apiUrl, params, ofetch, load, cache),
+ );
+
+ const info = await getInfo(currentUrl, ofetch, load);
+
+ return {
+ ...info,
+ item,
+ language: "zh-CN",
+ link: currentUrl,
+ title: info.title ?? `Cyzone - ${id}`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ id: t.String({
+ default: "news",
+ description: "Channel id, for example news, 5, 14, 13, or 8.",
+ }),
+ }),
+ },
+ ),
+ )
+ .feed(
+ {
+ description: "Fetch articles from a Cyzone author.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Author",
+ },
+ (app) =>
+ app.get(
+ "/author/:id",
+ async ({ cache, load, ofetch, params: { id } }) => {
+ const apiUrl = `${API_URL}/v2/author/author/detail`;
+ const currentUrl = `${ROOT_URL}/author/${id}`;
+
+ const item = await cache.tryGet(apiUrl + id, async () =>
+ processItems(apiUrl, { author_id: id }, ofetch, load, cache),
+ );
+
+ const info = await getInfo(currentUrl, ofetch, load);
+
+ return {
+ ...info,
+ item,
+ language: "zh-CN",
+ link: currentUrl,
+ title: info.title ?? `Cyzone - Author ${id}`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ id: t.String({ description: "Author id." }),
+ }),
+ },
+ ),
+ )
+ .feed(
+ {
+ description: "Fetch articles from a Cyzone label.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Label",
+ },
+ (app) =>
+ app.get(
+ "/label/:label",
+ async ({ cache, load, ofetch, params: { label } }) => {
+ const apiUrl = `${API_URL}/v2/content/tag/tagList`;
+ const currentUrl = `${ROOT_URL}/label/${encodeURIComponent(label)}`;
+
+ const item = await cache.tryGet(apiUrl + label, async () =>
+ processItems(apiUrl, { tag: label }, ofetch, load, cache),
+ );
+
+ const info = await getInfo(currentUrl, ofetch, load);
+
+ return {
+ ...info,
+ item,
+ language: "zh-CN",
+ link: currentUrl,
+ title: info.title ?? `Cyzone - Label ${label}`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ label: t.String({ description: "Label name." }),
+ }),
+ },
+ ),
+ );
diff --git a/pkgs/rssbook/src/routers/feeds/multimedia/geekpark/index.test.ts b/pkgs/rssbook/src/routers/feeds/multimedia/geekpark/index.test.ts
new file mode 100644
index 0000000..a1c71f8
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/multimedia/geekpark/index.test.ts
@@ -0,0 +1,24 @@
+import { describe, expect, test } from "bun:test";
+import type { Data } from "@/types";
+import { getRouteData } from "@/utils/tests/source";
+import source from ".";
+
+function expectData(data: Data) {
+ expect(data.title.length).toBeGreaterThan(0);
+ expect(data.link).toStartWith("https://");
+ expect(data.item?.length).toBeGreaterThan(0);
+}
+
+describe("Geekpark", () => {
+ test("fetches home articles", async () => {
+ const data = await getRouteData(source, "/index");
+
+ expectData(data);
+ }, 20_000);
+
+ test("fetches column articles", async () => {
+ const data = await getRouteData(source, "/column/179");
+
+ expectData(data);
+ }, 20_000);
+});
diff --git a/pkgs/rssbook/src/routers/feeds/multimedia/geekpark/index.ts b/pkgs/rssbook/src/routers/feeds/multimedia/geekpark/index.ts
new file mode 100644
index 0000000..a1ad3f3
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/multimedia/geekpark/index.ts
@@ -0,0 +1,202 @@
+import type { Data, DataItem } from "@/types";
+import { Source, t } from "@/utils";
+
+type GeekparkAuthor = {
+ nickname?: string;
+ realname?: string;
+};
+
+type GeekparkColumn = {
+ banner_url?: string;
+ description?: string;
+ title?: string;
+};
+
+type GeekparkPost = {
+ abstract?: string;
+ authors?: GeekparkAuthor[];
+ column?: GeekparkColumn;
+ content?: string;
+ cover_url?: string;
+ id: number;
+ published_timestamp?: number;
+ tags?: string[];
+ title: string;
+ updated_at?: string;
+};
+
+type GeekparkPostWrapper = {
+ post?: GeekparkPost;
+};
+
+const ROOT_URL = "https://geekpark.net";
+const API_URL = "https://mainssl.geekpark.net";
+
+function buildDescription(intro?: string, content?: string, image?: string): string {
+ let description = "";
+
+ if (image) {
+ description += ` `;
+ }
+
+ if (intro) {
+ description += `${intro}
`;
+ }
+
+ if (content) {
+ description += content;
+ }
+
+ return description;
+}
+
+function unwrapPost(item: GeekparkPostWrapper | GeekparkPost): GeekparkPost {
+ return "post" in item && item.post ? item.post : (item as GeekparkPost);
+}
+
+export default new Source({
+ description: "Article feeds from Geekpark (极客公园).",
+ domain: "geekpark.net",
+ slug: "geekpark",
+ title: "Geekpark",
+})
+ .feed(
+ {
+ description: "Fetch latest articles from the Geekpark home page.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Home",
+ },
+ (app) =>
+ app.get("/index", async ({ cache, date, load, ofetch }) => {
+ const apiUrl = `${API_URL}/api/v2`;
+ const [apiResponse, pageResponse] = await Promise.allSettled([
+ ofetch<{ homepage_posts?: GeekparkPostWrapper[] }>(apiUrl, { responseType: "json" }),
+ ofetch(ROOT_URL, { responseType: "text" }) as Promise,
+ ]);
+
+ const homepagePosts =
+ apiResponse.status === "fulfilled" ? (apiResponse.value.homepage_posts ?? []) : [];
+ const pageHtml = pageResponse.status === "fulfilled" ? pageResponse.value : "";
+ const $ = load(pageHtml);
+ const item = await cache.tryGet(apiUrl, async () => {
+ return await Promise.all(
+ homepagePosts.slice(0, 20).map(async (wrapper) => {
+ const post = unwrapPost(wrapper);
+ const detail = await cache.tryGet(
+ `geekpark:post:${post.id}`,
+ async () => {
+ const detailResponse = await ofetch<{ post?: GeekparkPost }>(
+ `${API_URL}/api/v1/posts/${post.id}`,
+ {
+ responseType: "json",
+ },
+ );
+ return (detailResponse.post ?? post) as GeekparkPost;
+ },
+ );
+
+ const guid = `geekpark-${detail.id}`;
+ return {
+ author: [
+ { name: detail.authors?.map((a) => a.realname ?? a.nickname).join("/") ?? "" },
+ ],
+ category: [...new Set([...(detail.tags ?? []), detail.column?.title])]
+ .filter((name): name is string => !!name)
+ .map((name) => ({ name })),
+ date: date((detail.published_timestamp ?? Date.now() / 1000) * 1000),
+ description: buildDescription(detail.abstract, detail.content, detail.cover_url),
+ id: guid,
+ image: detail.cover_url,
+ link: `${ROOT_URL}/news/${detail.id}`,
+ title: detail.title,
+ } satisfies DataItem;
+ }),
+ );
+ });
+
+ return {
+ author: { name: $("meta[property='og:site_name']").attr("content") ?? "Geekpark" },
+ description:
+ $("meta[property='og:description']").attr("content") ?? "Latest Geekpark articles.",
+ image: $("meta[name='og:image']").attr("content")
+ ? `https:${$("meta[name='og:image']").attr("content")}`
+ : undefined,
+ item,
+ language: "zh-CN",
+ link: ROOT_URL,
+ title: $("title").text() || "Geekpark",
+ } satisfies Data;
+ }),
+ )
+ .feed(
+ {
+ description: "Fetch articles from a Geekpark column.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Column",
+ },
+ (app) =>
+ app.get(
+ "/column/:column",
+ async ({ cache, date, ofetch, params: { column } }) => {
+ const apiUrl = `${API_URL}/api/v1/columns/${column}`;
+ const response = await ofetch<{
+ column?: GeekparkColumn & { posts?: GeekparkPostWrapper[] };
+ }>(apiUrl, {
+ responseType: "json",
+ });
+
+ const columnData = response.column ?? {};
+ const item = await cache.tryGet(`${apiUrl}items`, async () => {
+ return await Promise.all(
+ (columnData.posts ?? []).slice(0, 20).map(async (wrapper) => {
+ const post = unwrapPost(wrapper);
+ const detail = await cache.tryGet(
+ `geekpark:post:${post.id}`,
+ async () => {
+ const detailResponse = await ofetch<{ post?: GeekparkPost }>(
+ `${API_URL}/api/v1/posts/${post.id}`,
+ {
+ responseType: "json",
+ },
+ );
+ return (detailResponse.post ?? post) as GeekparkPost;
+ },
+ );
+
+ const guid = `geekpark-${detail.id}`;
+ return {
+ author: [
+ { name: detail.authors?.map((a) => a.realname ?? a.nickname).join("/") ?? "" },
+ ],
+ category: [...new Set([...(detail.tags ?? []), detail.column?.title])]
+ .filter((name): name is string => !!name)
+ .map((name) => ({ name })),
+ date: date((detail.published_timestamp ?? Date.now() / 1000) * 1000),
+ description: buildDescription(detail.abstract, detail.content, detail.cover_url),
+ id: guid,
+ image: detail.cover_url,
+ link: `${ROOT_URL}/news/${detail.id}`,
+ title: detail.title,
+ } satisfies DataItem;
+ }),
+ );
+ });
+
+ return {
+ description: columnData.description,
+ image: columnData.banner_url,
+ item,
+ language: "zh-CN",
+ link: `${ROOT_URL}/column/${column}`,
+ title: `${columnData.title ?? column} | Geekpark`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ column: t.String({ description: "Column id, for example 179, 304, or 305." }),
+ }),
+ },
+ ),
+ );
diff --git a/pkgs/rssbook/src/routers/feeds/multimedia/huxiu/index.test.ts b/pkgs/rssbook/src/routers/feeds/multimedia/huxiu/index.test.ts
new file mode 100644
index 0000000..9e42d90
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/multimedia/huxiu/index.test.ts
@@ -0,0 +1,11 @@
+import { describe, expect, test } from "bun:test";
+import { getRouteData } from "@/utils/tests/source";
+import huxiu from ".";
+
+describe("Huxiu", () => {
+ test("fetches moments", async () => {
+ const data = await getRouteData(huxiu, "/moment");
+
+ expect(data.title).toContain("Huxiu");
+ }, 20_000);
+});
diff --git a/pkgs/rssbook/src/routers/feeds/multimedia/huxiu/index.ts b/pkgs/rssbook/src/routers/feeds/multimedia/huxiu/index.ts
new file mode 100644
index 0000000..38c418d
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/multimedia/huxiu/index.ts
@@ -0,0 +1,55 @@
+import type { Data, DataItem } from "@/types";
+import { Source } from "@/utils";
+
+type HuxiuMoment = {
+ content?: string;
+ id?: string | number;
+ published_at?: string;
+ title?: string;
+};
+
+export default new Source({
+ description: "Moment feeds from Huxiu (虎嗅).",
+ domain: "huxiu.com",
+ slug: "huxiu",
+ title: "Huxiu",
+}).feed(
+ {
+ description: "Fetch Huxiu 24-hour moment items.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Moments",
+ },
+ (app) =>
+ app.get("/moment", async ({ cache, date, ofetch }) => {
+ const apiUrl = "https://api-article.huxiu.com/web-v3/moment/feed";
+ const item = await cache.tryGet(apiUrl, async () => {
+ const response = await ofetch<{
+ data?: { moment_list?: { datalist?: HuxiuMoment[] } };
+ }>(apiUrl, {
+ body: new URLSearchParams({ platform: "www" }),
+ method: "POST",
+ responseType: "json",
+ });
+ return (response.data?.moment_list?.datalist ?? []).map(
+ (moment) =>
+ ({
+ date: date(moment.published_at ?? new Date().toISOString()),
+ description: moment.content,
+ link: moment.id
+ ? `https://www.huxiu.com/moment/${moment.id}.html`
+ : "https://www.huxiu.com/moment",
+ title: moment.title || moment.content?.slice(0, 80) || "Huxiu Moment",
+ }) satisfies DataItem,
+ );
+ });
+
+ return {
+ description: "Huxiu 24-hour moments.",
+ item,
+ language: "zh-CN",
+ link: "https://www.huxiu.com/moment",
+ title: "Huxiu Moments",
+ } satisfies Data;
+ }),
+);
diff --git a/pkgs/rssbook/src/routers/feeds/multimedia/ifanr/index.test.ts b/pkgs/rssbook/src/routers/feeds/multimedia/ifanr/index.test.ts
new file mode 100644
index 0000000..a7b72d2
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/multimedia/ifanr/index.test.ts
@@ -0,0 +1,30 @@
+import { describe, expect, test } from "bun:test";
+import type { Data } from "@/types";
+import { getRouteData } from "@/utils/tests/source";
+import source from ".";
+
+function expectData(data: Data) {
+ expect(data.title.length).toBeGreaterThan(0);
+ expect(data.link).toStartWith("https://");
+ expect(data.item?.length).toBeGreaterThan(0);
+}
+
+describe("iFanr", () => {
+ test("fetches home articles", async () => {
+ const data = await getRouteData(source, "/index");
+
+ expectData(data);
+ }, 20_000);
+
+ test("fetches category articles", async () => {
+ const data = await getRouteData(source, "/category/早报");
+
+ expectData(data);
+ }, 20_000);
+
+ test("fetches digest buzz", async () => {
+ const data = await getRouteData(source, "/digest");
+
+ expectData(data);
+ }, 20_000);
+});
diff --git a/pkgs/rssbook/src/routers/feeds/multimedia/ifanr/index.ts b/pkgs/rssbook/src/routers/feeds/multimedia/ifanr/index.ts
new file mode 100644
index 0000000..95f589b
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/multimedia/ifanr/index.ts
@@ -0,0 +1,187 @@
+import type { Data, DataItem } from "@/types";
+import { Source, t } from "@/utils";
+
+type IfanrAuthor = {
+ name?: string;
+};
+
+type IfanrArticle = {
+ buzz_original_url?: string;
+ created_at?: number;
+ created_by?: IfanrAuthor;
+ id?: string | number;
+ post_category?: string;
+ post_content?: string;
+ post_cover_image?: string;
+ post_id?: string | number;
+ post_title?: string;
+ post_url?: string;
+ published_at?: number;
+ updated_at?: number;
+};
+
+const ROOT_URL = "https://www.ifanr.com";
+const API_URL = "https://sso.ifanr.com";
+
+const PATH_LIST: Record = {
+ 产品: "product",
+ 早报: "ifanrnews",
+ 糖纸众测: "tangzhi-evaluation",
+ 评测: "review",
+};
+
+function buildArticleDescription(article: IfanrArticle): string {
+ let description = "";
+
+ if (article.post_cover_image) {
+ description += ` `;
+ }
+
+ description += article.post_content ?? "";
+ return description;
+}
+
+export default new Source({
+ description: "Article feeds from iFanr (爱范儿).",
+ domain: "ifanr.com",
+ slug: "ifanr",
+ title: "iFanr",
+})
+ .feed(
+ {
+ description: "Fetch latest articles from the iFanr home page.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Home",
+ },
+ (app) =>
+ app.get("/index", async ({ cache, date, ofetch }) => {
+ const apiUrl = `${API_URL}/api/v5/wp/web-feed/?limit=20&offset=0`;
+ const item = await cache.tryGet(apiUrl, async () => {
+ const response = await ofetch<{ objects?: IfanrArticle[] }>(apiUrl, {
+ responseType: "json",
+ });
+
+ return await Promise.all(
+ (response.objects ?? []).map(async (article) => {
+ const detail = await cache.tryGet(
+ `ifanr:article:${article.post_id}`,
+ async () => {
+ const detailResponse = await ofetch<{ objects?: IfanrArticle[] }>(
+ `${API_URL}/api/v5/wp/article/?post_id=${article.post_id}`,
+ { responseType: "json" },
+ );
+ return (detailResponse.objects?.[0] ?? {}) as IfanrArticle;
+ },
+ );
+
+ return {
+ author: [{ name: article.created_by?.name ?? detail.created_by?.name ?? "" }],
+ date: date((article.created_at ?? Date.now() / 1000) * 1000),
+ description: buildArticleDescription(detail),
+ link: article.post_url ?? `${ROOT_URL}/${article.post_id}`,
+ title: article.post_title ?? "Untitled",
+ } satisfies DataItem;
+ }),
+ );
+ });
+
+ return {
+ description: "Latest iFanr home page articles.",
+ item,
+ language: "zh-CN",
+ link: ROOT_URL,
+ title: "iFanr",
+ } satisfies Data;
+ }),
+ )
+ .feed(
+ {
+ description: "Fetch iFanr articles by category.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Category",
+ },
+ (app) =>
+ app.get(
+ "/category/:category",
+ async ({ cache, date, ofetch, params: { category } }) => {
+ const nameEncode = encodeURIComponent(decodeURIComponent(category));
+ const apiUrl = `${API_URL}/api/v5/wp/article/?post_category=${nameEncode}&limit=20&offset=0`;
+ const item = await cache.tryGet(apiUrl, async () => {
+ const response = await ofetch<{ objects?: IfanrArticle[] }>(apiUrl, {
+ responseType: "json",
+ });
+
+ return (response.objects ?? []).map(
+ (article) =>
+ ({
+ author: [{ name: article.created_by?.name ?? "" }],
+ date: date((article.published_at ?? Date.now() / 1000) * 1000),
+ description: buildArticleDescription(article),
+ link: article.post_url ?? `${ROOT_URL}/${article.post_id}`,
+ title: article.post_title ?? "Untitled",
+ }) satisfies DataItem,
+ );
+ });
+
+ return {
+ description: `iFanr category ${category}.`,
+ item,
+ language: "zh-CN",
+ link: `${ROOT_URL}/category/${PATH_LIST[category] ?? category}`,
+ title: `#${category} - iFanr`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ category: t.String({
+ description: "Category name, for example 早报, 评测, 糖纸众测, or 产品.",
+ }),
+ }),
+ },
+ ),
+ )
+ .feed(
+ {
+ description: "Fetch iFanr digest buzz news.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Digest",
+ },
+ (app) =>
+ app.get("/digest", async ({ date, load, ofetch }) => {
+ const targetUrl = `${ROOT_URL}/digest`;
+ const apiUrl = `${API_URL}/api/v5/wp/buzz?limit=20&offset=0`;
+
+ const [response, targetResponse] = await Promise.all([
+ ofetch<{ objects?: IfanrArticle[] }>(apiUrl, { responseType: "json" }),
+ ofetch(targetUrl, { responseType: "text" }) as Promise,
+ ]);
+
+ const $ = load(targetResponse);
+ const language = ($("html").attr("lang") ?? "zh-CN") as "zh-CN";
+
+ const item = (response.objects ?? []).map(
+ (article) =>
+ ({
+ author: [{ name: article.created_by?.name ?? "" }],
+ date: date((article.created_at ?? Date.now() / 1000) * 1000),
+ description: article.post_content,
+ id: `ifanr-digest-${article.post_id}`,
+ link: `${ROOT_URL}/digest/${article.post_id}`,
+ title: article.post_title ?? "Untitled",
+ }) satisfies DataItem,
+ );
+
+ return {
+ author: { name: $("meta[property='og:site_name']").attr("content") ?? "" },
+ description: $("title").text(),
+ image: $("img.c-header-navbar__logo").attr("src"),
+ item,
+ language,
+ link: targetUrl,
+ title: $("title").text(),
+ } satisfies Data;
+ }),
+ );
diff --git a/pkgs/rssbook/src/routers/feeds/multimedia/index.ts b/pkgs/rssbook/src/routers/feeds/multimedia/index.ts
index 75ab905..fa09e46 100644
--- a/pkgs/rssbook/src/routers/feeds/multimedia/index.ts
+++ b/pkgs/rssbook/src/routers/feeds/multimedia/index.ts
@@ -1,6 +1,29 @@
import { Category } from "@/utils";
+import cyzone from "./cyzone";
+import geekpark from "./geekpark";
+import huxiu from "./huxiu";
+import ifanr from "./ifanr";
+import ithome from "./ithome";
+import kr36 from "./kr36";
+import leiphone from "./leiphone";
+import pingwest from "./pingwest";
+import solidot from "./solidot";
+import sspai from "./sspai";
+import thepaper from "./thepaper";
export default new Category(
"multimedia",
"Various **media content**, including videos, audio, images, and interactive media.",
-).use({});
+).use({
+ cyzone,
+ geekpark,
+ huxiu,
+ ifanr,
+ ithome,
+ kr36,
+ leiphone,
+ pingwest,
+ solidot,
+ sspai,
+ thepaper,
+});
diff --git a/pkgs/rssbook/src/routers/feeds/multimedia/ithome/index.test.ts b/pkgs/rssbook/src/routers/feeds/multimedia/ithome/index.test.ts
new file mode 100644
index 0000000..f4e52e4
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/multimedia/ithome/index.test.ts
@@ -0,0 +1,36 @@
+import { describe, expect, test } from "bun:test";
+import type { Data } from "@/types";
+import { getRouteData } from "@/utils/tests/source";
+import source from ".";
+
+function expectData(data: Data) {
+ expect(data.title.length).toBeGreaterThan(0);
+ expect(data.link).toStartWith("https://");
+ expect(data.item?.length).toBeGreaterThan(0);
+}
+
+describe("IT Home", () => {
+ test("fetches category news", async () => {
+ const data = await getRouteData(source, "/it");
+
+ expectData(data);
+ }, 20_000);
+
+ test("fetches ranking list", async () => {
+ const data = await getRouteData(source, "/ranking/24h");
+
+ expectData(data);
+ }, 20_000);
+
+ test("fetches tag articles", async () => {
+ const data = await getRouteData(source, "/tag/win11");
+
+ expectData(data);
+ }, 20_000);
+
+ test("fetches taiwan feeds", async () => {
+ const data = await getRouteData(source, "/tw/news");
+
+ expectData(data);
+ }, 20_000);
+});
diff --git a/pkgs/rssbook/src/routers/feeds/multimedia/ithome/index.ts b/pkgs/rssbook/src/routers/feeds/multimedia/ithome/index.ts
new file mode 100644
index 0000000..7e3e12e
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/multimedia/ithome/index.ts
@@ -0,0 +1,310 @@
+import type { Data, DataItem } from "@/types";
+import { Source, t } from "@/utils";
+
+const CATEGORIES: Record = {
+ android: "Android",
+ digi: "Digital",
+ ipad: "iPad",
+ iphone: "iPhone",
+ it: "IT News",
+ next: "Smart Era",
+ soft: "Software",
+ win10: "Windows 10",
+ win11: "Windows 11",
+};
+
+const TW_CATEGORIES: Record = {
+ "big-data": "Big Data",
+ cloud: "Cloud",
+ devops: "DevOps",
+ news: "News",
+ security: "Security",
+};
+
+const FILE_LINK_RE = /\.(pdf|docx?|xlsx?|xls|zip|rar|png|jpe?g|gif|bmp|mp4|mp3|txt)$/i;
+
+function shouldFetchArticle(link: string, rootURL: string): boolean {
+ if (!link || FILE_LINK_RE.test(link)) return false;
+
+ try {
+ return new URL(link).origin === new URL(rootURL).origin;
+ } catch {
+ return false;
+ }
+}
+
+async function enrichArticle(
+ item: DataItem,
+ ofetch: (url: string, options?: object) => Promise,
+ load: typeof import("cheerio").load,
+): Promise {
+ if (!shouldFetchArticle(item.link, "https://www.ithome.com")) return item;
+
+ try {
+ const html = (await ofetch(item.link, { responseType: "text" })) as string;
+ const $ = load(html);
+ const post = $("#paragraph");
+
+ post.find("img[data-original]").each((_, ele) => {
+ const $ele = $(ele);
+ const original = $ele.attr("data-original");
+ if (original) {
+ $ele.attr("src", original);
+ }
+ $ele.removeAttr("class");
+ $ele.removeAttr("data-original");
+ });
+
+ const contentHTML = post.html();
+ if (contentHTML) {
+ item.description = contentHTML;
+ }
+
+ const pubTime = $("#pubtime_baidu").text().trim();
+ if (pubTime) {
+ item.date = new Date(`${pubTime} GMT+8`);
+ }
+
+ const author = $(".author_baidu > strong").text().trim();
+ if (author) {
+ item.author = [{ name: author }];
+ }
+ } catch {
+ return item;
+ }
+
+ return item;
+}
+
+export default new Source({
+ description: "Category news feeds from IT Home (IT之家).",
+ domain: "ithome.com",
+ slug: "ithome",
+ title: "IT Home",
+})
+ .feed(
+ {
+ description: "Fetch category news from IT Home with full article content.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Category News",
+ },
+ (app) =>
+ app.get(
+ "/:category",
+ async ({ cache, date, load, ofetch, params: { category }, toAbsoluteURL }) => {
+ const link = `https://${category}.ithome.com/`;
+ const item = await cache.tryGet(link, async () => {
+ const html = (await ofetch(link, { responseType: "text" })) as string;
+ const $ = load(html);
+ const listItems = $("#list h2 a, .list h2 a")
+ .toArray()
+ .map((anchor) => {
+ const element = $(anchor);
+ return {
+ date: date(new Date()),
+ link: toAbsoluteURL(element.attr("href") ?? "", link),
+ title: element.text().trim(),
+ } satisfies DataItem;
+ })
+ .filter((entry) => entry.title && entry.link)
+ .slice(0, 10);
+
+ return await Promise.all(
+ listItems.map((listItem) => enrichArticle(listItem, ofetch, load)),
+ );
+ });
+
+ return {
+ description: `IT Home ${CATEGORIES[category] ?? category} news.`,
+ item,
+ language: "zh-CN",
+ link,
+ title: `IT Home - ${CATEGORIES[category] ?? category}`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ category: t.String({
+ default: "it",
+ description: "IT Home category, for example it, soft, android, or iphone.",
+ }),
+ }),
+ },
+ ),
+ )
+ .feed(
+ {
+ description: "Fetch IT Home ranking lists.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Ranking",
+ },
+ (app) =>
+ app.get(
+ "/ranking/:type",
+ async ({ cache, date, load, ofetch, params: { type } }) => {
+ const id =
+ type === "24h"
+ ? "d-1"
+ : type === "7days"
+ ? "d-2"
+ : type === "monthly"
+ ? "d-3"
+ : undefined;
+ if (!id) {
+ throw new Error(`Invalid ranking type: ${type}`);
+ }
+
+ const rankingUrl = "https://www.ithome.com/block/rank.html";
+ const item = await cache.tryGet(rankingUrl + type, async () => {
+ const html = (await ofetch(rankingUrl, { responseType: "text" })) as string;
+ const $ = load(html);
+
+ const listItems = $(`#${id} > li`)
+ .toArray()
+ .map((li) => {
+ const $li = $(li);
+ return {
+ date: date(new Date()),
+ link: $li.find("a").attr("href") ?? "",
+ title: $li.find("a").text().trim(),
+ } satisfies DataItem;
+ })
+ .filter((entry) => entry.title && entry.link);
+
+ return await Promise.all(
+ listItems.map((listItem) => enrichArticle(listItem, ofetch, load)),
+ );
+ });
+
+ const titles: Record = {
+ "7days": "7 Days Hot",
+ "24h": "24h Hot",
+ monthly: "Monthly Hot",
+ };
+
+ return {
+ description: `IT Home ${titles[type]} ranking.`,
+ item,
+ language: "zh-CN",
+ link: "https://www.ithome.com",
+ title: `IT Home - ${titles[type]}`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ type: t.String({
+ description: "Ranking type: 24h, 7days, or monthly.",
+ }),
+ }),
+ },
+ ),
+ )
+ .feed(
+ {
+ description: "Fetch IT Home articles by tag.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Tag",
+ },
+ (app) =>
+ app.get(
+ "/tag/:tag",
+ async ({ cache, date, load, ofetch, params: { tag } }) => {
+ const url = `https://www.ithome.com/tag/${tag}`;
+ const item = await cache.tryGet(url, async () => {
+ const html = (await ofetch(url, { responseType: "text" })) as string;
+ const $ = load(html);
+
+ const listItems = $("ul.bl > li")
+ .toArray()
+ .map((li) => {
+ const $li = $(li);
+ const timestamp = $li.find("div.c").attr("data-ot");
+ return {
+ date: timestamp ? date(timestamp, 8) : date(new Date()),
+ link: $li.find("h2 > a").attr("href") ?? "",
+ title: $li.find("h2 > a").text().trim(),
+ } satisfies DataItem;
+ })
+ .filter((entry) => entry.title && entry.link);
+
+ return await Promise.all(
+ listItems.map((listItem) => enrichArticle(listItem, ofetch, load)),
+ );
+ });
+
+ return {
+ description: `IT Home articles tagged ${tag}.`,
+ item,
+ language: "zh-CN",
+ link: url,
+ title: `IT Home - #${tag}`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ tag: t.String({ description: "Tag name." }),
+ }),
+ },
+ ),
+ )
+ .feed(
+ {
+ description: "Fetch feeds from IT Home Taiwan.",
+ language: "zh-TW",
+ maintainer: { name: "RSSBook" },
+ title: "Taiwan Feeds",
+ },
+ (app) =>
+ app.get(
+ "/tw/:caty",
+ async ({ cache, date, load, ofetch, params: { caty } }) => {
+ const baseUrl = "https://www.ithome.com.tw";
+ const currentUrl = `${baseUrl}/${caty}/feeds`;
+ const item = await cache.tryGet(currentUrl, async () => {
+ const html = (await ofetch(currentUrl, { responseType: "text" })) as string;
+ const $ = load(html);
+ const name = $("a.active-trail").text().trim() || TW_CATEGORIES[caty] || caty;
+
+ return await Promise.all(
+ $(".title a")
+ .toArray()
+ .map((anchor) => {
+ const $anchor = $(anchor);
+ const link = `${baseUrl}${$anchor.attr("href")}`;
+ return cache.tryGet(link, async () => {
+ const articleHtml = (await ofetch(link, { responseType: "text" })) as string;
+ const $article = load(articleHtml);
+ return {
+ author: [{ name: $article(".author a").text().trim() }],
+ category: [{ name }],
+ date: date($article(".created").text().trim(), 8),
+ description: $article("article").eq(0).html() ?? "",
+ link,
+ title: $article(".page-header").text().trim(),
+ } satisfies DataItem;
+ });
+ }),
+ );
+ });
+
+ return {
+ description: `IT Home Taiwan ${caty} feeds.`,
+ item,
+ language: "zh-TW",
+ link: currentUrl,
+ title: `${TW_CATEGORIES[caty] ?? caty} | IT Home Taiwan`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ caty: t.String({
+ description:
+ "Taiwan category, for example news, big-data, cloud, devops, or security.",
+ }),
+ }),
+ },
+ ),
+ );
diff --git a/pkgs/rssbook/src/routers/feeds/multimedia/kr36/index.test.ts b/pkgs/rssbook/src/routers/feeds/multimedia/kr36/index.test.ts
new file mode 100644
index 0000000..0bbe7d9
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/multimedia/kr36/index.test.ts
@@ -0,0 +1,11 @@
+import { describe, expect, test } from "bun:test";
+import { getRouteData } from "@/utils/tests/source";
+import kr36 from ".";
+
+describe("36Kr", () => {
+ test("fetches news", async () => {
+ const data = await getRouteData(kr36, "/news");
+
+ expect(data.title).toContain("36Kr");
+ }, 20_000);
+});
diff --git a/pkgs/rssbook/src/routers/feeds/multimedia/kr36/index.ts b/pkgs/rssbook/src/routers/feeds/multimedia/kr36/index.ts
new file mode 100644
index 0000000..dfe08ca
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/multimedia/kr36/index.ts
@@ -0,0 +1,49 @@
+import type { Data, DataItem } from "@/types";
+import { Source } from "@/utils";
+
+export default new Source({
+ description: "News feeds from 36Kr (36氪).",
+ domain: "36kr.com",
+ slug: "kr36",
+ title: "36Kr",
+}).feed(
+ {
+ description: "Fetch article links from the 36Kr news page.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "News",
+ },
+ (app) =>
+ app.get("/news", async ({ cache, date, load, ofetch, toAbsoluteURL }) => {
+ const link = "https://36kr.com/newsflashes";
+ const item = await cache.tryGet(link, async () => {
+ const html = await ofetch(link, { responseType: "text" });
+ const $ = load(html);
+ const seen = new Set();
+ return $("a[href*='/p/']")
+ .toArray()
+ .map((anchor) => {
+ const element = $(anchor);
+ return {
+ date: date(new Date()),
+ link: toAbsoluteURL(element.attr("href") ?? "", link),
+ title: element.text().trim().replaceAll(/\s+/g, " "),
+ } satisfies DataItem;
+ })
+ .filter((entry) => {
+ if (!entry.title || seen.has(entry.link)) return false;
+ seen.add(entry.link);
+ return true;
+ })
+ .slice(0, 20);
+ });
+
+ return {
+ description: "Latest 36Kr news links.",
+ item,
+ language: "zh-CN",
+ link,
+ title: "36Kr News",
+ } satisfies Data;
+ }),
+);
diff --git a/pkgs/rssbook/src/routers/feeds/multimedia/leiphone/index.test.ts b/pkgs/rssbook/src/routers/feeds/multimedia/leiphone/index.test.ts
new file mode 100644
index 0000000..690fa84
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/multimedia/leiphone/index.test.ts
@@ -0,0 +1,30 @@
+import { describe, expect, test } from "bun:test";
+import type { Data } from "@/types";
+import { getRouteData } from "@/utils/tests/source";
+import source from ".";
+
+function expectData(data: Data) {
+ expect(data.title.length).toBeGreaterThan(0);
+ expect(data.link).toStartWith("https://");
+ expect(data.item?.length).toBeGreaterThan(0);
+}
+
+describe("Leiphone", () => {
+ test("fetches home articles", async () => {
+ const data = await getRouteData(source, "/index");
+
+ expectData(data);
+ }, 20_000);
+
+ test("fetches newsflash", async () => {
+ const data = await getRouteData(source, "/newsflash");
+
+ expectData(data);
+ }, 20_000);
+
+ test("fetches category articles", async () => {
+ const data = await getRouteData(source, "/category/ai");
+
+ expectData(data);
+ }, 20_000);
+});
diff --git a/pkgs/rssbook/src/routers/feeds/multimedia/leiphone/index.ts b/pkgs/rssbook/src/routers/feeds/multimedia/leiphone/index.ts
new file mode 100644
index 0000000..8fed959
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/multimedia/leiphone/index.ts
@@ -0,0 +1,156 @@
+import type { Data, DataItem } from "@/types";
+import type { Cache } from "@/utils";
+import { Source, t } from "@/utils";
+
+const ROOT_URL = "https://www.leiphone.com";
+
+async function enrichLeiphoneArticle(
+ link: string,
+ ofetch: (url: string, options?: object) => Promise,
+ load: (html: string) => ReturnType,
+ cache: Cache,
+): Promise {
+ return cache.tryGet(link, async () => {
+ const html = (await ofetch(link, { responseType: "text" })) as string;
+ const $ = load(html);
+
+ let description = "";
+ const topImg = $(".top-img").html();
+ if (topImg) {
+ description += topImg;
+ }
+
+ const lead = $(".article-lead").text();
+ if (lead) {
+ description += `${lead}
`;
+ }
+
+ const content = $(".lph-article-comView").html();
+ if (content) {
+ description += content;
+ }
+
+ return {
+ author: [{ name: $(".aut > a").text().trim() }],
+ date: new Date($(".time").text().trim()),
+ description,
+ link,
+ title: $(".headTit").text().trim(),
+ } satisfies DataItem;
+ });
+}
+
+export default new Source({
+ description: "Article feeds from Leiphone (雷锋网).",
+ domain: "leiphone.com",
+ slug: "leiphone",
+ title: "Leiphone",
+})
+ .feed(
+ {
+ description: "Fetch latest articles from the Leiphone home page.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Home",
+ },
+ (app) =>
+ app.get("/index", async ({ cache, load, ofetch }) => {
+ const url = ROOT_URL;
+ const item = await cache.tryGet(url, async () => {
+ const html = (await ofetch(url, { responseType: "text" })) as string;
+ const $ = load(html);
+
+ const links = $(".word > h3 > a")
+ .slice(0, 10)
+ .toArray()
+ .map((anchor) => $(anchor).attr("href"))
+ .filter((href): href is string => !!href);
+
+ return await Promise.all(
+ links.map((link) => enrichLeiphoneArticle(link, ofetch, load, cache)),
+ );
+ });
+
+ return {
+ description: "Latest Leiphone articles.",
+ item,
+ language: "zh-CN",
+ link: url,
+ title: "Leiphone",
+ } satisfies Data;
+ }),
+ )
+ .feed(
+ {
+ description: "Fetch Leiphone industry newsflash.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Newsflash",
+ },
+ (app) =>
+ app.get("/newsflash", async ({ cache, load, ofetch }) => {
+ const url = `${ROOT_URL}/site/YejieKuaixun`;
+ const item = await cache.tryGet(url, async () => {
+ const response = await ofetch<{ article?: { url?: string }[] }>(url, {
+ responseType: "json",
+ });
+ const links = (response.article ?? [])
+ .map((article) => article.url)
+ .filter((href): href is string => !!href);
+
+ return await Promise.all(
+ links.map((link) => enrichLeiphoneArticle(link, ofetch, load, cache)),
+ );
+ });
+
+ return {
+ description: "Leiphone industry newsflash.",
+ item,
+ language: "zh-CN",
+ link: url,
+ title: "Leiphone Newsflash",
+ } satisfies Data;
+ }),
+ )
+ .feed(
+ {
+ description: "Fetch Leiphone articles by category.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Category",
+ },
+ (app) =>
+ app.get(
+ "/category/:keyword",
+ async ({ cache, load, ofetch, params: { keyword } }) => {
+ const url = `${ROOT_URL}/category/${keyword}`;
+ const item = await cache.tryGet(url, async () => {
+ const html = (await ofetch(url, { responseType: "text" })) as string;
+ const $ = load(html);
+
+ const links = $(".word > h3 > a")
+ .slice(0, 10)
+ .toArray()
+ .map((anchor) => $(anchor).attr("href"))
+ .filter((href): href is string => !!href);
+
+ return await Promise.all(
+ links.map((link) => enrichLeiphoneArticle(link, ofetch, load, cache)),
+ );
+ });
+
+ return {
+ description: `Leiphone category ${keyword}.`,
+ item,
+ language: "zh-CN",
+ link: url,
+ title: `Leiphone - ${keyword}`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ keyword: t.String({ description: "Category keyword." }),
+ }),
+ },
+ ),
+ );
diff --git a/pkgs/rssbook/src/routers/feeds/multimedia/pingwest/index.test.ts b/pkgs/rssbook/src/routers/feeds/multimedia/pingwest/index.test.ts
new file mode 100644
index 0000000..76e33f8
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/multimedia/pingwest/index.test.ts
@@ -0,0 +1,30 @@
+import { describe, expect, test } from "bun:test";
+import type { Data } from "@/types";
+import { getRouteData } from "@/utils/tests/source";
+import source from ".";
+
+function expectData(data: Data) {
+ expect(data.title.length).toBeGreaterThan(0);
+ expect(data.link).toStartWith("https://");
+ expect(data.item?.length).toBeGreaterThan(0);
+}
+
+describe("Pingwest", () => {
+ test("fetches status news", async () => {
+ const data = await getRouteData(source, "/status");
+
+ expectData(data);
+ }, 20_000);
+
+ test("fetches tag articles", async () => {
+ const data = await getRouteData(source, "/tag/ChinaJoy/1");
+
+ expectData(data);
+ }, 20_000);
+
+ test("fetches user articles", async () => {
+ const data = await getRouteData(source, "/user/7781550877/article");
+
+ expectData(data);
+ }, 20_000);
+});
diff --git a/pkgs/rssbook/src/routers/feeds/multimedia/pingwest/index.ts b/pkgs/rssbook/src/routers/feeds/multimedia/pingwest/index.ts
new file mode 100644
index 0000000..24884f0
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/multimedia/pingwest/index.ts
@@ -0,0 +1,339 @@
+import type { Data, DataItem } from "@/types";
+import type { Cache } from "@/utils";
+import { Source, t } from "@/utils";
+
+const ROOT_URL = "https://www.pingwest.com";
+
+type TagInfo = {
+ tagId: string;
+ tagName: string;
+};
+
+type UserInfo = {
+ realUid: string;
+ userAvatar?: string;
+ userName: string;
+ userSign: string;
+};
+
+function modifiedLink(link?: string): string {
+ if (!link) return "";
+ return link.startsWith("http") ? link : `https:${link}`;
+}
+
+function cleanImageUrl(src?: string): string | undefined {
+ if (!src) return undefined;
+ return src.split("?x-")[0];
+}
+
+async function getFullArticle(
+ link: string,
+ ofetch: (url: string, options?: object) => Promise,
+ load: (html: string) => ReturnType,
+ cache: Cache,
+): Promise {
+ return cache.tryGet(link, async () => {
+ const html = (await ofetch(link, { responseType: "text" })) as string;
+ const $ = load(html);
+
+ $("img").each((_, ele) => {
+ const $ele = $(ele);
+ const src = $ele.attr("src");
+ if (src?.includes("?x-")) {
+ $ele.attr("src", src.split("?x-")[0]);
+ }
+ });
+
+ return $("section .article-style").html() ?? "";
+ });
+}
+
+function statusListParser(
+ listHtml: string,
+ load: (html: string) => ReturnType,
+ date: (input: string | number | Date, timezone?: number) => Date,
+): DataItem[] {
+ const $ = load(listHtml);
+
+ return $(".item")
+ .toArray()
+ .map((ele) => {
+ const timestamp = (ele.attribs as { pt?: string }).pt;
+ const $item = load($.html(ele));
+ const mainNode = $item(".news-detail");
+ const imgsStr = mainNode
+ .find("img")
+ .toArray()
+ .map((img) => ` `)
+ .join(" ");
+ const link = mainNode.find(".content a").first().attr("href");
+ const content = mainNode
+ .text()
+ .trim()
+ .replace(/展开全文$/, "");
+
+ return {
+ date: timestamp ? date(Number(timestamp) * 1000) : date(new Date()),
+ description: [content, imgsStr].filter(Boolean).join(" "),
+ link: modifiedLink(link),
+ title: content,
+ } satisfies DataItem;
+ });
+}
+
+async function articleListParser(
+ listHtml: string,
+ needFullText: boolean,
+ ofetch: (url: string, options?: object) => Promise,
+ load: (html: string) => ReturnType,
+ date: (input: string | number | Date, timezone?: number) => Date,
+ cache: Cache,
+): Promise {
+ const $ = load(listHtml);
+
+ return await Promise.all(
+ $(".item")
+ .toArray()
+ .map(async (ele) => {
+ const $item = load($.html(ele));
+ const isNewsflash = $item(".news-detail").children().length <= 2;
+
+ const titleNode = isNewsflash
+ ? $item(".news-detail .content .text")
+ : $item(".news-detail .title");
+ const authorNode = isNewsflash
+ ? $item(".news-detail .content .op")
+ : $item(".news-detail .author");
+
+ const title = titleNode.find("a").text().trim();
+ const prefixUrl = titleNode.find("a").attr("href");
+ const link = modifiedLink(prefixUrl);
+ const imgUrl = cleanImageUrl($item(".news-img img").attr("src"));
+ const author = authorNode.children().first().text().trim();
+ const timestamp = authorNode.find(".time").text().trim();
+
+ let itemDate = date(timestamp, 8);
+ if (!timestamp) {
+ itemDate = date(new Date());
+ }
+
+ let description = $item(".desc").text().trim();
+ if (needFullText && link) {
+ description += await getFullArticle(link, ofetch, load, cache);
+ } else if (imgUrl) {
+ description += ` `;
+ }
+
+ return {
+ author: author ? [{ name: author }] : undefined,
+ date: itemDate,
+ description,
+ link,
+ title,
+ } satisfies DataItem;
+ }),
+ );
+}
+
+export default new Source({
+ description: "News feeds from Pingwest (品玩).",
+ domain: "pingwest.com",
+ slug: "pingwest",
+ title: "Pingwest",
+})
+ .feed(
+ {
+ description: "Fetch real-time news from Pingwest status.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Status",
+ },
+ (app) =>
+ app.get("/status", async ({ cache, date, load, ofetch }) => {
+ const url = `${ROOT_URL}/api/state/list?page=1`;
+ const item = await cache.tryGet(url, async () => {
+ const response = await ofetch<{ data?: { list?: string } }>(url, {
+ headers: { Referer: ROOT_URL },
+ responseType: "json",
+ });
+
+ const $ = load(response.data?.list ?? "");
+
+ return $("section.item")
+ .toArray()
+ .map((ele) => {
+ const timestamp = (ele.attribs as { "data-t"?: string })["data-t"];
+ const $item = load($.html(ele));
+ const rightNode = $item(".news-info");
+ const tag = rightNode.find(".item-tag-list").text().trim();
+ const title = rightNode.find(".title").text().trim();
+ const link = rightNode.find("a").last().attr("href");
+ let description = rightNode.text().trim();
+ const imgUrl = $item(".news-img img").attr("src");
+
+ if (imgUrl) {
+ description += ` `;
+ }
+
+ return {
+ category: tag ? [{ name: tag }] : undefined,
+ date: timestamp ? date(Number(timestamp) * 1000) : date(new Date()),
+ description,
+ link: modifiedLink(link),
+ title: title || tag,
+ } satisfies DataItem;
+ });
+ });
+
+ return {
+ description: "Pingwest real-time news.",
+ item,
+ language: "zh-CN",
+ link: `${ROOT_URL}/status`,
+ title: "Pingwest - Status",
+ } satisfies Data;
+ }),
+ )
+ .feed(
+ {
+ description: "Fetch Pingwest articles by tag.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Tag",
+ },
+ (app) =>
+ app.get(
+ "/tag/:tag/:type/:option?",
+ async ({ cache, date, load, ofetch, params: { tag, type, option } }) => {
+ const tagUrl = `${ROOT_URL}/tag/${tag}`;
+ const needFullText = option === "fulltext";
+ const tagInfo = await cache.tryGet(`pingwest:tag:${tag}`, async () => {
+ const res = (await ofetch(tagUrl, {
+ headers: { Referer: ROOT_URL },
+ responseType: "text",
+ })) as string;
+ const $ = load(res);
+ return {
+ tagId: $(".tag-detail").attr("data-id") ?? "",
+ tagName: $(".tag-detail .info .title").text().trim(),
+ };
+ });
+
+ if (!tagInfo.tagId) {
+ throw new Error(`Pingwest tag not found: ${tag}`);
+ }
+
+ const apiUrl = `${ROOT_URL}/api/tag_article_list?id=${tagInfo.tagId}&type=${Number(type) - 1}`;
+ const item = await cache.tryGet(apiUrl, async () => {
+ const response = await ofetch<{ data?: { list?: string } }>(apiUrl, {
+ headers: { Referer: ROOT_URL },
+ responseType: "json",
+ });
+
+ return await articleListParser(
+ response.data?.list ?? "",
+ needFullText,
+ ofetch,
+ load,
+ date,
+ cache,
+ );
+ });
+
+ return {
+ description: `Pingwest tag ${tagInfo.tagName || tag}.`,
+ item,
+ language: "zh-CN",
+ link: tagUrl,
+ title: `Pingwest - ${tagInfo.tagName || tag}`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ option: t.Optional(
+ t.String({ description: "Set to 'fulltext' for full article output." }),
+ ),
+ tag: t.String({ description: "Tag name or id." }),
+ type: t.String({ description: "Content type: 1 for latest, 2 for hot." }),
+ }),
+ },
+ ),
+ )
+ .feed(
+ {
+ description: "Fetch Pingwest articles or status updates by user.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "User",
+ },
+ (app) =>
+ app.get(
+ "/user/:uid/:type?/:option?",
+ async ({ cache, date, load, ofetch, params: { uid, type = "article", option } }) => {
+ const aimUrl = `${ROOT_URL}/user/${uid}/${type}`;
+ const needFullText = option === "fulltext";
+
+ const userInfo = await cache.tryGet(`pingwest:user:${uid}`, async () => {
+ const res = (await ofetch(aimUrl, {
+ headers: { Referer: ROOT_URL },
+ responseType: "text",
+ })) as string;
+ const $ = load(res);
+ const userInfoNode = $("#J_userId");
+ return {
+ realUid: userInfoNode.attr("data-user-id") ?? "",
+ userAvatar: $("#J_userAvatar").attr("src"),
+ userName: userInfoNode.text().trim(),
+ userSign: $("#J_userSign").text().trim(),
+ };
+ });
+
+ if (!userInfo.realUid) {
+ throw new Error(`Pingwest user not found: ${uid}`);
+ }
+
+ const apiUrl = `${ROOT_URL}/api/user_data?page=1&user_id=${userInfo.realUid}&tab=${type}`;
+ const item = await cache.tryGet(apiUrl, async () => {
+ const response = await ofetch<{ data?: { list?: string } }>(apiUrl, {
+ headers: { Referer: ROOT_URL },
+ responseType: "json",
+ });
+
+ if (type === "state") {
+ return statusListParser(response.data?.list ?? "", load, date);
+ }
+
+ return await articleListParser(
+ response.data?.list ?? "",
+ needFullText,
+ ofetch,
+ load,
+ date,
+ cache,
+ );
+ });
+
+ const typeLabel = type === "state" ? "States" : "Articles";
+
+ return {
+ description: userInfo.userSign,
+ image: userInfo.userAvatar,
+ item,
+ language: "zh-CN",
+ link: aimUrl,
+ title: `Pingwest - ${userInfo.userName} - ${typeLabel}`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ option: t.Optional(
+ t.String({ description: "Set to 'fulltext' for full article output." }),
+ ),
+ type: t.Optional(
+ t.String({ default: "article", description: "Content type: article or state." }),
+ ),
+ uid: t.String({ description: "User id." }),
+ }),
+ },
+ ),
+ );
diff --git a/pkgs/rssbook/src/routers/feeds/multimedia/solidot/index.test.ts b/pkgs/rssbook/src/routers/feeds/multimedia/solidot/index.test.ts
new file mode 100644
index 0000000..c5939ae
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/multimedia/solidot/index.test.ts
@@ -0,0 +1,11 @@
+import { describe, expect, test } from "bun:test";
+import { getRouteData } from "@/utils/tests/source";
+import solidot from ".";
+
+describe("Solidot", () => {
+ test("fetches latest stories", async () => {
+ const data = await getRouteData(solidot, "/");
+
+ expect(data.item?.length).toBeGreaterThan(0);
+ }, 20_000);
+});
diff --git a/pkgs/rssbook/src/routers/feeds/multimedia/solidot/index.ts b/pkgs/rssbook/src/routers/feeds/multimedia/solidot/index.ts
new file mode 100644
index 0000000..3e13c56
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/multimedia/solidot/index.ts
@@ -0,0 +1,30 @@
+import type { Data } from "@/types";
+import { Source } from "@/utils";
+import { parse } from "@/utils/feeds";
+
+export default new Source({
+ description: "RSS feeds from Solidot.",
+ domain: "solidot.org",
+ slug: "solidot",
+ title: "Solidot",
+}).feed(
+ {
+ description: "Fetch the latest Solidot stories.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Latest Stories",
+ },
+ (app) =>
+ app.get("/", async ({ cache, ofetch }) => {
+ const feedUrl = "https://www.solidot.org/index.rss";
+ return await cache.tryGet(feedUrl, async () => {
+ const xml = await ofetch(feedUrl, { responseType: "text" });
+ return {
+ ...parse(xml),
+ language: "zh-CN",
+ link: "https://www.solidot.org",
+ title: "Solidot",
+ } satisfies Data;
+ });
+ }),
+);
diff --git a/pkgs/rssbook/src/routers/feeds/multimedia/sspai/index.test.ts b/pkgs/rssbook/src/routers/feeds/multimedia/sspai/index.test.ts
new file mode 100644
index 0000000..d120e26
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/multimedia/sspai/index.test.ts
@@ -0,0 +1,78 @@
+import { describe, expect, test } from "bun:test";
+import type { Data } from "@/types";
+import { getRouteData } from "@/utils/tests/source";
+import source from ".";
+
+function expectData(data: Data) {
+ expect(data.title.length).toBeGreaterThan(0);
+ expect(data.link).toStartWith("https://");
+ expect(data.item?.length).toBeGreaterThan(0);
+}
+
+describe("SSPAI", () => {
+ test("fetches home articles", async () => {
+ const data = await getRouteData(source, "/index");
+
+ expectData(data);
+ }, 20_000);
+
+ test("fetches matrix articles", async () => {
+ const data = await getRouteData(source, "/matrix");
+
+ expectData(data);
+ }, 20_000);
+
+ test("fetches author articles", async () => {
+ const data = await getRouteData(source, "/author/796518");
+
+ expectData(data);
+ }, 20_000);
+
+ test("fetches column articles", async () => {
+ const data = await getRouteData(source, "/column/262");
+
+ expectData(data);
+ }, 20_000);
+
+ test("fetches paid series updates", async () => {
+ const data = await getRouteData(source, "/series/77");
+
+ expectData(data);
+ }, 20_000);
+
+ test("fetches tag articles", async () => {
+ const data = await getRouteData(source, "/tag/apple");
+
+ expectData(data);
+ }, 20_000);
+
+ test("fetches topic articles", async () => {
+ const data = await getRouteData(source, "/topic/250");
+
+ expectData(data);
+ }, 20_000);
+
+ test("fetches topic square updates", async () => {
+ const data = await getRouteData(source, "/topics");
+
+ expectData(data);
+ }, 20_000);
+
+ test("fetches user bookmarks", async () => {
+ const data = await getRouteData(source, "/bookmarks/urfp0d9i");
+
+ expectData(data);
+ }, 20_000);
+
+ test("fetches newly listed paid series", async () => {
+ const data = await getRouteData(source, "/series");
+
+ expectData(data);
+ }, 20_000);
+
+ test("fetches user activity", async () => {
+ const data = await getRouteData(source, "/activity/urfp0d9i");
+
+ expectData(data);
+ }, 20_000);
+});
diff --git a/pkgs/rssbook/src/routers/feeds/multimedia/sspai/index.ts b/pkgs/rssbook/src/routers/feeds/multimedia/sspai/index.ts
new file mode 100644
index 0000000..57657f5
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/multimedia/sspai/index.ts
@@ -0,0 +1,748 @@
+import type { Data, DataItem } from "@/types";
+import { Source, t } from "@/utils";
+
+type SSPAIAuthor = {
+ nickname?: string;
+ slug?: string;
+};
+
+type SSPAIArticle = {
+ author?: SSPAIAuthor;
+ banner?: string;
+ body?: string;
+ body_extends?: { body?: string; title?: string }[];
+ created_at?: number;
+ id: number;
+ promote_image?: string;
+ released_at?: number;
+ released_time?: number;
+ summary?: string;
+ title: string;
+ topics?: { intro?: string; title?: string }[];
+};
+
+type SSPAISeries = {
+ author?: SSPAIAuthor;
+ banner?: string;
+ banner_web?: string;
+ created_at?: number;
+ description?: string;
+ id: number;
+ intro?: string;
+ price?: number;
+ probation?: boolean;
+ sell_status?: boolean;
+ title: string;
+ title_prefix?: string;
+};
+
+type SSPAITopic = {
+ author?: SSPAIAuthor;
+ banner?: string;
+ id: number;
+ intro?: string;
+ released_at?: number;
+ title: string;
+};
+
+type SSPAIActivity = {
+ action?: string;
+ author?: SSPAIAuthor;
+ created_at?: number;
+ data?: Record;
+ key?: string;
+};
+
+const ROOT_URL = "https://sspai.com";
+
+async function fetchArticleDetail(
+ id: number,
+ slug: string | undefined,
+ ofetch: (url: string, options?: object) => Promise,
+): Promise<{
+ body?: string;
+ bodyExtends?: { body?: string; title?: string }[];
+ promoteImage?: string;
+}> {
+ const infoUrl = slug
+ ? `${ROOT_URL}/api/v1/member/article/single/info/get?slug=${slug}&view=second&support_webp=true`
+ : `${ROOT_URL}/api/v1/article/info/get?id=${id}&view=second&support_webp=true`;
+
+ const response = await ofetch<{ data?: SSPAIArticle }>(infoUrl, {
+ responseType: "json",
+ });
+ const articleData = (response.data ?? {}) as SSPAIArticle;
+
+ return {
+ body: articleData.body,
+ bodyExtends: articleData.body_extends,
+ promoteImage: articleData.promote_image,
+ };
+}
+
+function buildDescription(article: {
+ body?: string;
+ bodyExtends?: { body?: string; title?: string }[];
+ promoteImage?: string;
+}): string {
+ let description = "";
+
+ if (article.promoteImage) {
+ description += ` `;
+ }
+
+ if (article.bodyExtends?.length) {
+ for (const ext of article.bodyExtends) {
+ description += `${ext.title ?? ""} ${ext.body ?? ""}`;
+ }
+ }
+
+ description += article.body ?? "";
+ return description;
+}
+
+export default new Source({
+ description: "Article feeds from SSPAI (少数派).",
+ domain: "sspai.com",
+ slug: "sspai",
+ title: "SSPAI",
+})
+ .feed(
+ {
+ description: "Fetch latest articles from the SSPAI home page API.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Home Articles",
+ },
+ (app) =>
+ app.get("/index", async ({ cache, date, ofetch }) => {
+ const apiUrl = `${ROOT_URL}/api/v1/article/index/page/get?limit=20&offset=0&created_at=0`;
+ const item = await cache.tryGet(apiUrl, async () => {
+ const response = await ofetch<{ data?: SSPAIArticle[] }>(apiUrl, {
+ responseType: "json",
+ });
+ return (response.data ?? []).map(
+ (article) =>
+ ({
+ author: [{ name: article.author?.nickname }],
+ date: date((article.released_time ?? Date.now() / 1000) * 1000),
+ description: article.summary,
+ link: `${ROOT_URL}/post/${article.id}`,
+ title: article.title,
+ }) satisfies DataItem,
+ );
+ });
+
+ return {
+ description: "Latest SSPAI home page articles.",
+ item,
+ language: "zh-CN",
+ link: ROOT_URL,
+ title: "SSPAI Home Articles",
+ } satisfies Data;
+ }),
+ )
+ .feed(
+ {
+ description: "Fetch latest articles from the SSPAI Matrix community.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Matrix",
+ },
+ (app) =>
+ app.get("/matrix", async ({ cache, date, ofetch }) => {
+ const apiUrl = `${ROOT_URL}/api/v1/articles?offset=0&limit=20&is_matrix=1&sort=matrix_at&include_total=false`;
+ const item = await cache.tryGet(apiUrl, async () => {
+ const response = await ofetch<{ list?: SSPAIArticle[] }>(apiUrl, {
+ responseType: "json",
+ });
+
+ return await Promise.all(
+ (response.list ?? []).map(async (article) => {
+ const detail = await cache.tryGet(`sspai:article:${article.id}`, () =>
+ fetchArticleDetail(article.id, undefined, ofetch),
+ );
+
+ return {
+ author: [{ name: article.author?.nickname }],
+ date: date((article.released_at ?? Date.now() / 1000) * 1000),
+ description: buildDescription(detail),
+ link: `${ROOT_URL}/post/${article.id}`,
+ title: article.title,
+ } satisfies DataItem;
+ }),
+ );
+ });
+
+ return {
+ description: "Latest SSPAI Matrix community articles.",
+ item,
+ language: "zh-CN",
+ link: `${ROOT_URL}/matrix`,
+ title: "SSPAI Matrix",
+ } satisfies Data;
+ }),
+ )
+ .feed(
+ {
+ description: "Fetch articles by a SSPAI author.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Author",
+ },
+ (app) =>
+ app.get(
+ "/author/:id",
+ async ({ cache, date, ofetch, params: { id } }) => {
+ const numericId = /^\d+$/.test(id) ? id : undefined;
+ let authorId = numericId;
+ let authorSlug = id;
+ let authorNickname = id;
+
+ if (!numericId) {
+ const userUrl = `${ROOT_URL}/api/v1/user/slug/info/get?slug=${id}`;
+ const userResponse = await ofetch<{
+ data?: { id?: number; nickname?: string; slug?: string };
+ error?: number;
+ }>(userUrl, {
+ headers: { Referer: `${ROOT_URL}/u/${id}/posts` },
+ responseType: "json",
+ });
+
+ if (userResponse.error !== 0 || !userResponse.data?.id) {
+ throw new Error(`SSPAI author not found: ${id}`);
+ }
+
+ authorId = String(userResponse.data.id);
+ authorSlug = userResponse.data.slug ?? id;
+ authorNickname = userResponse.data.nickname ?? id;
+ }
+
+ const apiUrl = `${ROOT_URL}/api/v1/articles?offset=0&limit=20&author_ids=${authorId}&include_total=false`;
+ const item = await cache.tryGet(apiUrl, async () => {
+ const response = await ofetch<{ list?: SSPAIArticle[] }>(apiUrl, {
+ responseType: "json",
+ });
+
+ if (!authorNickname && response.list?.[0]?.author?.nickname) {
+ authorNickname = response.list[0].author.nickname;
+ authorSlug = response.list[0].author.slug ?? authorSlug;
+ }
+
+ return await Promise.all(
+ (response.list ?? []).map(async (article) => {
+ const detail = await cache.tryGet(`sspai:article:${article.id}`, () =>
+ fetchArticleDetail(article.id, undefined, ofetch),
+ );
+
+ return {
+ author: [{ name: article.author?.nickname }],
+ date: date((article.released_at ?? Date.now() / 1000) * 1000),
+ description: buildDescription(detail),
+ link: `${ROOT_URL}/post/${article.id}`,
+ title: article.title,
+ } satisfies DataItem;
+ }),
+ );
+ });
+
+ return {
+ description: `Articles by SSPAI author ${authorNickname}.`,
+ item,
+ language: "zh-CN",
+ link: `${ROOT_URL}/u/${authorSlug}/posts`,
+ title: `${authorNickname} - SSPAI Author`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ id: t.String({ description: "Author slug or numeric id." }),
+ }),
+ },
+ ),
+ )
+ .feed(
+ {
+ description: "Fetch articles from a SSPAI column.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Column",
+ },
+ (app) =>
+ app.get(
+ "/column/:id",
+ async ({ cache, date, ofetch, params: { id } }) => {
+ const link = `${ROOT_URL}/column/${id}`;
+ const desApi = `${ROOT_URL}/api/v1/special_columns/${id}`;
+ const columnResponse = await ofetch<{ intro?: string; title?: string }>(desApi, {
+ headers: { Referer: link },
+ responseType: "json",
+ });
+
+ const apiUrl = `${ROOT_URL}/api/v1/articles?offset=0&limit=10&special_column_ids=${id}&include_total=false`;
+ const item = await cache.tryGet(apiUrl, async () => {
+ const response = await ofetch<{ list?: SSPAIArticle[] }>(apiUrl, {
+ headers: { Referer: link },
+ responseType: "json",
+ });
+
+ return await Promise.all(
+ (response.list ?? []).map(async (article) => {
+ const detail = await cache.tryGet(`sspai:article:${article.id}`, () =>
+ fetchArticleDetail(article.id, undefined, ofetch),
+ );
+
+ return {
+ author: [{ name: article.author?.nickname }],
+ date: date((article.created_at ?? Date.now() / 1000) * 1000),
+ description: buildDescription(detail),
+ link: `${ROOT_URL}/post/${article.id}`,
+ title: article.title,
+ } satisfies DataItem;
+ }),
+ );
+ });
+
+ return {
+ description: columnResponse.intro ?? `SSPAI column ${id}.`,
+ item,
+ language: "zh-CN",
+ link,
+ title: `${columnResponse.title ?? id} - SSPAI Column`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ id: t.String({ description: "Column id." }),
+ }),
+ },
+ ),
+ )
+ .feed(
+ {
+ description: "Fetch latest paid series updates from SSPAI.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Series Update",
+ },
+ (app) =>
+ app.get(
+ "/series/:id",
+ async ({ cache, date, ofetch, params: { id } }) => {
+ const seriesInfo = await ofetch<{ data?: SSPAISeries }>(
+ `${ROOT_URL}/api/v1/series/info/get?id=${id}&view=second`,
+ {
+ responseType: "json",
+ },
+ );
+ const seriesData = (seriesInfo.data ?? {}) as SSPAISeries;
+
+ const apiUrl = `${ROOT_URL}/api/v1/series/article/search/page/get?series_id=${id}&weight=0&sort=desc&title=&limit=20&offset=0`;
+ const item = await cache.tryGet(apiUrl, async () => {
+ const response = await ofetch<{ data?: SSPAISeries[] }>(apiUrl, {
+ responseType: "json",
+ });
+
+ return await Promise.all(
+ (response.data ?? []).map(async (article) => {
+ let description = "";
+
+ if (article.probation) {
+ const detail = await cache.tryGet(`sspai:article:${article.id}`, () =>
+ fetchArticleDetail(article.id, undefined, ofetch),
+ );
+ description = detail.body ?? "";
+ } else if (article.banner) {
+ description = ` `;
+ }
+
+ return {
+ author: [{ name: seriesData.author?.nickname }],
+ date: date((article.created_at ?? Date.now() / 1000) * 1000),
+ description,
+ link: `${ROOT_URL}/post/${article.id}`,
+ title: `${article.title_prefix ?? ""} - ${article.title}`,
+ } satisfies DataItem;
+ }),
+ );
+ });
+
+ return {
+ description: seriesData.description ?? `SSPAI paid series ${id}.`,
+ item,
+ language: "zh-CN",
+ link: `${ROOT_URL}/series/${id}`,
+ title: `${seriesData.title ?? id} - SSPAI Series`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ id: t.String({ description: "Paid series id." }),
+ }),
+ },
+ ),
+ )
+ .feed(
+ {
+ description: "Fetch articles by tag keyword from SSPAI.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Tag",
+ },
+ (app) =>
+ app.get(
+ "/tag/:keyword",
+ async ({ cache, date, ofetch, params: { keyword } }) => {
+ const encodedKeyword = encodeURIComponent(decodeURIComponent(keyword));
+ const apiUrl = `${ROOT_URL}/api/v1/articles?offset=0&limit=50&has_tag=1&tag=${encodedKeyword}&include_total=false`;
+ const host = `${ROOT_URL}/tag/${encodedKeyword}`;
+
+ const item = await cache.tryGet(apiUrl, async () => {
+ const response = await ofetch<{ list?: SSPAIArticle[] }>(apiUrl, {
+ headers: { Referer: host },
+ responseType: "json",
+ });
+
+ return await Promise.all(
+ (response.list ?? []).map(async (article) => {
+ const detail = await cache.tryGet(`sspai:article:${article.id}`, () =>
+ fetchArticleDetail(article.id, undefined, ofetch),
+ );
+
+ return {
+ author: [{ name: article.author?.nickname }],
+ date: date((article.released_at ?? Date.now() / 1000) * 1000),
+ description: buildDescription(detail),
+ link: `${ROOT_URL}/post/${article.id}`,
+ title: article.title,
+ } satisfies DataItem;
+ }),
+ );
+ });
+
+ return {
+ description: `SSPAI articles tagged #${keyword}.`,
+ item,
+ language: "zh-CN",
+ link: host,
+ title: `#${keyword} - SSPAI`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ keyword: t.String({ description: "Tag keyword." }),
+ }),
+ },
+ ),
+ )
+ .feed(
+ {
+ description: "Fetch articles from a SSPAI topic.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Topic",
+ },
+ (app) =>
+ app.get(
+ "/topic/:id",
+ async ({ cache, date, ofetch, params: { id } }) => {
+ const apiUrl = `${ROOT_URL}/api/v1/articles?offset=0&limit=20&topic_id=${id}&sort=created_at&include_total=false`;
+
+ const item = await cache.tryGet(apiUrl, async () => {
+ const response = await ofetch<{ list?: SSPAIArticle[] }>(apiUrl, {
+ responseType: "json",
+ });
+
+ return await Promise.all(
+ (response.list ?? []).map(async (article) => {
+ const detail = await cache.tryGet(`sspai:article:${article.id}`, () =>
+ fetchArticleDetail(article.id, undefined, ofetch),
+ );
+
+ return {
+ author: [{ name: article.author?.nickname }],
+ date: date((article.created_at ?? Date.now() / 1000) * 1000),
+ description: buildDescription(detail),
+ link: `${ROOT_URL}/post/${article.id}`,
+ title: article.title,
+ } satisfies DataItem;
+ }),
+ );
+ });
+
+ const topic = item?.[0]?.category?.[0]?.name ?? id;
+
+ return {
+ description: `SSPAI topic ${id} articles.`,
+ item,
+ language: "zh-CN",
+ link: `${ROOT_URL}/topic/${id}`,
+ title: `${topic} - SSPAI Topic`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ id: t.String({ description: "Topic id." }),
+ }),
+ },
+ ),
+ )
+ .feed(
+ {
+ description: "Fetch new topic collections from SSPAI topic square.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Topics",
+ },
+ (app) =>
+ app.get("/topics", async ({ cache, date, ofetch }) => {
+ const apiUrl = `${ROOT_URL}/api/v1/topics?offset=0&limit=20&include_total=false`;
+ const item = await cache.tryGet(apiUrl, async () => {
+ const response = await ofetch<{ list?: SSPAITopic[] }>(apiUrl, { responseType: "json" });
+
+ return (response.list ?? []).map(
+ (topic) =>
+ ({
+ author: [{ name: topic.author?.nickname }],
+ date: date((topic.released_at ?? Date.now() / 1000) * 1000),
+ description: ` ${topic.intro} Subscribe to the topic: ${ROOT_URL}/topic/${topic.id}`,
+ link: `${ROOT_URL}/topic/${topic.id}`,
+ title: topic.title,
+ }) satisfies DataItem,
+ );
+ });
+
+ return {
+ description: "New topic collections from SSPAI topic square.",
+ item,
+ language: "zh-CN",
+ link: `${ROOT_URL}/topics`,
+ title: "SSPAI Topic Square",
+ } satisfies Data;
+ }),
+ )
+ .feed(
+ {
+ description: "Fetch public bookmarks of a SSPAI user.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Bookmarks",
+ },
+ (app) =>
+ app.get(
+ "/bookmarks/:slug",
+ async ({ cache, date, ofetch, params: { slug } }) => {
+ const link = `${ROOT_URL}/u/${slug}/bookmark_posts`;
+ const [articleList, userInfo] = await Promise.all([
+ cache.tryGet(link, async () => {
+ const listResponse = await ofetch<{ data?: SSPAIArticle[] }>(
+ `${ROOT_URL}/api/v1/article/user/favorite/public/page/get?limit=10&offset=0&slug=${slug}&type=all`,
+ {
+ headers: { Referer: link },
+ responseType: "json",
+ },
+ );
+
+ return (listResponse.data ?? []).slice(0, 10).map(
+ (article) =>
+ ({
+ author: [{ name: article.author?.nickname ?? "" }],
+ date: date((article.released_time ?? Date.now() / 1000) * 1000),
+ description: article.summary,
+ link: `${ROOT_URL}/post/${article.id}`,
+ title: article.title,
+ }) satisfies DataItem,
+ );
+ }),
+ ofetch<{ data?: SSPAIAuthor & { nickname?: string } }>(
+ `${ROOT_URL}/api/v1/user/slug/info/get?slug=${slug}`,
+ {
+ headers: { Referer: link },
+ responseType: "json",
+ },
+ ),
+ ]);
+
+ const nickname = userInfo.data?.nickname ?? slug;
+
+ return {
+ description: `Public bookmarks of SSPAI user ${nickname}.`,
+ item: articleList,
+ language: "zh-CN",
+ link,
+ title: `${nickname} - SSPAI Bookmarks`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ slug: t.String({ description: "User slug." }),
+ }),
+ },
+ ),
+ )
+ .feed(
+ {
+ description: "Fetch newly listed paid series from SSPAI.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "New Series",
+ },
+ (app) =>
+ app.get("/series", async ({ cache, date, load, ofetch }) => {
+ const apiUrl = `${ROOT_URL}/api/v1/series/tag/all/get`;
+ const item = await cache.tryGet(apiUrl, async () => {
+ const response = await ofetch<{ data?: { children?: SSPAISeries[] }[] }>(apiUrl, {
+ responseType: "json",
+ });
+
+ const products = (response.data ?? []).flatMap((category) =>
+ (category.children ?? []).filter((child) => child.sell_status).map((child) => child),
+ );
+
+ return (await Promise.all(
+ products.map(async (product) => {
+ const link = `${ROOT_URL}/series/${product.id}`;
+ const res = await ofetch<{ data?: SSPAISeries }>(
+ `${ROOT_URL}/api/v1/series/info/get?id=${product.id}&view=second`,
+ {
+ responseType: "json",
+ },
+ );
+ const data = (res.data ?? {}) as SSPAISeries;
+ const banner = data.banner_web
+ ? ` `
+ : "";
+ const description = banner + (data.intro ?? "");
+ const price = product.price ? product.price / 100 : 0;
+
+ return {
+ author: [{ name: product.author?.nickname ?? "" }],
+ date: date(new Date()),
+ description: load(description).html() ?? description,
+ link,
+ title: `¥${price} - ${product.title}`,
+ } satisfies DataItem;
+ }),
+ )) as DataItem[];
+ });
+
+ return {
+ description: "Newly listed paid series on SSPAI.",
+ item,
+ language: "zh-CN",
+ link: `${ROOT_URL}/series`,
+ title: "SSPAI New Paid Series",
+ } satisfies Data;
+ }),
+ )
+ .feed(
+ {
+ description: "Fetch activity updates of a SSPAI user.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Activity",
+ },
+ (app) =>
+ app.get(
+ "/activity/:slug",
+ async ({ date, ofetch, params: { slug } }) => {
+ const link = `${ROOT_URL}/u/${slug}/updates`;
+ const [response, responseUser] = await Promise.all([
+ ofetch<{ data?: SSPAIActivity[] }>(
+ `${ROOT_URL}/api/v1/information/user/activity/page/get?limit=10&offset=0&slug=${slug}`,
+ {
+ headers: { Referer: link },
+ responseType: "json",
+ },
+ ),
+ ofetch<{ data?: SSPAIAuthor & { nickname?: string } }>(
+ `${ROOT_URL}/api/v1/user/slug/info/get?slug=${slug}`,
+ {
+ headers: { Referer: link },
+ responseType: "json",
+ },
+ ),
+ ]);
+
+ const userNickname = responseUser.data?.nickname ?? slug;
+ const data = response.data ?? [];
+
+ const item = data.map((activity) => {
+ const contentData = activity.data ?? {};
+ let itemTitle = "";
+ let itemDesc = "";
+ let itemUrl = link;
+
+ switch (activity.key) {
+ case "follow_user": {
+ const users =
+ (contentData as unknown as { nickname?: string; slug?: string }[]) ?? [];
+ const nicknames = users.map((u) => u.nickname ?? "");
+ const slugs = users.map(
+ (u) => `${u.nickname ?? ""} `,
+ );
+ itemTitle = `${activity.author?.nickname}${activity.action}:${nicknames.join("、")}`;
+ itemDesc = `${activity.author?.nickname}${activity.action}:${slugs.join("、")}`;
+ itemUrl = `${ROOT_URL}/u/${slug}/follow`;
+ break;
+ }
+ case "like_article": {
+ const article = contentData as { id?: number; summary?: string; title?: string };
+ itemTitle = `${activity.author?.nickname}${activity.action}:${article.title}`;
+ itemDesc = `Summary: ${article.summary}`;
+ itemUrl = `${ROOT_URL}/post/${article.id}`;
+ break;
+ }
+ case "comment_article": {
+ const comment = contentData as {
+ article_id?: number;
+ article_title?: string;
+ comment?: string;
+ };
+ itemTitle = `${activity.author?.nickname}${activity.action}:${comment.article_title}`;
+ itemDesc = comment.comment ?? "";
+ itemUrl = `${ROOT_URL}/post/${comment.article_id}`;
+ break;
+ }
+ case "release_article": {
+ const article = contentData as { id?: number; summary?: string; title?: string };
+ itemTitle = `${activity.author?.nickname}${activity.action}:${article.title}`;
+ itemDesc = article.summary ?? "";
+ itemUrl = `${ROOT_URL}/post/${article.id}`;
+ break;
+ }
+ case "chosen_comment": {
+ const comment = contentData as { article_title?: string; comment?: string };
+ itemTitle = `${activity.author?.nickname} selected comment on ${comment.article_title}`;
+ itemDesc = comment.comment ?? "";
+ break;
+ }
+ default: {
+ itemTitle = `Unknown activity: ${activity.key}`;
+ itemDesc = JSON.stringify(contentData);
+ }
+ }
+
+ return {
+ date: date((activity.created_at ?? Date.now() / 1000) * 1000),
+ description: itemDesc,
+ link: itemUrl,
+ title: itemTitle,
+ } satisfies DataItem;
+ });
+
+ return {
+ description: `Activity updates of SSPAI user ${userNickname}.`,
+ item,
+ language: "zh-CN",
+ link,
+ title: `${userNickname} - SSPAI Activity`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ slug: t.String({ description: "User slug." }),
+ }),
+ },
+ ),
+ );
diff --git a/pkgs/rssbook/src/routers/feeds/multimedia/thepaper/index.test.ts b/pkgs/rssbook/src/routers/feeds/multimedia/thepaper/index.test.ts
new file mode 100644
index 0000000..46f239a
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/multimedia/thepaper/index.test.ts
@@ -0,0 +1,11 @@
+import { describe, expect, test } from "bun:test";
+import { getRouteData } from "@/utils/tests/source";
+import thepaper from ".";
+
+describe("The Paper", () => {
+ test("fetches featured articles", async () => {
+ const data = await getRouteData(thepaper, "/featured");
+
+ expect(data.item?.length).toBeGreaterThan(0);
+ }, 20_000);
+});
diff --git a/pkgs/rssbook/src/routers/feeds/multimedia/thepaper/index.ts b/pkgs/rssbook/src/routers/feeds/multimedia/thepaper/index.ts
new file mode 100644
index 0000000..7a1a18e
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/multimedia/thepaper/index.ts
@@ -0,0 +1,62 @@
+import type { Data, DataItem } from "@/types";
+import { Source } from "@/utils";
+
+type ThePaperItem = {
+ contId?: string | number;
+ name?: string;
+ pubTime?: string;
+ title?: string;
+};
+
+export default new Source({
+ description: "Front-page news feeds from The Paper (澎湃新闻).",
+ domain: "thepaper.cn",
+ slug: "thepaper",
+ title: "The Paper",
+}).feed(
+ {
+ description: "Fetch front-page featured articles from The Paper mobile site.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Featured Articles",
+ },
+ (app) =>
+ app.get("/featured", async ({ cache, date, load, ofetch }) => {
+ const link = "https://m.thepaper.cn";
+ const item = await cache.tryGet(link, async () => {
+ const html = await ofetch(link, { responseType: "text" });
+ const $ = load(html);
+ const raw = $("#__NEXT_DATA__").text();
+ const parsed = JSON.parse(raw) as {
+ props?: {
+ pageProps?: {
+ data?: { list?: ThePaperItem[] };
+ topData?: { recommendImg?: ThePaperItem[] };
+ };
+ };
+ };
+ const list = [
+ ...(parsed.props?.pageProps?.data?.list ?? []),
+ ...(parsed.props?.pageProps?.topData?.recommendImg ?? []),
+ ];
+ return list
+ .map(
+ (entry) =>
+ ({
+ date: date(entry.pubTime ?? new Date().toISOString()),
+ link: entry.contId ? `https://m.thepaper.cn/detail/${entry.contId}` : link,
+ title: entry.title || entry.name || "The Paper Article",
+ }) satisfies DataItem,
+ )
+ .slice(0, 20);
+ });
+
+ return {
+ description: "Featured articles from The Paper.",
+ item,
+ language: "zh-CN",
+ link,
+ title: "The Paper Featured Articles",
+ } satisfies Data;
+ }),
+);
diff --git a/pkgs/rssbook/src/routers/feeds/news/bbc/index.test.ts b/pkgs/rssbook/src/routers/feeds/news/bbc/index.test.ts
new file mode 100644
index 0000000..d517dbc
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/news/bbc/index.test.ts
@@ -0,0 +1,11 @@
+import { describe, expect, test } from "bun:test";
+import { getRouteData } from "@/utils/tests/source";
+import bbc from ".";
+
+describe("BBC", () => {
+ test("fetches news channel", async () => {
+ const data = await getRouteData(bbc, "/news/world");
+
+ expect(data.item?.length).toBeGreaterThan(0);
+ }, 20_000);
+});
diff --git a/pkgs/rssbook/src/routers/feeds/news/bbc/index.ts b/pkgs/rssbook/src/routers/feeds/news/bbc/index.ts
new file mode 100644
index 0000000..256105d
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/news/bbc/index.ts
@@ -0,0 +1,46 @@
+import type { Data } from "@/types";
+import { Source, t } from "@/utils";
+import { parse } from "@/utils/feeds";
+
+const CHANNELS: Record = {
+ home: "https://feeds.bbci.co.uk/news/rss.xml",
+ technology: "https://feeds.bbci.co.uk/news/technology/rss.xml",
+ world: "https://feeds.bbci.co.uk/news/world/rss.xml",
+};
+
+export default new Source({
+ description: "RSS feeds from BBC News.",
+ domain: "bbc.com",
+ slug: "bbc",
+ title: "BBC",
+}).feed(
+ {
+ description: "Fetch a BBC News RSS channel.",
+ language: "en",
+ maintainer: { name: "RSSBook" },
+ title: "News Channel",
+ },
+ (app) =>
+ app.get(
+ "/news/:channel",
+ async ({ cache, ofetch, params: { channel } }) => {
+ const feedUrl = CHANNELS[channel] ?? CHANNELS.home;
+ return await cache.tryGet(feedUrl, async () => {
+ const xml = await ofetch(feedUrl, { responseType: "text" });
+ return {
+ ...parse(xml),
+ link: `https://www.bbc.com/news${channel === "home" ? "" : `/${channel}`}`,
+ title: `BBC News - ${channel}`,
+ } satisfies Data;
+ });
+ },
+ {
+ params: t.Object({
+ channel: t.String({
+ default: "home",
+ description: "BBC channel, for example home, world, or technology.",
+ }),
+ }),
+ },
+ ),
+);
diff --git a/pkgs/rssbook/src/routers/feeds/news/index.ts b/pkgs/rssbook/src/routers/feeds/news/index.ts
index bdcd46e..1c7e894 100644
--- a/pkgs/rssbook/src/routers/feeds/news/index.ts
+++ b/pkgs/rssbook/src/routers/feeds/news/index.ts
@@ -1,6 +1,15 @@
import { Category } from "@/utils";
+import bbc from "./bbc";
+import reuters from "./reuters";
+import techcrunch from "./techcrunch";
+import theverge from "./theverge";
export default new Category(
"news",
"**Latest news** and current events from around the world.",
-).use({});
+).use({
+ bbc,
+ reuters,
+ techcrunch,
+ theverge,
+});
diff --git a/pkgs/rssbook/src/routers/feeds/news/reuters/index.test.ts b/pkgs/rssbook/src/routers/feeds/news/reuters/index.test.ts
new file mode 100644
index 0000000..d41978d
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/news/reuters/index.test.ts
@@ -0,0 +1,11 @@
+import { describe, expect, test } from "bun:test";
+import { getRouteData } from "@/utils/tests/source";
+import reuters from ".";
+
+describe("Reuters", () => {
+ test("fetches section articles", async () => {
+ const data = await getRouteData(reuters, "/world");
+
+ expect(data.item?.length).toBeGreaterThan(0);
+ }, 20_000);
+});
diff --git a/pkgs/rssbook/src/routers/feeds/news/reuters/index.ts b/pkgs/rssbook/src/routers/feeds/news/reuters/index.ts
new file mode 100644
index 0000000..0c46b46
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/news/reuters/index.ts
@@ -0,0 +1,45 @@
+import type { Data } from "@/types";
+import { Source, t } from "@/utils";
+import { parse } from "@/utils/feeds";
+
+export default new Source({
+ description: "HTML feeds from Reuters.",
+ domain: "reuters.com",
+ slug: "reuters",
+ title: "Reuters",
+}).feed(
+ {
+ description: "Fetch article cards from a Reuters section page.",
+ language: "en",
+ maintainer: { name: "RSSBook" },
+ title: "Section Articles",
+ },
+ (app) =>
+ app.get(
+ "/:section",
+ async ({ cache, ofetch, params: { section } }) => {
+ const link = `https://www.reuters.com/${section}/`;
+ const feedUrl = `https://news.google.com/rss/search?q=${encodeURIComponent(`site:reuters.com/${section} Reuters`)}&hl=en-US&gl=US&ceid=US:en`;
+ const data = await cache.tryGet(feedUrl, async () => {
+ const xml = await ofetch(feedUrl, { responseType: "text" });
+ return parse(xml) satisfies Data;
+ });
+
+ return {
+ description: `Reuters ${section} articles.`,
+ item: data.item,
+ language: "en",
+ link,
+ title: `Reuters - ${section}`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ section: t.String({
+ default: "world",
+ description: "Reuters section path, for example world or business.",
+ }),
+ }),
+ },
+ ),
+);
diff --git a/pkgs/rssbook/src/routers/feeds/news/techcrunch/index.test.ts b/pkgs/rssbook/src/routers/feeds/news/techcrunch/index.test.ts
new file mode 100644
index 0000000..abc98c6
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/news/techcrunch/index.test.ts
@@ -0,0 +1,17 @@
+import { describe, expect, test } from "bun:test";
+import { getRouteData } from "@/utils/tests/source";
+import techcrunch from ".";
+
+describe("TechCrunch", () => {
+ test("fetches latest news", async () => {
+ const data = await getRouteData(techcrunch, "/news");
+
+ expect(data.item?.length).toBeGreaterThan(0);
+ }, 20_000);
+
+ test("fetches category", async () => {
+ const data = await getRouteData(techcrunch, "/category/17396");
+
+ expect(data.title).toContain("TechCrunch Category");
+ }, 20_000);
+});
diff --git a/pkgs/rssbook/src/routers/feeds/news/techcrunch/index.ts b/pkgs/rssbook/src/routers/feeds/news/techcrunch/index.ts
new file mode 100644
index 0000000..7b9b484
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/news/techcrunch/index.ts
@@ -0,0 +1,96 @@
+import type { Data, DataItem } from "@/types";
+import { Source, t } from "@/utils";
+
+type TechCrunchPost = {
+ content?: { rendered?: string };
+ date?: string;
+ excerpt?: { rendered?: string };
+ link: string;
+ title?: { rendered?: string };
+};
+
+export default new Source({
+ description: "Public WordPress API feeds from TechCrunch.",
+ domain: "techcrunch.com",
+ slug: "techcrunch",
+ title: "TechCrunch",
+})
+ .feed(
+ {
+ description: "Fetch latest TechCrunch posts.",
+ fulltext: true,
+ language: "en",
+ maintainer: { name: "RSSBook" },
+ title: "Latest News",
+ },
+ (app) =>
+ app.get("/news", async ({ cache, date, formatHTML, load, ofetch }) => {
+ const apiUrl = "https://techcrunch.com/wp-json/wp/v2/posts?per_page=20";
+ const item = await cache.tryGet(apiUrl, async () => {
+ const posts = await ofetch(apiUrl, { responseType: "json" });
+ return posts.map((post) => toItem(post, { date, formatHTML, load }));
+ });
+
+ return {
+ description: "Latest posts from TechCrunch.",
+ item,
+ language: "en",
+ link: "https://techcrunch.com",
+ title: "TechCrunch News",
+ } satisfies Data;
+ }),
+ )
+ .feed(
+ {
+ description: "Fetch latest TechCrunch posts from a WordPress category ID.",
+ fulltext: true,
+ language: "en",
+ maintainer: { name: "RSSBook" },
+ title: "Category",
+ },
+ (app) =>
+ app.get(
+ "/category/:categoryId",
+ async ({ cache, date, formatHTML, load, ofetch, params: { categoryId } }) => {
+ const apiUrl = `https://techcrunch.com/wp-json/wp/v2/posts?per_page=20&categories=${categoryId}`;
+ const item = await cache.tryGet(apiUrl, async () => {
+ const posts = await ofetch(apiUrl, { responseType: "json" });
+ return posts.map((post) => toItem(post, { date, formatHTML, load }));
+ });
+
+ return {
+ description: `Latest TechCrunch posts from category ${categoryId}.`,
+ item,
+ language: "en",
+ link: "https://techcrunch.com",
+ title: `TechCrunch Category - ${categoryId}`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ categoryId: t.String({ description: "WordPress category ID." }),
+ }),
+ },
+ ),
+ );
+
+function toItem(
+ post: TechCrunchPost,
+ utils: {
+ date: (value: string) => Date;
+ formatHTML: (html: string, baseURL?: string) => string;
+ load: typeof import("cheerio").load;
+ },
+): DataItem {
+ return {
+ content: post.content?.rendered
+ ? utils.formatHTML(post.content.rendered, post.link)
+ : undefined,
+ date: utils.date(post.date ?? new Date().toISOString()),
+ description: post.excerpt?.rendered
+ ? utils.load(post.excerpt.rendered).text().trim()
+ : undefined,
+ link: post.link,
+ title: post.title?.rendered ? utils.load(post.title.rendered).text().trim() : post.link,
+ } satisfies DataItem;
+}
diff --git a/pkgs/rssbook/src/routers/feeds/news/theverge/index.test.ts b/pkgs/rssbook/src/routers/feeds/news/theverge/index.test.ts
new file mode 100644
index 0000000..b0a44b0
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/news/theverge/index.test.ts
@@ -0,0 +1,11 @@
+import { describe, expect, test } from "bun:test";
+import { getRouteData } from "@/utils/tests/source";
+import theverge from ".";
+
+describe("The Verge", () => {
+ test("fetches front page", async () => {
+ const data = await getRouteData(theverge, "/");
+
+ expect(data.item?.length).toBeGreaterThan(0);
+ }, 20_000);
+});
diff --git a/pkgs/rssbook/src/routers/feeds/news/theverge/index.ts b/pkgs/rssbook/src/routers/feeds/news/theverge/index.ts
new file mode 100644
index 0000000..976e8d3
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/news/theverge/index.ts
@@ -0,0 +1,30 @@
+import type { Data } from "@/types";
+import { Source } from "@/utils";
+import { parse } from "@/utils/feeds";
+
+export default new Source({
+ description: "RSS feeds from The Verge.",
+ domain: "theverge.com",
+ slug: "theverge",
+ title: "The Verge",
+}).feed(
+ {
+ description: "Fetch The Verge front page RSS feed.",
+ language: "en",
+ maintainer: { name: "RSSBook" },
+ title: "Front Page",
+ },
+ (app) =>
+ app.get("/", async ({ cache, ofetch }) => {
+ const feedUrl = "https://www.theverge.com/rss/index.xml";
+ return await cache.tryGet(feedUrl, async () => {
+ const xml = await ofetch(feedUrl, { responseType: "text" });
+ return {
+ ...parse(xml),
+ language: "en",
+ link: "https://www.theverge.com",
+ title: "The Verge",
+ } satisfies Data;
+ });
+ }),
+);
diff --git a/pkgs/rssbook/src/routers/feeds/programming/github/index.test.ts b/pkgs/rssbook/src/routers/feeds/programming/github/index.test.ts
new file mode 100644
index 0000000..41b044b
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/programming/github/index.test.ts
@@ -0,0 +1,94 @@
+import { describe, expect, test } from "bun:test";
+import type { Data } from "@/types";
+import { getRouteData } from "@/utils/tests/source";
+import github from ".";
+
+function expectData(data: Data) {
+ expect(data.title.length).toBeGreaterThan(0);
+ expect(data.link).toStartWith("https://");
+ expect(data.item?.length).toBeGreaterThan(0);
+}
+
+describe("GitHub", () => {
+ test("fetches user events", async () => {
+ const data = await getRouteData(github, "/events/torvalds");
+
+ expectData(data);
+ }, 20_000);
+
+ test("fetches repository releases", async () => {
+ const data = await getRouteData(github, "/repo/nodejs/node/releases");
+
+ expectData(data);
+ }, 20_000);
+
+ test("accepts optional token config", async () => {
+ const data = await getRouteData(github, "/events/torvalds", {
+ config: {
+ GITHUB_TOKEN: "",
+ },
+ });
+
+ expect(data.title).toContain("GitHub Events");
+ }, 20_000);
+
+ test("fetches user repositories", async () => {
+ const data = await getRouteData(github, "/repos/torvalds");
+
+ expectData(data);
+ }, 20_000);
+
+ test("fetches repository issues", async () => {
+ const data = await getRouteData(github, "/repo/nodejs/node/issues");
+
+ expectData(data);
+ }, 20_000);
+
+ test("fetches repository pull requests", async () => {
+ const data = await getRouteData(github, "/repo/nodejs/node/pulls");
+
+ expectData(data);
+ }, 20_000);
+
+ test("fetches repository commits", async () => {
+ const data = await getRouteData(github, "/repo/nodejs/node/commits");
+
+ expectData(data);
+ }, 20_000);
+
+ test("fetches repository tags", async () => {
+ const data = await getRouteData(github, "/repo/nodejs/node/tags");
+
+ expectData(data);
+ }, 20_000);
+
+ test("fetches repository wiki", async () => {
+ const data = await getRouteData(github, "/repo/microsoft/vscode/wiki");
+
+ expectData(data);
+ }, 20_000);
+
+ test("searches repositories", async () => {
+ const data = await getRouteData(github, "/search/RSSHub");
+
+ expectData(data);
+ }, 20_000);
+
+ test("fetches trending repositories", async () => {
+ const data = await getRouteData(github, "/trending/daily/typescript");
+
+ expectData(data);
+ }, 20_000);
+
+ test("fetches topic repositories", async () => {
+ const data = await getRouteData(github, "/topics/framework");
+
+ expectData(data);
+ }, 20_000);
+
+ test("fetches user activity", async () => {
+ const data = await getRouteData(github, "/activity/torvalds");
+
+ expectData(data);
+ }, 20_000);
+});
diff --git a/pkgs/rssbook/src/routers/feeds/programming/github/index.ts b/pkgs/rssbook/src/routers/feeds/programming/github/index.ts
new file mode 100644
index 0000000..ad620b4
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/programming/github/index.ts
@@ -0,0 +1,981 @@
+import type { Data, DataItem } from "@/types";
+import { load, Source, t } from "@/utils";
+import { parse } from "@/utils/feeds";
+
+type GitHubEvent = {
+ actor?: { display_login?: string };
+ created_at?: string;
+ id: string;
+ payload?: { action?: string };
+ repo?: { name?: string; url?: string };
+ type: string;
+};
+
+type GitHubRelease = {
+ body?: string;
+ html_url: string;
+ name?: string;
+ published_at?: string;
+ tag_name: string;
+};
+
+type GitHubSearchRepo = {
+ hl_name: string;
+ hl_trunc_description?: string;
+ public: boolean;
+ repo: {
+ repository: {
+ name: string;
+ owner_login: string;
+ updated_at: string;
+ };
+ };
+};
+
+type GitHubSearchIssue = {
+ author_name: string;
+ created: string;
+ hl_text?: string;
+ hl_title: string;
+ labels?: string[];
+ number: number;
+ repo: {
+ repository: {
+ name: string;
+ owner_login: string;
+ };
+ };
+};
+
+function stripHtmlTags(html: string) {
+ const $ = load(`${html}
`);
+ return $.text().trim();
+}
+
+export default new Source({
+ config: {
+ GITHUB_TOKEN: {
+ description: "Optional GitHub access token used to increase API rate limits.",
+ required: false,
+ },
+ },
+ description: "Public events, repositories, issues, and release feeds from GitHub.",
+ domain: "github.com",
+ slug: "github",
+ title: "GitHub",
+})
+ .feed(
+ {
+ description: "Fetch public activity events for a GitHub user.",
+ language: "en",
+ maintainer: { name: "RSSBook" },
+ title: "User Events",
+ },
+ (app) =>
+ app.get(
+ "/events/:username",
+ async ({ cache, date, meta: { config }, ofetch, params: { username } }) => {
+ if (!config.GITHUB_TOKEN) {
+ const feedUrl = `https://github.com/${username}.atom`;
+ const data = await cache.tryGet(feedUrl, async () => {
+ const xml = await ofetch(feedUrl, { responseType: "text" });
+ return parse(xml) satisfies Data;
+ });
+
+ return {
+ ...data,
+ description: `Public GitHub events for ${username}.`,
+ language: "en",
+ link: `https://github.com/${username}`,
+ title: `GitHub Events - ${username}`,
+ } satisfies Data;
+ }
+
+ const apiUrl = `https://api.github.com/users/${username}/events`;
+ const headers = githubHeaders(config.GITHUB_TOKEN);
+ const item = await cache.tryGet(apiUrl, async () => {
+ const events = await ofetch(apiUrl, { headers, responseType: "json" });
+ return events.map(
+ (event) =>
+ ({
+ author: [{ name: event.actor?.display_login }],
+ date: date(event.created_at ?? new Date().toISOString()),
+ id: event.id,
+ link: event.repo?.name
+ ? `https://github.com/${event.repo.name}`
+ : "https://github.com",
+ title: `${event.type}${event.payload?.action ? ` ${event.payload.action}` : ""} - ${event.repo?.name ?? username}`,
+ }) satisfies DataItem,
+ );
+ });
+
+ return {
+ description: `Public GitHub events for ${username}.`,
+ item,
+ language: "en",
+ link: `https://github.com/${username}`,
+ title: `GitHub Events - ${username}`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ username: t.String({ description: "GitHub username." }),
+ }),
+ },
+ ),
+ )
+ .feed(
+ {
+ description: "Fetch releases from a GitHub repository.",
+ fulltext: true,
+ language: "en",
+ maintainer: { name: "RSSBook" },
+ title: "Repository Releases",
+ },
+ (app) =>
+ app.get(
+ "/repo/:owner/:repo/releases",
+ async ({ cache, date, meta: { config }, ofetch, params: { owner, repo } }) => {
+ if (!config.GITHUB_TOKEN) {
+ const feedUrl = `https://github.com/${owner}/${repo}/releases.atom`;
+ const data = await cache.tryGet(feedUrl, async () => {
+ const xml = await ofetch(feedUrl, { responseType: "text" });
+ return parse(xml) satisfies Data;
+ });
+
+ return {
+ ...data,
+ description: `GitHub releases for ${owner}/${repo}.`,
+ language: "en",
+ link: `https://github.com/${owner}/${repo}/releases`,
+ title: `GitHub Releases - ${owner}/${repo}`,
+ } satisfies Data;
+ }
+
+ const apiUrl = `https://api.github.com/repos/${owner}/${repo}/releases`;
+ const headers = githubHeaders(config.GITHUB_TOKEN);
+ const item = await cache.tryGet(apiUrl, async () => {
+ const releases = await ofetch(apiUrl, {
+ headers,
+ responseType: "json",
+ });
+ return releases.map(
+ (release) =>
+ ({
+ date: date(release.published_at ?? new Date().toISOString()),
+ description: release.body,
+ link: release.html_url,
+ title: release.name || release.tag_name,
+ }) satisfies DataItem,
+ );
+ });
+
+ return {
+ description: `GitHub releases for ${owner}/${repo}.`,
+ item,
+ language: "en",
+ link: `https://github.com/${owner}/${repo}/releases`,
+ title: `GitHub Releases - ${owner}/${repo}`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ owner: t.String({ description: "Repository owner." }),
+ repo: t.String({ description: "Repository name." }),
+ }),
+ },
+ ),
+ )
+ .feed(
+ {
+ description: "Fetch repositories owned by a GitHub user.",
+ language: "en",
+ maintainer: { name: "RSSBook" },
+ title: "User Repositories",
+ },
+ (app) =>
+ app.get(
+ "/repos/:user/:type?/:sort?",
+ async ({ cache, date, meta: { config }, ofetch, params: { user, type, sort } }) => {
+ const repoType = type || "all";
+ const repoSort = sort || "created";
+
+ if (config.GITHUB_TOKEN) {
+ const apiUrl = `https://api.github.com/users/${user}/repos`;
+ const headers = githubHeaders(config.GITHUB_TOKEN);
+ const item = await cache.tryGet(apiUrl, async () => {
+ const repos = await ofetch<
+ Array<{
+ created_at: string;
+ description: string | null;
+ fork: boolean;
+ html_url: string;
+ name: string;
+ owner: { login: string };
+ private: boolean;
+ pushed_at?: string;
+ updated_at?: string;
+ }>
+ >(apiUrl, {
+ headers,
+ query: {
+ per_page: "100",
+ sort: repoSort,
+ type: repoType,
+ },
+ responseType: "json",
+ });
+
+ return repos
+ .filter((item) => filterRepoByType(item, repoType, user))
+ .map((item) => {
+ const updated = item.pushed_at || item.updated_at || item.created_at;
+ return {
+ author: [{ name: item.owner.login }],
+ date: date(updated || new Date().toISOString()),
+ description: item.description || undefined,
+ link: item.html_url,
+ title: item.name,
+ } satisfies DataItem;
+ });
+ });
+
+ return {
+ description: `GitHub repositories for ${user}.`,
+ item,
+ language: "en",
+ link: `https://github.com/${user}`,
+ title: `GitHub Repositories - ${user}`,
+ } satisfies Data;
+ }
+
+ const pageUrl = `https://github.com/${user}?tab=repositories`;
+ const item = await cache.tryGet(pageUrl, async () => {
+ const html = await ofetch(pageUrl, { responseType: "text" });
+ const $ = load(html);
+
+ return $("li[itemtype='http://schema.org/Code']")
+ .toArray()
+ .map((el) => {
+ const $el = $(el);
+ const $repo = $el.find('a[itemprop="name codeRepository"]').first();
+ const href = $repo.attr("href") || "";
+ const name = $repo.text().trim();
+ const description = $el.find('p[itemprop="description"]').first().text().trim();
+ const updated = $el.find("relative-time").first().attr("datetime");
+ const ownerMatch = href.match(/^\/([^/]+)\/([^/]+)/);
+ const owner = ownerMatch?.[1] ?? user;
+ const isPublic = $el.hasClass("public");
+ const isPrivate = $el.hasClass("private");
+ const isFork = $el.hasClass("fork");
+ const isSource = $el.hasClass("source");
+
+ return {
+ flags: { isFork, isPrivate, isPublic, isSource, owner },
+ item: {
+ author: [{ name: owner }],
+ date: updated ? date(updated) : new Date(),
+ description: description || undefined,
+ link: href ? `https://github.com${href}` : pageUrl,
+ title: name || href,
+ } satisfies DataItem,
+ };
+ })
+ .filter((entry) => filterHtmlRepoByType(entry.flags, repoType, user))
+ .map((entry) => entry.item);
+ });
+
+ return {
+ description: `GitHub repositories for ${user}.`,
+ item,
+ language: "en",
+ link: `https://github.com/${user}`,
+ title: `GitHub Repositories - ${user}`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ sort: t.Optional(
+ t.String({
+ description: "Sort by created, updated, pushed, full_name, stars, or forks.",
+ }),
+ ),
+ type: t.Optional(
+ t.String({
+ description:
+ "Repository type: all, owner, member, public, private, forks, or sources.",
+ }),
+ ),
+ user: t.String({ description: "GitHub username." }),
+ }),
+ },
+ ),
+ )
+ .feed(
+ {
+ description: "Fetch issues from a GitHub repository.",
+ fulltext: true,
+ language: "en",
+ maintainer: { name: "RSSBook" },
+ title: "Repository Issues",
+ },
+ (app) =>
+ app.get(
+ "/repo/:owner/:repo/issues/:state?/:labels?",
+ async ({
+ cache,
+ date,
+ meta: { config },
+ ofetch,
+ params: { labels, owner, repo, state },
+ }) => {
+ const issueState = state || "open";
+ const labelList = labels ? labels.split(",") : [];
+
+ if (config.GITHUB_TOKEN) {
+ const apiUrl = `https://api.github.com/repos/${owner}/${repo}/issues`;
+ const headers = {
+ Accept: "application/vnd.github.v3+json",
+ ...githubHeaders(config.GITHUB_TOKEN),
+ };
+ const item = await cache.tryGet(apiUrl, async () => {
+ const issues = await ofetch<
+ Array<{
+ body?: string;
+ created_at: string;
+ html_url: string;
+ number: number;
+ title: string;
+ user: { login: string };
+ }>
+ >(apiUrl, {
+ headers,
+ query: {
+ direction: "desc",
+ labels: labels,
+ per_page: "100",
+ sort: "created",
+ state: issueState,
+ },
+ responseType: "json",
+ });
+
+ return issues
+ .filter((issue) => !issue.html_url.includes("/pull/"))
+ .map((issue) => ({
+ author: [{ name: issue.user.login }],
+ date: date(issue.created_at),
+ description: issue.body,
+ link: issue.html_url,
+ title: issue.title,
+ })) satisfies DataItem[];
+ });
+
+ return {
+ description: `GitHub issues for ${owner}/${repo}.`,
+ item,
+ language: "en",
+ link: `https://github.com/${owner}/${repo}/issues`,
+ title: `GitHub Issues - ${owner}/${repo}`,
+ } satisfies Data;
+ }
+
+ const queryParts = [
+ `repo:${owner}/${repo}`,
+ "is:issue",
+ `is:${issueState}`,
+ ...labelList.map((label) => `label:${label}`),
+ ];
+ const searchUrl = `https://github.com/search?type=issues&q=${encodeURIComponent(queryParts.join(" "))}&s=created&o=desc`;
+ const item = await cache.tryGet(searchUrl, async () => {
+ const response = await ofetch<{ payload: { results: GitHubSearchIssue[] } }>(
+ searchUrl,
+ {
+ headers: { accept: "application/json" },
+ responseType: "json",
+ },
+ );
+
+ return response.payload.results.map((issue) => {
+ const repository = issue.repo.repository;
+ return {
+ author: [{ name: issue.author_name }],
+ category: issue.labels?.map((label) => ({ name: label })),
+ date: date(issue.created),
+ description: issue.hl_text,
+ link: `https://github.com/${repository.owner_login}/${repository.name}/issues/${issue.number}`,
+ title: stripHtmlTags(issue.hl_title),
+ } satisfies DataItem;
+ });
+ });
+
+ return {
+ description: `GitHub issues for ${owner}/${repo}.`,
+ item,
+ language: "en",
+ link: `https://github.com/${owner}/${repo}/issues`,
+ title: `GitHub Issues - ${owner}/${repo}`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ labels: t.Optional(t.String({ description: "Comma-separated list of label names." })),
+ owner: t.String({ description: "Repository owner." }),
+ repo: t.String({ description: "Repository name." }),
+ state: t.Optional(
+ t.String({
+ default: "open",
+ description: "Issue state: open, closed, or all.",
+ }),
+ ),
+ }),
+ },
+ ),
+ )
+ .feed(
+ {
+ description: "Fetch pull requests from a GitHub repository.",
+ fulltext: true,
+ language: "en",
+ maintainer: { name: "RSSBook" },
+ title: "Repository Pull Requests",
+ },
+ (app) =>
+ app.get(
+ "/repo/:owner/:repo/pulls/:state?/:labels?",
+ async ({
+ cache,
+ date,
+ meta: { config },
+ ofetch,
+ params: { labels, owner, repo, state },
+ }) => {
+ const prState = state || "open";
+ const labelList = labels ? labels.split(",") : [];
+
+ if (config.GITHUB_TOKEN) {
+ const apiUrl = `https://api.github.com/repos/${owner}/${repo}/issues`;
+ const headers = {
+ Accept: "application/vnd.github.v3+json",
+ ...githubHeaders(config.GITHUB_TOKEN),
+ };
+ const item = await cache.tryGet(apiUrl, async () => {
+ const pulls = await ofetch<
+ Array<{
+ body?: string;
+ created_at: string;
+ html_url: string;
+ number: number;
+ pull_request?: unknown;
+ title: string;
+ user: { login: string };
+ }>
+ >(apiUrl, {
+ headers,
+ query: {
+ direction: "desc",
+ labels: labels,
+ per_page: "100",
+ sort: "created",
+ state: prState,
+ },
+ responseType: "json",
+ });
+
+ return pulls
+ .filter((issue) => issue.pull_request)
+ .map((pull) => ({
+ author: [{ name: pull.user.login }],
+ date: date(pull.created_at),
+ description: pull.body,
+ link: pull.html_url,
+ title: pull.title,
+ })) satisfies DataItem[];
+ });
+
+ return {
+ description: `GitHub pull requests for ${owner}/${repo}.`,
+ item,
+ language: "en",
+ link: `https://github.com/${owner}/${repo}/pulls`,
+ title: `GitHub Pull Requests - ${owner}/${repo}`,
+ } satisfies Data;
+ }
+
+ const queryParts = [
+ `repo:${owner}/${repo}`,
+ "is:pr",
+ `is:${prState}`,
+ ...labelList.map((label) => `label:${label}`),
+ ];
+ const searchUrl = `https://github.com/search?type=pullrequests&q=${encodeURIComponent(queryParts.join(" "))}&s=created&o=desc`;
+ const item = await cache.tryGet(searchUrl, async () => {
+ const response = await ofetch<{ payload: { results: GitHubSearchIssue[] } }>(
+ searchUrl,
+ {
+ headers: { accept: "application/json" },
+ responseType: "json",
+ },
+ );
+
+ return response.payload.results.map((pull) => {
+ const repository = pull.repo.repository;
+ return {
+ author: [{ name: pull.author_name }],
+ category: pull.labels?.map((label) => ({ name: label })),
+ date: date(pull.created),
+ description: pull.hl_text,
+ link: `https://github.com/${repository.owner_login}/${repository.name}/pull/${pull.number}`,
+ title: stripHtmlTags(pull.hl_title),
+ } satisfies DataItem;
+ });
+ });
+
+ return {
+ description: `GitHub pull requests for ${owner}/${repo}.`,
+ item,
+ language: "en",
+ link: `https://github.com/${owner}/${repo}/pulls`,
+ title: `GitHub Pull Requests - ${owner}/${repo}`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ labels: t.Optional(t.String({ description: "Comma-separated list of label names." })),
+ owner: t.String({ description: "Repository owner." }),
+ repo: t.String({ description: "Repository name." }),
+ state: t.Optional(
+ t.String({
+ default: "open",
+ description: "Pull request state: open, closed, or all.",
+ }),
+ ),
+ }),
+ },
+ ),
+ )
+ .feed(
+ {
+ description: "Fetch commits from a GitHub repository.",
+ language: "en",
+ maintainer: { name: "RSSBook" },
+ title: "Repository Commits",
+ },
+ (app) =>
+ app.get(
+ "/repo/:owner/:repo/commits",
+ async ({ cache, date, meta: { config }, ofetch, params: { owner, repo } }) => {
+ if (!config.GITHUB_TOKEN) {
+ const feedUrl = `https://github.com/${owner}/${repo}/commits.atom`;
+ const data = await cache.tryGet(feedUrl, async () => {
+ const xml = await ofetch(feedUrl, { responseType: "text" });
+ return parse(xml) satisfies Data;
+ });
+
+ return {
+ ...data,
+ description: `GitHub commits for ${owner}/${repo}.`,
+ language: "en",
+ link: `https://github.com/${owner}/${repo}/commits`,
+ title: `GitHub Commits - ${owner}/${repo}`,
+ } satisfies Data;
+ }
+
+ const apiUrl = `https://api.github.com/repos/${owner}/${repo}/commits`;
+ const headers = githubHeaders(config.GITHUB_TOKEN);
+ const item = await cache.tryGet(apiUrl, async () => {
+ const commits = await ofetch<
+ Array<{
+ commit: { author: { date: string; name: string }; message: string };
+ html_url: string;
+ sha: string;
+ }>
+ >(apiUrl, {
+ headers,
+ query: { per_page: "100" },
+ responseType: "json",
+ });
+
+ return commits.map((commit) => ({
+ author: [{ name: commit.commit.author.name }],
+ date: date(commit.commit.author.date),
+ description: commit.commit.message,
+ link: commit.html_url,
+ title: commit.commit.message.split("\n")[0],
+ })) satisfies DataItem[];
+ });
+
+ return {
+ description: `GitHub commits for ${owner}/${repo}.`,
+ item,
+ language: "en",
+ link: `https://github.com/${owner}/${repo}/commits`,
+ title: `GitHub Commits - ${owner}/${repo}`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ owner: t.String({ description: "Repository owner." }),
+ repo: t.String({ description: "Repository name." }),
+ }),
+ },
+ ),
+ )
+ .feed(
+ {
+ description: "Fetch tags from a GitHub repository.",
+ language: "en",
+ maintainer: { name: "RSSBook" },
+ title: "Repository Tags",
+ },
+ (app) =>
+ app.get(
+ "/repo/:owner/:repo/tags",
+ async ({ cache, ofetch, params: { owner, repo } }) => {
+ const feedUrl = `https://github.com/${owner}/${repo}/tags.atom`;
+ const data = await cache.tryGet(feedUrl, async () => {
+ const xml = await ofetch(feedUrl, { responseType: "text" });
+ return parse(xml) satisfies Data;
+ });
+
+ return {
+ ...data,
+ description: `GitHub tags for ${owner}/${repo}.`,
+ language: "en",
+ link: `https://github.com/${owner}/${repo}/tags`,
+ title: `GitHub Tags - ${owner}/${repo}`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ owner: t.String({ description: "Repository owner." }),
+ repo: t.String({ description: "Repository name." }),
+ }),
+ },
+ ),
+ )
+ .feed(
+ {
+ description: "Fetch wiki history from a GitHub repository.",
+ language: "en",
+ maintainer: { name: "RSSBook" },
+ title: "Repository Wiki",
+ },
+ (app) =>
+ app.get(
+ "/repo/:owner/:repo/wiki/:page?",
+ async ({ cache, ofetch, params: { owner, repo }, toAbsoluteURL }) => {
+ const feedUrl = `https://github.com/${owner}/${repo}/wiki.atom`;
+ const data = await cache.tryGet(feedUrl, async () => {
+ const xml = await ofetch(feedUrl, { responseType: "text" });
+ return parse(xml) satisfies Data;
+ });
+
+ return {
+ ...data,
+ description: `GitHub wiki for ${owner}/${repo}.`,
+ item: data.item?.map((item) => ({
+ ...item,
+ link: toAbsoluteURL(item.link, `https://github.com/${owner}/${repo}`),
+ })),
+ language: "en",
+ link: `https://github.com/${owner}/${repo}/wiki`,
+ title: `GitHub Wiki - ${owner}/${repo}`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ owner: t.String({ description: "Repository owner." }),
+ page: t.Optional(
+ t.String({ description: "Wiki page slug (ignored; all pages are returned)." }),
+ ),
+ repo: t.String({ description: "Repository name." }),
+ }),
+ },
+ ),
+ )
+ .feed(
+ {
+ description: "Search repositories on GitHub.",
+ language: "en",
+ maintainer: { name: "RSSBook" },
+ title: "Search Results",
+ },
+ (app) =>
+ app.get(
+ "/search/:query/:sort?/:order?",
+ async ({ cache, date, ofetch, params: { order, query, sort } }) => {
+ const searchSort = sort === "bestmatch" || !sort ? "" : sort;
+ const searchOrder = order || "desc";
+ const searchUrl = `https://github.com/search?type=repositories&q=${encodeURIComponent(query)}&s=${searchSort}&o=${searchOrder}`;
+ const item = await cache.tryGet(searchUrl, async () => {
+ const response = await ofetch<{ payload: { results: GitHubSearchRepo[] } }>(searchUrl, {
+ headers: { accept: "application/json" },
+ responseType: "json",
+ });
+
+ return response.payload.results.map((repo) => {
+ const repository = repo.repo.repository;
+ return {
+ author: [{ name: repository.owner_login }],
+ date: date(repository.updated_at),
+ description: repo.hl_trunc_description,
+ link: `https://github.com/${repository.owner_login}/${repository.name}`,
+ title: stripHtmlTags(repo.hl_name),
+ } satisfies DataItem;
+ });
+ });
+
+ return {
+ description: `GitHub search results for "${query}".`,
+ item,
+ language: "en",
+ link: `https://github.com/search?q=${encodeURIComponent(query)}&type=repositories`,
+ title: `GitHub Search - ${query}`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ order: t.Optional(
+ t.String({
+ default: "desc",
+ description: "Sort order: asc or desc.",
+ }),
+ ),
+ query: t.String({ description: "Search query." }),
+ sort: t.Optional(
+ t.String({
+ default: "bestmatch",
+ description: "Sort by bestmatch, stars, forks, or updated.",
+ }),
+ ),
+ }),
+ },
+ ),
+ )
+ .feed(
+ {
+ description: "Fetch trending repositories from GitHub.",
+ language: "en",
+ maintainer: { name: "RSSBook" },
+ title: "Trending Repositories",
+ },
+ (app) =>
+ app.get(
+ "/trending/:since/:language?/:spoken?",
+ async ({ cache, formatHTML, ofetch, params: { language, since, spoken } }) => {
+ const trendingLanguage = language && language !== "any" ? language : "";
+ const spokenLanguage = spoken || "";
+ const url = `https://github.com/trending/${encodeURIComponent(trendingLanguage)}?since=${since}&spoken_language_code=${spokenLanguage}`;
+
+ const item = await cache.tryGet(url, async () => {
+ const html = await ofetch(url, { responseType: "text" });
+ const $ = load(html);
+
+ return $("article.Box-row")
+ .toArray()
+ .map((el) => {
+ const $el = $(el);
+ const $link = $el.find("h2 a").last();
+ const href = $link.attr("href");
+ const title = $link.text().trim().replace(/\s+/g, " ");
+ const description = $el.find("p.color-fg-muted").first().html();
+
+ return {
+ date: new Date(),
+ description: description ? formatHTML(description, url) : undefined,
+ link: href ? `https://github.com${href}` : "https://github.com/trending",
+ title,
+ } satisfies DataItem;
+ });
+ });
+
+ return {
+ description: `GitHub trending repositories (${since}).`,
+ item,
+ language: "en",
+ link: url,
+ title: `GitHub Trending - ${since}${trendingLanguage ? ` / ${trendingLanguage}` : ""}`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ language: t.Optional(
+ t.String({
+ default: "any",
+ description: "Programming language filter. Use 'any' for all languages.",
+ }),
+ ),
+ since: t.String({
+ description: "Time range: daily, weekly, or monthly.",
+ }),
+ spoken: t.Optional(
+ t.String({
+ description: "Spoken language code.",
+ }),
+ ),
+ }),
+ },
+ ),
+ )
+ .feed(
+ {
+ description: "Fetch repositories for a GitHub topic.",
+ language: "en",
+ maintainer: { name: "RSSBook" },
+ title: "Topic Repositories",
+ },
+ (app) =>
+ app.get(
+ "/topics/:name/:qs?",
+ async ({ cache, date, formatHTML, load, ofetch, params: { name, qs } }) => {
+ const url = new URL(`https://github.com/topics/${name}`);
+ if (qs) {
+ const params = new URLSearchParams(qs);
+ for (const [key, value] of params) {
+ url.searchParams.set(key, value);
+ }
+ }
+
+ const item = await cache.tryGet(url.href, async () => {
+ const html = await ofetch(url.href, { responseType: "text" });
+ const $ = load(html);
+
+ return $("article.border")
+ .toArray()
+ .map((el) => {
+ const $el = $(el);
+ const $repoLink = $el.find("h3 a").last();
+ const href = $repoLink.attr("href");
+ const title = $repoLink.text().trim().replace(/\s+/g, " ");
+ const $owner = $el.find("h3 a").first();
+ const description = $el.find("p.color-fg-muted").first().html();
+ const updated = $el.find("relative-time").attr("datetime");
+ const categories = $el
+ .find(".topic-tag")
+ .toArray()
+ .map((tag) => ({ name: $(tag).text().trim() }));
+
+ return {
+ author: [{ name: $owner.text().trim() }],
+ category: categories.length ? categories : undefined,
+ date: updated ? date(updated) : new Date(),
+ description: description ? formatHTML(description, url.href) : undefined,
+ link: href ? `https://github.com${href}` : `https://github.com/topics/${name}`,
+ title,
+ } satisfies DataItem;
+ });
+ });
+
+ return {
+ description: `GitHub topic repositories for ${name}.`,
+ item,
+ language: "en",
+ link: url.href,
+ title: `GitHub Topic - ${name}`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ name: t.String({ description: "Topic name." }),
+ qs: t.Optional(
+ t.String({
+ description: "Query string like l=php&o=desc&s=stars.",
+ }),
+ ),
+ }),
+ },
+ ),
+ )
+ .feed(
+ {
+ description: "Fetch public activity for a GitHub user from the official Atom feed.",
+ language: "en",
+ maintainer: { name: "RSSBook" },
+ title: "User Activity",
+ },
+ (app) =>
+ app.get(
+ "/activity/:user",
+ async ({ cache, ofetch, params: { user } }) => {
+ const feedUrl = `https://github.com/${user}.atom`;
+ const data = await cache.tryGet(feedUrl, async () => {
+ const xml = await ofetch(feedUrl, { responseType: "text" });
+ return parse(xml) satisfies Data;
+ });
+
+ return {
+ ...data,
+ description: `GitHub activity for ${user}.`,
+ language: "en",
+ link: `https://github.com/${user}`,
+ title: `GitHub Activity - ${user}`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ user: t.String({ description: "GitHub username." }),
+ }),
+ },
+ ),
+ );
+
+function githubHeaders(token?: string) {
+ return token ? { Authorization: `Bearer ${token}` } : undefined;
+}
+
+function filterRepoByType(
+ item: { fork: boolean; owner: { login: string }; private: boolean },
+ type: string,
+ user: string,
+) {
+ switch (type) {
+ case "all":
+ return true;
+ case "owner":
+ return item.owner.login === user;
+ case "member":
+ return item.owner.login !== user;
+ case "public":
+ return !item.private;
+ case "private":
+ return item.private;
+ case "forks":
+ return item.fork;
+ case "sources":
+ return !item.fork;
+ default:
+ return true;
+ }
+}
+
+function filterHtmlRepoByType(
+ flags: {
+ isFork: boolean;
+ isPrivate: boolean;
+ isPublic: boolean;
+ isSource: boolean;
+ owner: string;
+ },
+ type: string,
+ user: string,
+) {
+ switch (type) {
+ case "all":
+ return true;
+ case "owner":
+ return flags.owner === user && !flags.isFork;
+ case "member":
+ return flags.owner !== user;
+ case "public":
+ return flags.isPublic;
+ case "private":
+ return flags.isPrivate;
+ case "forks":
+ return flags.isFork;
+ case "sources":
+ return flags.isSource;
+ default:
+ return true;
+ }
+}
diff --git a/pkgs/rssbook/src/routers/feeds/programming/hackernews/index.test.ts b/pkgs/rssbook/src/routers/feeds/programming/hackernews/index.test.ts
new file mode 100644
index 0000000..8261306
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/programming/hackernews/index.test.ts
@@ -0,0 +1,24 @@
+import { describe, expect, test } from "bun:test";
+import type { Data } from "@/types";
+import { getRouteData } from "@/utils/tests/source";
+import hackernews from ".";
+
+function expectData(data: Data) {
+ expect(data.title.length).toBeGreaterThan(0);
+ expect(data.link).toStartWith("https://");
+ expect(data.item?.length).toBeGreaterThan(0);
+}
+
+describe("Hacker News", () => {
+ test("fetches news stories", async () => {
+ const data = await getRouteData(hackernews, "/news");
+
+ expectData(data);
+ }, 20_000);
+
+ test("fetches user submissions", async () => {
+ const data = await getRouteData(hackernews, "/submitted/clmul");
+
+ expectData(data);
+ }, 20_000);
+});
diff --git a/pkgs/rssbook/src/routers/feeds/programming/hackernews/index.ts b/pkgs/rssbook/src/routers/feeds/programming/hackernews/index.ts
new file mode 100644
index 0000000..465f167
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/programming/hackernews/index.ts
@@ -0,0 +1,134 @@
+import type { Data, DataItem } from "@/types";
+import { Source, t } from "@/utils";
+
+const SECTION_URLS: Record = {
+ best: "beststories",
+ newest: "newstories",
+ news: "topstories",
+};
+
+type HackerNewsItem = {
+ by?: string;
+ id: number;
+ score?: number;
+ text?: string;
+ time?: number;
+ title?: string;
+ url?: string;
+};
+
+type HackerNewsUser = {
+ about?: string;
+ created?: number;
+ karma?: number;
+ submitted?: number[];
+};
+
+export default new Source({
+ description: "Stories and submissions from Hacker News.",
+ domain: "news.ycombinator.com",
+ slug: "hackernews",
+ title: "Hacker News",
+})
+ .feed(
+ {
+ description: "Fetch stories submitted by a Hacker News user.",
+ language: "en",
+ maintainer: { name: "RSSBook" },
+ title: "User Submissions",
+ },
+ (app) =>
+ app.get(
+ "/submitted/:user",
+ async ({ cache, date, ofetch, params: { user } }) => {
+ const userUrl = `https://hacker-news.firebaseio.com/v0/user/${user}.json`;
+ const link = `https://news.ycombinator.com/submitted?id=${user}`;
+
+ const item = await cache.tryGet(userUrl, async () => {
+ const profile = await ofetch(userUrl, { responseType: "json" });
+ const ids = profile.submitted?.slice(0, 30) ?? [];
+ const stories = await Promise.all(
+ ids.map((id) =>
+ ofetch(`https://hacker-news.firebaseio.com/v0/item/${id}.json`, {
+ responseType: "json",
+ }),
+ ),
+ );
+
+ return stories
+ .filter((story): story is HackerNewsItem & { title: string } => Boolean(story?.title))
+ .map((story) => ({
+ author: story.by ? [{ name: story.by }] : undefined,
+ date: date((story.time ?? Date.now() / 1000) * 1000),
+ description: story.text,
+ link: story.url ?? `https://news.ycombinator.com/item?id=${story.id}`,
+ title: story.title,
+ })) satisfies DataItem[];
+ });
+
+ return {
+ description: `Hacker News submissions by ${user}.`,
+ item,
+ language: "en",
+ link,
+ title: `Hacker News Submissions - ${user}`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ user: t.String({ description: "Hacker News username." }),
+ }),
+ },
+ ),
+ )
+ .feed(
+ {
+ description: "Fetch stories from a Hacker News section.",
+ language: "en",
+ maintainer: { name: "RSSBook" },
+ title: "Section Stories",
+ },
+ (app) =>
+ app.get(
+ "/:section",
+ async ({ cache, date, ofetch, params: { section } }) => {
+ const storyType = SECTION_URLS[section] ?? SECTION_URLS.news;
+ const apiUrl = `https://hacker-news.firebaseio.com/v0/${storyType}.json`;
+ const link = `https://news.ycombinator.com/${section === "news" ? "news" : section}`;
+ const item = await cache.tryGet(apiUrl, async () => {
+ const storyIds = await ofetch(apiUrl, { responseType: "json" });
+ const stories = await Promise.all(
+ storyIds.slice(0, 20).map((id) =>
+ ofetch(`https://hacker-news.firebaseio.com/v0/item/${id}.json`, {
+ responseType: "json",
+ }),
+ ),
+ );
+
+ return stories.map(
+ (story) =>
+ ({
+ author: story.by ? [{ name: story.by }] : undefined,
+ date: date((story.time ?? Date.now() / 1000) * 1000),
+ description: story.score ? `${story.score} points` : undefined,
+ link: story.url ?? `https://news.ycombinator.com/item?id=${story.id}`,
+ title: story.title ?? "Hacker News Story",
+ }) satisfies DataItem,
+ );
+ });
+
+ return {
+ description: `Hacker News ${section} stories.`,
+ item,
+ language: "en",
+ link,
+ title: `Hacker News - ${section}`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ section: t.String({ default: "news", description: "Section: news, newest, or best." }),
+ }),
+ },
+ ),
+ );
diff --git a/pkgs/rssbook/src/routers/feeds/programming/index.ts b/pkgs/rssbook/src/routers/feeds/programming/index.ts
index e4e8639..4b1a719 100644
--- a/pkgs/rssbook/src/routers/feeds/programming/index.ts
+++ b/pkgs/rssbook/src/routers/feeds/programming/index.ts
@@ -1,6 +1,13 @@
import { Category } from "@/utils";
+import github from "./github";
+import hackernews from "./hackernews";
+import v2ex from "./v2ex";
export default new Category(
"programming",
"Resources, tutorials, and discussions about **coding, software development, and tech projects**.",
-).use({});
+).use({
+ github,
+ hackernews,
+ v2ex,
+});
diff --git a/pkgs/rssbook/src/routers/feeds/programming/v2ex/index.test.ts b/pkgs/rssbook/src/routers/feeds/programming/v2ex/index.test.ts
new file mode 100644
index 0000000..1a992f4
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/programming/v2ex/index.test.ts
@@ -0,0 +1,36 @@
+import { describe, expect, test } from "bun:test";
+import type { Data } from "@/types";
+import { getRouteData } from "@/utils/tests/source";
+import v2ex from ".";
+
+function expectData(data: Data) {
+ expect(data.title.length).toBeGreaterThan(0);
+ expect(data.link).toStartWith("https://");
+ expect(data.item?.length).toBeGreaterThan(0);
+}
+
+describe("V2EX", () => {
+ test("fetches latest topics", async () => {
+ const data = await getRouteData(v2ex, "/topics/latest");
+
+ expectData(data);
+ }, 20_000);
+
+ test("fetches post replies", async () => {
+ const data = await getRouteData(v2ex, "/post/1");
+
+ expect(data.title).toContain("V2EX Post");
+ }, 20_000);
+
+ test("fetches XNA feed", async () => {
+ const data = await getRouteData(v2ex, "/xna");
+
+ expectData(data);
+ }, 20_000);
+
+ test("fetches tab topics", async () => {
+ const data = await getRouteData(v2ex, "/tab/hot");
+
+ expectData(data);
+ }, 20_000);
+});
diff --git a/pkgs/rssbook/src/routers/feeds/programming/v2ex/index.ts b/pkgs/rssbook/src/routers/feeds/programming/v2ex/index.ts
new file mode 100644
index 0000000..3e40641
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/programming/v2ex/index.ts
@@ -0,0 +1,246 @@
+import type { Data, DataItem } from "@/types";
+import { Source, t } from "@/utils";
+
+type V2EXTopic = {
+ content?: string;
+ content_rendered?: string;
+ created?: number;
+ id: number;
+ member?: { username?: string };
+ title: string;
+ url: string;
+};
+
+export default new Source({
+ description: "Public topic feeds from V2EX (中国程序员社区).",
+ domain: "www.v2ex.com",
+ slug: "v2ex",
+ title: "V2EX",
+})
+ .feed(
+ {
+ description: "Fetch latest, hot, or recent V2EX topics.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Topics",
+ },
+ (app) =>
+ app.get(
+ "/topics/:type",
+ async ({ cache, date, ofetch, params: { type } }) => {
+ const apiUrl = `https://www.v2ex.com/api/topics/${type}.json`;
+ const item = await cache.tryGet(apiUrl, async () => {
+ const topics = await ofetch(apiUrl, { responseType: "json" });
+ return topics.map(
+ (topic) =>
+ ({
+ author: [{ name: topic.member?.username }],
+ date: date((topic.created ?? Date.now() / 1000) * 1000),
+ description: topic.content,
+ link: topic.url,
+ title: topic.title,
+ }) satisfies DataItem,
+ );
+ });
+
+ return {
+ description: `V2EX ${type} topics.`,
+ item,
+ language: "zh-CN",
+ link: `https://www.v2ex.com/?tab=${type}`,
+ title: `V2EX Topics - ${type}`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ type: t.String({ description: "Topic type, for example latest or hot." }),
+ }),
+ },
+ ),
+ )
+ .feed(
+ {
+ description: "Fetch a V2EX topic and its replies.",
+ fulltext: true,
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Post Replies",
+ },
+ (app) =>
+ app.get(
+ "/post/:postid",
+ async ({ cache, date, ofetch, params: { postid } }) => {
+ const topicUrl = `https://www.v2ex.com/api/topics/show.json?id=${postid}`;
+ const repliesUrl = `https://www.v2ex.com/api/replies/show.json?topic_id=${postid}`;
+ const data = await cache.tryGet(`v2ex:${postid}`, async () => {
+ const [topics, replies] = await Promise.all([
+ ofetch(topicUrl, { responseType: "json" }),
+ ofetch<
+ Array<{
+ content_rendered?: string;
+ created?: number;
+ member?: { username?: string };
+ }>
+ >(repliesUrl, { responseType: "json" }),
+ ]);
+ const topic = topics[0];
+ return {
+ description: topic?.content,
+ item: replies.map(
+ (reply) =>
+ ({
+ author: [{ name: reply.member?.username }],
+ date: date((reply.created ?? Date.now() / 1000) * 1000),
+ description: reply.content_rendered,
+ link: topic?.url ?? `https://www.v2ex.com/t/${postid}`,
+ title: `${topic?.title ?? `V2EX ${postid}`} - ${reply.member?.username ?? "reply"}`,
+ }) satisfies DataItem,
+ ),
+ language: "zh-CN",
+ link: topic?.url ?? `https://www.v2ex.com/t/${postid}`,
+ title: `V2EX Post - ${topic?.title ?? postid}`,
+ } satisfies Data;
+ });
+
+ return data satisfies Data;
+ },
+ {
+ params: t.Object({
+ postid: t.String({ description: "V2EX topic ID." }),
+ }),
+ },
+ ),
+ )
+ .feed(
+ {
+ description: "Fetch the V2EX XNA aggregated blog feed.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "XNA",
+ },
+ (app) =>
+ app.get("/xna", async ({ cache, date, load, ofetch }) => {
+ const pageUrl = "https://v2ex.com/xna";
+ const item = await cache.tryGet(pageUrl, async () => {
+ const html = await ofetch(pageUrl, { responseType: "text" });
+ const $ = load(html);
+
+ const items = $("div.xna-entry-main-container")
+ .toArray()
+ .map((el) => {
+ const $el = $(el);
+ const $link = $el.find(".xna-entry-title a").first();
+ const author = $el.find(".xna-source-author a").first().text().trim();
+ const dateText = $el.find(".xna-entry-date").first().text().trim();
+ const rawLink = $link.attr("href") || "";
+ let link = rawLink;
+ if (rawLink) {
+ try {
+ link = new URL(rawLink).href;
+ } catch {
+ // keep original link if URL normalization fails
+ }
+ }
+
+ return {
+ author: author ? [{ name: author }] : undefined,
+ date: dateText ? date(dateText) : new Date(),
+ description: $link.text().trim(),
+ link: link || pageUrl,
+ title: $link.text().trim(),
+ } satisfies DataItem;
+ });
+
+ return items;
+ });
+
+ return {
+ description: "V2EX XNA aggregated blog feed.",
+ item,
+ language: "zh-CN",
+ link: pageUrl,
+ title: "V2EX XNA",
+ } satisfies Data;
+ }),
+ )
+ .feed(
+ {
+ description: "Fetch topics from a V2EX tab.",
+ fulltext: true,
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Tab",
+ },
+ (app) =>
+ app.get(
+ "/tab/:tabid",
+ async ({ cache, formatHTML, load, ofetch, params: { tabid } }) => {
+ const pageUrl = `https://v2ex.com/?tab=${tabid}`;
+ const data = await cache.tryGet(pageUrl, async () => {
+ const html = await ofetch(pageUrl, { responseType: "text" });
+ const $ = load(html);
+ const links = $("span.item_title a")
+ .toArray()
+ .slice(0, 10)
+ .map((el) => {
+ const href = $(el).attr("href") || "";
+ return `https://www.v2ex.com${href.replace(/#.*$/, "")}`;
+ });
+
+ const item = await Promise.all(
+ links.map(async (link) =>
+ cache.tryGet(`v2ex:tab:${link}`, async () => {
+ try {
+ const topicHtml = await ofetch(link, { responseType: "text" });
+ const $$ = load(topicHtml);
+ const title = $$(".header h1").first().text().trim();
+ const author = $$("div.header > small > a").first().text().trim();
+ const topicContent = $$("div.topic_content").first().html();
+
+ const replies = $$('[id^="r_"]')
+ .toArray()
+ .map((replyEl) => {
+ const $reply = $$(replyEl);
+ const no = $reply.find(".no").first().text().trim();
+ const replyAuthor = $reply.find(".dark").first().text().trim();
+ const content = $reply.find(".reply_content").html();
+ return `
#${no}: ${replyAuthor}
${content}
`;
+ })
+ .join("");
+
+ return {
+ author: author ? [{ name: author }] : undefined,
+ date: new Date(),
+ description: formatHTML(`${topicContent ?? ""}${replies}
`, link),
+ link,
+ title: title || link,
+ } satisfies DataItem;
+ } catch {
+ return {
+ date: new Date(),
+ link,
+ title: link,
+ } satisfies DataItem;
+ }
+ }),
+ ),
+ );
+
+ return {
+ description: `V2EX ${tabid} tab topics.`,
+ item,
+ language: "zh-CN",
+ link: pageUrl,
+ title: `V2EX Tab - ${tabid}`,
+ } satisfies Data;
+ });
+
+ return data satisfies Data;
+ },
+ {
+ params: t.Object({
+ tabid: t.String({ description: "Tab ID, for example hot or tech." }),
+ }),
+ },
+ ),
+ );
diff --git a/pkgs/rssbook/src/routers/feeds/school/htu/index.test.ts b/pkgs/rssbook/src/routers/feeds/school/htu/index.test.ts
new file mode 100644
index 0000000..6904aa0
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/school/htu/index.test.ts
@@ -0,0 +1,29 @@
+import { describe, expect, test } from "bun:test";
+import type { Data } from "@/types";
+import { getRouteData } from "@/utils/tests/source";
+import htu from ".";
+
+describe("河南师范大学", () => {
+ test("fetches official website news", async () => {
+ const data = await getRouteData(htu, "/www/8954");
+
+ expectHTUData(data);
+ }, 20_000);
+
+ test("fetches teaching notices", async () => {
+ const data = await getRouteData(htu, "/teaching/3251");
+
+ expectHTUData(data);
+ }, 20_000);
+});
+
+function expectHTUData(data: Data) {
+ expect(data.title).toContain("河南师范大学");
+ expect(data.link).toStartWith("https://www.htu.edu.cn/");
+ expect(data.language).toBe("zh-CN");
+ expect(data.item?.length).toBeGreaterThan(0);
+
+ const item = data.item?.[0];
+ expect(item?.title.length).toBeGreaterThan(0);
+ expect(item?.link).toStartWith("https://www.htu.edu.cn/");
+}
diff --git a/pkgs/rssbook/src/routers/feeds/school/htu/index.ts b/pkgs/rssbook/src/routers/feeds/school/htu/index.ts
new file mode 100644
index 0000000..32939f5
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/school/htu/index.ts
@@ -0,0 +1,223 @@
+import type { Data, DataItem } from "@/types";
+import { Source, t } from "@/utils";
+
+const ROOT_URL = "https://www.htu.edu.cn/";
+const FILE_LINK_RE = /\.(pdf|docx?|xlsx?|xls|zip|rar|png|jpe?g|gif|bmp|mp4|mp3|txt)$/i;
+
+type ParsedItem = DataItem & {
+ enclosure?: {
+ url: string;
+ };
+};
+
+export default new Source({
+ description:
+ "Official website, academic affairs, and campus news from Henan Normal University (河南师范大学).",
+ domain: "www.htu.edu.cn",
+ slug: "htu",
+ title: "Henan Normal University",
+})
+ .feed(
+ {
+ description:
+ "Fetch news from a specified Henan Normal University website category and enrich each item with full article content.",
+ fulltext: true,
+ language: "zh-CN",
+ maintainer: { name: "HackHTU" },
+ title: "Official Website News",
+ withImage: "If-Present",
+ },
+ (app) =>
+ app.get(
+ "/www/:category",
+ async ({ cache, date, formatHTML, load, ofetch, params: { category }, toAbsoluteURL }) => {
+ const link = `${ROOT_URL}${category}/list.htm`;
+ const data = await cache.tryGet(link, async () => {
+ const html = await ofetch(link, { responseType: "text" });
+ const $ = load(html);
+ const channel = $("title").text().trim();
+
+ const items = $("ul.news_list li.news")
+ .toArray()
+ .map((item) => {
+ const element = $(item);
+ const href = element.find("div.wz a").first().attr("href") ?? "";
+ const imgSrc =
+ element.find("div.imgs img").first().attr("src") ??
+ element.find("div.wz a img").first().attr("src") ??
+ "";
+ const image = imgSrc ? toAbsoluteURL(imgSrc, ROOT_URL) : undefined;
+
+ return {
+ date: date(element.find("div.wz div.news_time").first().text().trim()),
+ description:
+ element.find("div.wz div.news_text").first().text().trim() || undefined,
+ enclosure: image ? { url: image } : undefined,
+ image,
+ link: toAbsoluteURL(href, ROOT_URL),
+ title: element.find("div.wz div.news_title").first().text().trim(),
+ } satisfies ParsedItem;
+ })
+ .filter((item) => item.title && item.link);
+
+ return {
+ category: channel,
+ description: channel,
+ item: await withArticleContent(items, {
+ formatHTML,
+ load,
+ ofetch,
+ toAbsoluteURL,
+ }),
+ language: "zh-CN",
+ link,
+ title: `河南师范大学 - ${channel}`,
+ } satisfies Data;
+ });
+
+ return data satisfies Data;
+ },
+ {
+ params: t.Object({
+ category: t.String({
+ default: "8954",
+ description: "Official website category ID, for example 8954.",
+ }),
+ }),
+ },
+ ),
+ )
+ .feed(
+ {
+ description:
+ "Fetch academic affairs notices from a specified Henan Normal University teaching category and enrich each item with full article content.",
+ fulltext: true,
+ language: "zh-CN",
+ maintainer: { name: "HackHTU" },
+ title: "Teaching Notices",
+ withImage: "If-Present",
+ },
+ (app) =>
+ app.get(
+ "/teaching/:category",
+ async ({ cache, date, formatHTML, load, ofetch, params: { category }, toAbsoluteURL }) => {
+ const link = `${ROOT_URL}teaching/${category}/list.htm`;
+ const data = await cache.tryGet(link, async () => {
+ const html = await ofetch(link, { responseType: "text" });
+ const $ = load(html);
+ const channel = $("title").text().trim();
+
+ const newsItems = $("ul.news_list li.news").toArray();
+ const listItems = newsItems.length ? newsItems : $("ul.news_list li").toArray();
+ const items = listItems
+ .map((item) => {
+ const element = $(item);
+ const titleElement = element.find(".news_title").first();
+ const linkElement = titleElement.find("a").first().length
+ ? titleElement.find("a").first()
+ : element.find("a").first();
+ const href = linkElement.attr("href") ?? "";
+ const title = (titleElement.text() || linkElement.text()).trim();
+ const timeRaw =
+ element.find(".news_meta").first().text().trim() ||
+ element
+ .clone()
+ .find("a")
+ .remove()
+ .end()
+ .text()
+ .trim()
+ .match(/\d{4}-\d{2}-\d{2}/)?.[0] ||
+ "";
+
+ return {
+ date: date(timeRaw),
+ link: toAbsoluteURL(href, ROOT_URL),
+ title,
+ } satisfies ParsedItem;
+ })
+ .filter((item) => item.title && item.link);
+
+ return {
+ category: channel,
+ description: channel,
+ item: await withArticleContent(items, {
+ formatHTML,
+ load,
+ ofetch,
+ toAbsoluteURL,
+ }),
+ language: "zh-CN",
+ link,
+ title: `河南师范大学 - ${channel}`,
+ } satisfies Data;
+ });
+
+ return data satisfies Data;
+ },
+ {
+ params: t.Object({
+ category: t.String({
+ default: "3251",
+ description: "Teaching category ID, for example 3251.",
+ }),
+ }),
+ },
+ ),
+ );
+
+/**
+ * Fetches same-site article pages to enrich list items with sanitized full text and first image.
+ */
+async function withArticleContent(
+ items: ParsedItem[],
+ utils: {
+ formatHTML: (html: string, baseURL?: string) => string;
+ load: typeof import("cheerio").load;
+ ofetch: (url: string, options: { responseType: "text" }) => Promise;
+ toAbsoluteURL: (url: string, baseURL: string) => string;
+ },
+): Promise {
+ return Promise.all(
+ items.map(async (item) => {
+ if (!shouldFetchArticle(item.link)) {
+ return item;
+ }
+
+ try {
+ const html = await utils.ofetch(item.link, { responseType: "text" });
+ const $ = utils.load(html);
+ const contentHTML = $("div.read").first().html();
+
+ if (contentHTML) {
+ item.description = utils.formatHTML(contentHTML, item.link);
+ }
+
+ if (!item.enclosure) {
+ const imgSrc = $("div.read img").first().attr("src");
+ if (imgSrc) {
+ const image = utils.toAbsoluteURL(imgSrc, item.link);
+ item.enclosure = { url: image };
+ item.image = image;
+ }
+ }
+ } catch {
+ return item;
+ }
+
+ return item;
+ }),
+ );
+}
+
+function shouldFetchArticle(link: string): boolean {
+ if (!link || FILE_LINK_RE.test(link)) {
+ return false;
+ }
+
+ try {
+ return new URL(link).origin === new URL(ROOT_URL).origin;
+ } catch {
+ return false;
+ }
+}
diff --git a/pkgs/rssbook/src/routers/feeds/school/index.ts b/pkgs/rssbook/src/routers/feeds/school/index.ts
index 03f7150..03200d3 100644
--- a/pkgs/rssbook/src/routers/feeds/school/index.ts
+++ b/pkgs/rssbook/src/routers/feeds/school/index.ts
@@ -1,6 +1,9 @@
import { Category } from "@/utils";
+import htu from "./htu";
export default new Category(
"school",
"Educational resources, **school-related news**, and learning materials for students and teachers.",
-).use({});
+).use({
+ htu,
+});
diff --git a/pkgs/rssbook/src/routers/feeds/socialmedia/bilibili/index.test.ts b/pkgs/rssbook/src/routers/feeds/socialmedia/bilibili/index.test.ts
new file mode 100644
index 0000000..909f7ab
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/socialmedia/bilibili/index.test.ts
@@ -0,0 +1,17 @@
+import { describe, expect, test } from "bun:test";
+import { getRouteData } from "@/utils/tests/source";
+import bilibili from ".";
+
+describe("Bilibili", () => {
+ test("fetches popular videos", async () => {
+ const data = await getRouteData(bilibili, "/popular");
+
+ expect(data.item?.length).toBeGreaterThan(0);
+ }, 20_000);
+
+ test("fetches ranking videos", async () => {
+ const data = await getRouteData(bilibili, "/ranking");
+
+ expect(data.item?.length).toBeGreaterThan(0);
+ }, 20_000);
+});
diff --git a/pkgs/rssbook/src/routers/feeds/socialmedia/bilibili/index.ts b/pkgs/rssbook/src/routers/feeds/socialmedia/bilibili/index.ts
new file mode 100644
index 0000000..3a989ca
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/socialmedia/bilibili/index.ts
@@ -0,0 +1,91 @@
+import type { Data, DataItem } from "@/types";
+import { Source } from "@/utils";
+
+type BilibiliVideo = {
+ bvid?: string;
+ cover?: string;
+ desc?: string;
+ owner?: { name?: string };
+ pic?: string;
+ pubdate?: number;
+ url?: string;
+ title?: string;
+};
+
+export default new Source({
+ description: "Public video feeds from Bilibili (哔哩哔哩).",
+ domain: "bilibili.com",
+ slug: "bilibili",
+ title: "Bilibili",
+})
+ .feed(
+ {
+ description: "Fetch popular Bilibili videos.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Popular Videos",
+ withImage: "If-Present",
+ },
+ (app) =>
+ app.get("/popular", async ({ cache, date, ofetch }) => {
+ const apiUrl = "https://api.bilibili.com/x/web-interface/popular?ps=20&pn=1";
+ const item = await cache.tryGet(apiUrl, async () => {
+ const response = await ofetch<{ data?: { list?: BilibiliVideo[] } }>(apiUrl, {
+ responseType: "json",
+ });
+ return (response.data?.list ?? []).map((video) => toItem(video, date));
+ });
+
+ return {
+ description: "Popular Bilibili videos.",
+ item,
+ language: "zh-CN",
+ link: "https://www.bilibili.com/v/popular/all",
+ title: "Bilibili Popular Videos",
+ } satisfies Data;
+ }),
+ )
+ .feed(
+ {
+ description: "Fetch Bilibili ranking videos.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Ranking Videos",
+ withImage: "If-Present",
+ },
+ (app) =>
+ app.get("/ranking", async ({ cache, date, ofetch }) => {
+ const apiUrl =
+ "https://api.bilibili.com/pgc/web/rank/list?day=3&season_type=1&web_location=333.934";
+ const item = await cache.tryGet(apiUrl, async () => {
+ const response = await ofetch<{ result?: { list?: BilibiliVideo[] } }>(apiUrl, {
+ headers: {
+ Referer: "https://www.bilibili.com/v/popular/rank/anime",
+ },
+ responseType: "json",
+ });
+ return (response.result?.list ?? []).map((video) => toItem(video, date));
+ });
+
+ return {
+ description: "Bilibili ranking videos.",
+ item,
+ language: "zh-CN",
+ link: "https://www.bilibili.com/v/popular/rank/anime",
+ title: "Bilibili Ranking",
+ } satisfies Data;
+ }),
+ );
+
+function toItem(video: BilibiliVideo, parseDate: (value: number) => Date): DataItem {
+ return {
+ author: [{ name: video.owner?.name }],
+ date: parseDate((video.pubdate ?? Date.now() / 1000) * 1000),
+ description: video.desc,
+ image: video.pic ?? video.cover,
+ link:
+ video.url ??
+ (video.bvid ? `https://www.bilibili.com/video/${video.bvid}` : "https://www.bilibili.com"),
+ title: video.title ?? "Bilibili Video",
+ } satisfies DataItem;
+}
diff --git a/pkgs/rssbook/src/routers/feeds/socialmedia/bsky/index.test.ts b/pkgs/rssbook/src/routers/feeds/socialmedia/bsky/index.test.ts
new file mode 100644
index 0000000..0a8ca2f
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/socialmedia/bsky/index.test.ts
@@ -0,0 +1,17 @@
+import { describe, expect, test } from "bun:test";
+import { getRouteData } from "@/utils/tests/source";
+import bsky from ".";
+
+describe("Bluesky", () => {
+ test("fetches keyword search", async () => {
+ const data = await getRouteData(bsky, "/keyword/rss");
+
+ expect(data.item?.length).toBeGreaterThan(0);
+ }, 20_000);
+
+ test("fetches profile posts", async () => {
+ const data = await getRouteData(bsky, "/profile/bsky.app");
+
+ expect(data.item?.length).toBeGreaterThan(0);
+ }, 20_000);
+});
diff --git a/pkgs/rssbook/src/routers/feeds/socialmedia/bsky/index.ts b/pkgs/rssbook/src/routers/feeds/socialmedia/bsky/index.ts
new file mode 100644
index 0000000..5a0545e
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/socialmedia/bsky/index.ts
@@ -0,0 +1,105 @@
+import type { Data, DataItem } from "@/types";
+import { Source, t } from "@/utils";
+
+const APP_URL = "https://bsky.app";
+const API_URL = "https://api.bsky.app/xrpc";
+
+type BskyPost = {
+ author?: { displayName?: string; handle?: string };
+ indexedAt?: string;
+ record?: { text?: string };
+ uri?: string;
+};
+
+export default new Source({
+ description: "Public posts and search feeds from Bluesky.",
+ domain: "bsky.app",
+ slug: "bsky",
+ title: "Bluesky",
+})
+ .feed(
+ {
+ description: "Fetch latest Bluesky posts matching a keyword.",
+ language: "en",
+ maintainer: { name: "RSSBook" },
+ title: "Keyword Search",
+ },
+ (app) =>
+ app.get(
+ "/keyword/:keyword",
+ async ({ cache, date, ofetch, params: { keyword } }) => {
+ const link = `${APP_URL}/search?q=${encodeURIComponent(keyword)}`;
+ const apiUrl = `${API_URL}/app.bsky.feed.searchPosts?q=${encodeURIComponent(keyword)}&limit=25&sort=latest`;
+ const item = await cache.tryGet(apiUrl, async () => {
+ const response = await ofetch<{ posts?: BskyPost[] }>(apiUrl, { responseType: "json" });
+ return (response.posts ?? []).map((post) => toItem(post, date));
+ });
+
+ return {
+ description: `Latest Bluesky posts matching "${keyword}".`,
+ item,
+ language: "en",
+ link,
+ title: `Bluesky Keyword - ${keyword}`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ keyword: t.String({ description: "Keyword to search for." }),
+ }),
+ },
+ ),
+ )
+ .feed(
+ {
+ description: "Fetch latest public posts from a Bluesky profile.",
+ language: "en",
+ maintainer: { name: "RSSBook" },
+ title: "Profile Posts",
+ },
+ (app) =>
+ app.get(
+ "/profile/:handle",
+ async ({ cache, date, ofetch, params: { handle } }) => {
+ const link = `${APP_URL}/profile/${handle}`;
+ const apiUrl = `${API_URL}/app.bsky.feed.getAuthorFeed?actor=${encodeURIComponent(handle)}&limit=25`;
+ const item = await cache.tryGet(apiUrl, async () => {
+ const response = await ofetch<{ feed?: Array<{ post?: BskyPost }> }>(apiUrl, {
+ responseType: "json",
+ });
+ return (response.feed ?? [])
+ .map((entry) => entry.post)
+ .filter((post): post is BskyPost => !!post)
+ .map((post) => toItem(post, date));
+ });
+
+ return {
+ description: `Latest public posts from ${handle}.`,
+ item,
+ language: "en",
+ link,
+ title: `Bluesky Profile - ${handle}`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ handle: t.String({ description: "Bluesky handle, for example bsky.app." }),
+ }),
+ },
+ ),
+ );
+
+function toItem(post: BskyPost, parseDate: (value: string) => Date): DataItem {
+ const handle = post.author?.handle ?? "unknown";
+ const rkey = post.uri?.split("/").pop() ?? "";
+ const link = `${APP_URL}/profile/${handle}/post/${rkey}`;
+ const text = post.record?.text?.trim() || "(no text)";
+
+ return {
+ author: [{ name: post.author?.displayName || handle }],
+ date: parseDate(post.indexedAt ?? new Date().toISOString()),
+ description: text,
+ link,
+ title: text.slice(0, 80),
+ } satisfies DataItem;
+}
diff --git a/pkgs/rssbook/src/routers/feeds/socialmedia/discord/index.test.ts b/pkgs/rssbook/src/routers/feeds/socialmedia/discord/index.test.ts
new file mode 100644
index 0000000..33c44f7
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/socialmedia/discord/index.test.ts
@@ -0,0 +1,36 @@
+import { describe, expect, test } from "bun:test";
+import type { Data } from "@/types";
+import { getRouteData } from "@/utils/tests/source";
+import source from ".";
+
+function expectData(data: Data) {
+ expect(data.title.length).toBeGreaterThan(0);
+ expect(data.link).toStartWith("https://");
+ expect(Array.isArray(data.item)).toBe(true);
+}
+
+describe("Discord", () => {
+ test("channel messages degrades gracefully without authorization", async () => {
+ const data = await getRouteData(source, "/channel/950465850056536084", {
+ config: { DISCORD_AUTHORIZATION: "dummy" },
+ });
+
+ expectData(data);
+ }, 20_000);
+
+ test("guild search degrades gracefully without authorization", async () => {
+ const data = await getRouteData(source, "/search/302094807046684672/content=friendly", {
+ config: { DISCORD_AUTHORIZATION: "dummy" },
+ });
+
+ expectData(data);
+ }, 20_000);
+
+ test("quests degrades gracefully without authorization", async () => {
+ const data = await getRouteData(source, "/quests", {
+ config: { DISCORD_AUTHORIZATION: "dummy" },
+ });
+
+ expectData(data);
+ }, 20_000);
+});
diff --git a/pkgs/rssbook/src/routers/feeds/socialmedia/discord/index.ts b/pkgs/rssbook/src/routers/feeds/socialmedia/discord/index.ts
new file mode 100644
index 0000000..977c3ba
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/socialmedia/discord/index.ts
@@ -0,0 +1,336 @@
+import type { Data, DataItem } from "@/types";
+import { Source, t } from "@/utils";
+
+const BASE_URL = "https://discord.com";
+const API_URL = `${BASE_URL}/api/v10`;
+
+const VALID_HAS_TYPES = new Set([
+ "link",
+ "embed",
+ "poll",
+ "file",
+ "video",
+ "image",
+ "sound",
+ "sticker",
+ "snapshot",
+]);
+
+type HasType = (typeof VALID_HAS_TYPES extends Set ? T : never) & string;
+
+type DiscordAuthor = {
+ global_name?: string;
+ username: string;
+};
+
+type DiscordMessage = {
+ attachments?: Array<{ content_type?: string; filename: string; size: number; url: string }>;
+ author: DiscordAuthor;
+ channel_id: string;
+ content: string;
+ edited_timestamp?: string;
+ embeds?: Array<{ description?: string; title?: string; url?: string }>;
+ id: string;
+ timestamp: string;
+};
+
+type DiscordChannel = {
+ guild_id: string;
+ name: string;
+ topic?: string;
+};
+
+type DiscordGuild = {
+ icon?: string;
+ name: string;
+};
+
+type Quest = {
+ config: {
+ application: { link: string };
+ messages: { game_publisher: string; quest_name: string };
+ rewards_config: { rewards: Array<{ messages: { name: string } }> };
+ starts_at: string;
+ task_config: { tasks: Record };
+ };
+ id: string;
+};
+
+type SearchResult = {
+ messages: DiscordMessage[][];
+};
+
+function getAuthorization(meta: {
+ config: { DISCORD_AUTHORIZATION?: string };
+}): string | undefined {
+ return meta.config.DISCORD_AUTHORIZATION?.trim();
+}
+
+function missingAuthData(): Data {
+ return {
+ description:
+ "This route requires the DISCORD_AUTHORIZATION config. Provide a valid Discord authorization header to enable it.",
+ item: [],
+ link: BASE_URL,
+ title: "Discord - Authorization Required",
+ } satisfies Data;
+}
+
+function authorName(author: DiscordAuthor): string {
+ return author.global_name ? `${author.global_name} (${author.username})` : author.username;
+}
+
+function renderMessageDescription(message: DiscordMessage): string {
+ const parts: string[] = [];
+
+ if (message.content) {
+ parts.push(`${message.content}
`);
+ }
+
+ for (const embed of message.embeds ?? []) {
+ const embedHtml = `${embed.title ?? ""} ${embed.description ?? ""} `;
+ parts.push(embedHtml);
+ }
+
+ for (const attachment of message.attachments ?? []) {
+ parts.push(
+ `${attachment.filename} (${attachment.size} bytes)
`,
+ );
+ }
+
+ return parts.join("") || "(no content)";
+}
+
+function toMessageItem(message: DiscordMessage, guildId: string, channelId: string): DataItem {
+ return {
+ author: [{ name: authorName(message.author) }],
+ date: new Date(message.timestamp),
+ description: renderMessageDescription(message),
+ id: message.id,
+ link: `${BASE_URL}/channels/${guildId}/${channelId}/${message.id}`,
+ title: message.content.split("\n", 1)[0] || "(no content)",
+ } satisfies DataItem;
+}
+
+export default new Source({
+ config: {
+ DISCORD_AUTHORIZATION: {
+ description: "Discord authorization header from the browser.",
+ required: false,
+ },
+ },
+ description: "Discord guild channels, search, and quests via the official REST API v10.",
+ domain: "discord.com",
+ slug: "discord",
+ title: "Discord",
+})
+ .feed(
+ {
+ description: "Fetch recent messages from a Discord channel.",
+ language: "en",
+ maintainer: { name: "RSSBook" },
+ title: "Channel Messages",
+ },
+ (app) =>
+ app.get(
+ "/channel/:channelId",
+ async ({ cache, meta, ofetch, params: { channelId } }) => {
+ const authorization = getAuthorization(meta);
+ if (!authorization) {
+ return missingAuthData();
+ }
+
+ const cacheKey = `discord:channel:${channelId}`;
+ return cache.tryGet(cacheKey, async () => {
+ try {
+ const [channelInfo, messages] = await Promise.all([
+ ofetch(`${API_URL}/channels/${channelId}`, {
+ headers: { authorization },
+ responseType: "json",
+ }),
+ ofetch(`${API_URL}/channels/${channelId}/messages`, {
+ headers: { authorization },
+ query: { limit: 20 },
+ responseType: "json",
+ }),
+ ]);
+
+ const guildInfo = await ofetch(
+ `${API_URL}/guilds/${channelInfo.guild_id}`,
+ {
+ headers: { authorization },
+ responseType: "json",
+ },
+ );
+
+ return {
+ description: channelInfo.topic,
+ image: guildInfo.icon
+ ? `https://cdn.discordapp.com/icons/${channelInfo.guild_id}/${guildInfo.icon}.webp`
+ : undefined,
+ item: messages.map((message) =>
+ toMessageItem(message, channelInfo.guild_id, channelId),
+ ),
+ link: `${BASE_URL}/channels/${channelInfo.guild_id}/${channelId}`,
+ title: `#${channelInfo.name} - ${guildInfo.name} - Discord`,
+ } satisfies Data;
+ } catch {
+ return {
+ description:
+ "Failed to fetch Discord channel messages. The channel may be inaccessible or the authorization header invalid.",
+ item: [],
+ link: BASE_URL,
+ title: "Discord Channel - Unavailable",
+ } satisfies Data;
+ }
+ });
+ },
+ {
+ params: t.Object({
+ channelId: t.String({ description: "Discord channel ID." }),
+ }),
+ },
+ ),
+ )
+ .feed(
+ {
+ description:
+ "Search messages in a Discord guild. Pass search parameters as a query string in the route, e.g. `content=friendly&has=image,video`.",
+ language: "en",
+ maintainer: { name: "RSSBook" },
+ title: "Guild Search",
+ },
+ (app) =>
+ app.get(
+ "/search/:guildId/:routeParams",
+ async ({ meta, ofetch, params: { guildId, routeParams } }) => {
+ const authorization = getAuthorization(meta);
+ if (!authorization) {
+ return missingAuthData();
+ }
+
+ const parsed = new URLSearchParams(routeParams);
+ const hasTypes = parsed
+ .get("has")
+ ?.split(",")
+ .filter(Boolean)
+ .filter((type): type is HasType => VALID_HAS_TYPES.has(type as HasType));
+
+ const query: Record = {
+ author_id: parsed.get("author_id") ?? undefined,
+ channel_id: parsed.get("channel_id") ?? undefined,
+ content: parsed.get("content") ?? undefined,
+ has: hasTypes?.length ? hasTypes : undefined,
+ max_id: parsed.get("max_id") ?? undefined,
+ mentions: parsed.get("mentions") ?? undefined,
+ min_id: parsed.get("min_id") ?? undefined,
+ pinned: parsed.has("pinned") ? parsed.get("pinned") === "true" : undefined,
+ };
+
+ const searchDesc = Object.entries(query)
+ .filter(([, value]) => value !== undefined)
+ .map(([key, value]) => `${key}:${Array.isArray(value) ? value.join(",") : value}`)
+ .join(" ");
+
+ if (!searchDesc) {
+ return {
+ description: "At least one valid search parameter is required.",
+ item: [],
+ link: `${BASE_URL}/channels/${guildId}`,
+ title: "Discord Search - Invalid Parameters",
+ } satisfies Data;
+ }
+
+ try {
+ const [guildInfo, searchResult] = await Promise.all([
+ ofetch(`${API_URL}/guilds/${guildId}`, {
+ headers: { authorization },
+ responseType: "json",
+ }),
+ ofetch(`${API_URL}/guilds/${guildId}/messages/search`, {
+ headers: { authorization },
+ query,
+ responseType: "json",
+ }),
+ ]);
+
+ const messages = searchResult.messages.flat();
+
+ return {
+ item: messages.map((message) => toMessageItem(message, guildId, message.channel_id)),
+ link: `${BASE_URL}/channels/${guildId}`,
+ title: `Search "${searchDesc}" in ${guildInfo.name} - Discord`,
+ } satisfies Data;
+ } catch {
+ return {
+ description:
+ "Failed to search Discord guild messages. The guild may be inaccessible or the authorization header invalid.",
+ item: [],
+ link: `${BASE_URL}/channels/${guildId}`,
+ title: "Discord Search - Unavailable",
+ } satisfies Data;
+ }
+ },
+ {
+ params: t.Object({
+ guildId: t.String({ description: "Discord guild ID." }),
+ routeParams: t.String({
+ description:
+ "Search parameters as a query string. Supported: content, author_id, mentions, has, min_id, max_id, channel_id, pinned.",
+ }),
+ }),
+ },
+ ),
+ )
+ .feed(
+ {
+ description: "Fetch available Discord quests.",
+ language: "en",
+ maintainer: { name: "RSSBook" },
+ title: "Quests",
+ },
+ (app) =>
+ app.get("/quests", async ({ cache, meta, ofetch }) => {
+ const authorization = getAuthorization(meta);
+ if (!authorization) {
+ return missingAuthData();
+ }
+
+ return cache.tryGet("discord:quests", async () => {
+ try {
+ const response = await ofetch<{ quests: Quest[] }>(`${API_URL}/quests/@me`, {
+ headers: { authorization },
+ responseType: "json",
+ });
+
+ return {
+ item: response.quests.map((quest) => {
+ const tasks = Object.values(quest.config.task_config.tasks).map(
+ (task) => task.event_name,
+ );
+ const reward = quest.config.rewards_config.rewards[0];
+
+ return {
+ author: [{ name: quest.config.messages.game_publisher }],
+ date: new Date(quest.config.starts_at),
+ description: tasks.join(", "),
+ id: quest.id,
+ link: quest.config.application.link.split("?", 1)[0],
+ title: `${quest.config.messages.quest_name} - Claim ${reward?.messages.name ?? ""}`,
+ } satisfies DataItem;
+ }),
+ link: `${BASE_URL}/quest-home`,
+ title: "Available Quests - Discord",
+ } satisfies Data;
+ } catch {
+ return {
+ description:
+ "Failed to fetch Discord quests. The authorization header may be invalid.",
+ item: [],
+ link: `${BASE_URL}/quest-home`,
+ title: "Discord Quests - Unavailable",
+ } satisfies Data;
+ }
+ });
+ }),
+ );
diff --git a/pkgs/rssbook/src/routers/feeds/socialmedia/douban/index.test.ts b/pkgs/rssbook/src/routers/feeds/socialmedia/douban/index.test.ts
new file mode 100644
index 0000000..6c89044
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/socialmedia/douban/index.test.ts
@@ -0,0 +1,17 @@
+import { describe, expect, test } from "bun:test";
+import { getRouteData } from "@/utils/tests/source";
+import douban from ".";
+
+describe("Douban", () => {
+ test("fetches coming movies", async () => {
+ const data = await getRouteData(douban, "/movie/coming");
+
+ expect(data.item?.length).toBeGreaterThan(0);
+ }, 20_000);
+
+ test("fetches latest books", async () => {
+ const data = await getRouteData(douban, "/book/latest");
+
+ expect(data.item?.length).toBeGreaterThan(0);
+ }, 20_000);
+});
diff --git a/pkgs/rssbook/src/routers/feeds/socialmedia/douban/index.ts b/pkgs/rssbook/src/routers/feeds/socialmedia/douban/index.ts
new file mode 100644
index 0000000..133694a
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/socialmedia/douban/index.ts
@@ -0,0 +1,130 @@
+import type { Data, DataItem } from "@/types";
+import { Source } from "@/utils";
+
+type DoubanMovie = {
+ actors?: Array<{ name?: string }>;
+ cover_url?: string;
+ directors?: Array<{ name?: string }>;
+ genres?: string[];
+ intro?: string;
+ pubdate?: string[];
+ title?: string;
+ url?: string;
+};
+
+type DoubanBook = {
+ card_subtitle?: string;
+ cards?: Array<{ content?: string }>;
+ null_rating_reason?: string;
+ pic?: { normal?: string };
+ rating?: { value?: string };
+ title?: string;
+ url?: string;
+};
+
+export default new Source({
+ description: "Public API feeds from Douban (豆瓣).",
+ domain: "douban.com",
+ slug: "douban",
+ title: "Douban",
+})
+ .feed(
+ {
+ description: "Fetch coming-soon movies from Douban Movie.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Coming Movies",
+ },
+ (app) =>
+ app.get("/movie/coming", async ({ cache, date, ofetch }) => {
+ const link = "https://movie.douban.com/coming";
+ const apiUrl = "https://m.douban.com/rexxar/api/v2/movie/coming_soon";
+ const item = await cache.tryGet(apiUrl, async () => {
+ const response = await ofetch<{ subjects?: DoubanMovie[] }>(apiUrl, {
+ headers: {
+ Referer: "https://m.douban.com/movie/",
+ },
+ responseType: "json",
+ });
+
+ return (response.subjects ?? []).map((movie) => {
+ const description = [
+ movie.intro,
+ movie.pubdate?.join(", "),
+ movie.directors
+ ?.map((person) => person.name)
+ .filter(Boolean)
+ .join(", "),
+ movie.actors
+ ?.map((person) => person.name)
+ .filter(Boolean)
+ .join(", "),
+ ]
+ .filter(Boolean)
+ .join(" ");
+
+ return {
+ category: movie.genres?.map((name) => ({ name })),
+ date: date(new Date()),
+ description,
+ image: movie.cover_url,
+ link: movie.url ?? link,
+ title: movie.title ?? "Douban Movie",
+ } satisfies DataItem;
+ });
+ });
+
+ return {
+ description: "Coming-soon movies from Douban Movie.",
+ item,
+ language: "zh-CN",
+ link,
+ title: "Douban Coming Movies",
+ } satisfies Data;
+ }),
+ )
+ .feed(
+ {
+ description: "Fetch latest books from Douban Books.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Latest Books",
+ },
+ (app) =>
+ app.get("/book/latest", async ({ cache, date, ofetch }) => {
+ const link = "https://book.douban.com/latest";
+ const apiUrl =
+ "https://m.douban.com/rexxar/api/v2/subject_collection/new_book_all/items?start=0&count=20&mode=collection&for_mobile=1";
+ const item = await cache.tryGet(apiUrl, async () => {
+ const response = await ofetch<{ items?: DoubanBook[] }>(apiUrl, {
+ headers: {
+ Referer: "https://m.douban.com/book/",
+ },
+ responseType: "json",
+ });
+
+ return (response.items ?? []).map((book) => {
+ const rating = book.rating?.value
+ ? `${book.rating.value} points`
+ : book.null_rating_reason;
+ return {
+ date: date(new Date()),
+ description: [book.card_subtitle, book.cards?.[0]?.content, rating]
+ .filter(Boolean)
+ .join(" "),
+ image: book.pic?.normal,
+ link: book.url ?? link,
+ title: book.title ?? "Douban Book",
+ } satisfies DataItem;
+ });
+ });
+
+ return {
+ description: "Latest books from Douban Books.",
+ item,
+ language: "zh-CN",
+ link,
+ title: "Douban Latest Books",
+ } satisfies Data;
+ }),
+ );
diff --git a/pkgs/rssbook/src/routers/feeds/socialmedia/index.ts b/pkgs/rssbook/src/routers/feeds/socialmedia/index.ts
index 8011691..451a57e 100644
--- a/pkgs/rssbook/src/routers/feeds/socialmedia/index.ts
+++ b/pkgs/rssbook/src/routers/feeds/socialmedia/index.ts
@@ -1,6 +1,21 @@
import { Category } from "@/utils";
+import bilibili from "./bilibili";
+import bsky from "./bsky";
+import discord from "./discord";
+import douban from "./douban";
+import mastodon from "./mastodon";
+import telegram from "./telegram";
+import zhihu from "./zhihu";
export default new Category(
"socialmedia",
"Content and discussions from **social networking platforms**, trends, and viral posts.",
-).use({});
+).use({
+ bilibili,
+ bsky,
+ discord,
+ douban,
+ mastodon,
+ telegram,
+ zhihu,
+});
diff --git a/pkgs/rssbook/src/routers/feeds/socialmedia/mastodon/index.test.ts b/pkgs/rssbook/src/routers/feeds/socialmedia/mastodon/index.test.ts
new file mode 100644
index 0000000..a119124
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/socialmedia/mastodon/index.test.ts
@@ -0,0 +1,17 @@
+import { describe, expect, test } from "bun:test";
+import { getRouteData } from "@/utils/tests/source";
+import mastodon from ".";
+
+describe("Mastodon", () => {
+ test("fetches local timeline", async () => {
+ const data = await getRouteData(mastodon, "/timeline/fosstodon.org");
+
+ expect(data.item?.length).toBeGreaterThan(0);
+ }, 20_000);
+
+ test("fetches hashtag timeline", async () => {
+ const data = await getRouteData(mastodon, "/tag/mastodon.social/news");
+
+ expect(data.item?.length).toBeGreaterThan(0);
+ }, 20_000);
+});
diff --git a/pkgs/rssbook/src/routers/feeds/socialmedia/mastodon/index.ts b/pkgs/rssbook/src/routers/feeds/socialmedia/mastodon/index.ts
new file mode 100644
index 0000000..ebdc9f7
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/socialmedia/mastodon/index.ts
@@ -0,0 +1,102 @@
+import type { Data, DataItem } from "@/types";
+import { Source, t } from "@/utils";
+
+type MastodonStatus = {
+ account?: { display_name?: string; username?: string };
+ content?: string;
+ created_at?: string;
+ id?: string;
+ url?: string;
+};
+
+export default new Source({
+ description: "Public timeline and hashtag feeds from Mastodon instances.",
+ domain: "mastodon.social",
+ slug: "mastodon",
+ title: "Mastodon",
+})
+ .feed(
+ {
+ description: "Fetch the public local timeline from a Mastodon instance.",
+ fulltext: true,
+ language: "en",
+ maintainer: { name: "RSSBook" },
+ title: "Local Timeline",
+ },
+ (app) =>
+ app.get(
+ "/timeline/:site",
+ async ({ cache, date, ofetch, params: { site } }) => {
+ const root = normalizeInstance(site);
+ const apiUrl = `${root}/api/v1/timelines/public?local=true&limit=20`;
+ const item = await cache.tryGet(apiUrl, async () => {
+ const statuses = await ofetch(apiUrl, { responseType: "json" });
+ return statuses.map((status) => toItem(status, date));
+ });
+
+ return {
+ description: `Local public timeline from ${site}.`,
+ item,
+ language: "en",
+ link: root,
+ title: `Mastodon Local Timeline - ${site}`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ site: t.String({ description: "Mastodon instance hostname." }),
+ }),
+ },
+ ),
+ )
+ .feed(
+ {
+ description: "Fetch public posts for a hashtag from a Mastodon instance.",
+ fulltext: true,
+ language: "en",
+ maintainer: { name: "RSSBook" },
+ title: "Hashtag Timeline",
+ },
+ (app) =>
+ app.get(
+ "/tag/:site/:hashtag",
+ async ({ cache, date, ofetch, params: { hashtag, site } }) => {
+ const root = normalizeInstance(site);
+ const apiUrl = `${root}/api/v1/timelines/tag/${encodeURIComponent(hashtag)}?limit=20`;
+ const item = await cache.tryGet(apiUrl, async () => {
+ const statuses = await ofetch(apiUrl, { responseType: "json" });
+ return statuses.map((status) => toItem(status, date));
+ });
+
+ return {
+ description: `Mastodon posts tagged #${hashtag} from ${site}.`,
+ item,
+ language: "en",
+ link: `${root}/tags/${hashtag}`,
+ title: `Mastodon Hashtag - ${hashtag}`,
+ } satisfies Data;
+ },
+ {
+ params: t.Object({
+ hashtag: t.String({ description: "Hashtag without #." }),
+ site: t.String({ description: "Mastodon instance hostname." }),
+ }),
+ },
+ ),
+ );
+
+function normalizeInstance(site: string) {
+ return site.startsWith("http") ? site.replace(/\/$/, "") : `https://${site}`;
+}
+
+function toItem(status: MastodonStatus, parseDate: (value: string) => Date): DataItem {
+ const author = status.account?.display_name || status.account?.username || "Mastodon";
+ const description = status.content ?? "";
+ return {
+ author: [{ name: author }],
+ date: parseDate(status.created_at ?? new Date().toISOString()),
+ description,
+ link: status.url ?? `https://mastodon.social/@${status.account?.username}/${status.id}`,
+ title: `${author}: ${description.replace(/[<>]/g, "").slice(0, 80)}`,
+ } satisfies DataItem;
+}
diff --git a/pkgs/rssbook/src/routers/feeds/socialmedia/telegram/index.test.ts b/pkgs/rssbook/src/routers/feeds/socialmedia/telegram/index.test.ts
new file mode 100644
index 0000000..0242aa0
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/socialmedia/telegram/index.test.ts
@@ -0,0 +1,24 @@
+import { describe, expect, test } from "bun:test";
+import type { Data } from "@/types";
+import { getRouteData } from "@/utils/tests/source";
+import source from ".";
+
+function expectData(data: Data) {
+ expect(data.title.length).toBeGreaterThan(0);
+ expect(data.link).toStartWith("https://");
+ expect(data.item?.length).toBeGreaterThan(0);
+}
+
+describe("Telegram", () => {
+ test("fetches channel posts", async () => {
+ const data = await getRouteData(source, "/channel/awesomeRSSHub");
+
+ expectData(data);
+ }, 20_000);
+
+ test("fetches channel search results", async () => {
+ const data = await getRouteData(source, "/channel/awesomeRSSHub/RSSHub");
+
+ expectData(data);
+ }, 20_000);
+});
diff --git a/pkgs/rssbook/src/routers/feeds/socialmedia/telegram/index.ts b/pkgs/rssbook/src/routers/feeds/socialmedia/telegram/index.ts
new file mode 100644
index 0000000..39e6fc9
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/socialmedia/telegram/index.ts
@@ -0,0 +1,174 @@
+import type { Data, DataItem } from "@/types";
+import { Source, t } from "@/utils";
+
+const BASE_URL = "https://t.me";
+
+type CheerioAPI = ReturnType;
+type CheerioItem = ReturnType;
+
+function extractBackgroundImageUrl(style?: string): string | undefined {
+ const match = style?.match(/url\(['"]?(.*?)['"]?\)/);
+ return match?.[1];
+}
+
+function buildMessageDescription($: CheerioAPI, item: CheerioItem): string {
+ const parts: string[] = [];
+
+ const textHtml = item.find(".tgme_widget_message_text").html();
+ if (textHtml) {
+ parts.push(textHtml);
+ }
+
+ item.find(".tgme_widget_message_photo_wrap").each((_, el) => {
+ const url = extractBackgroundImageUrl($(el).css("background-image"));
+ if (url) {
+ parts.push(`
`);
+ }
+ });
+
+ item.find(".tgme_widget_message_video_player").each((_, el) => {
+ const $el = $(el);
+ const videoUrl = $el.find(".tgme_widget_message_video").attr("src");
+ const thumbUrl = extractBackgroundImageUrl(
+ $el.find(".tgme_widget_message_video_thumb").css("background-image"),
+ );
+ if (videoUrl) {
+ parts.push(
+ `
`,
+ );
+ }
+ });
+
+ item.find("audio.tgme_widget_message_voice").each((_, el) => {
+ const url = $(el).attr("src");
+ if (url) {
+ parts.push(`
`);
+ }
+ });
+
+ const pollQuestion = item.find(".tgme_widget_message_poll_question").text();
+ const pollType = item.find(".tgme_widget_message_poll_type").text();
+ if (pollQuestion && pollType) {
+ let pollHtml = `${pollQuestion}
${pollType}
`;
+ item.find(".tgme_widget_message_poll_option").each((_, el) => {
+ const $el = $(el);
+ const percent = $el.find(".tgme_widget_message_poll_option_percent").text();
+ const text = $el.find(".tgme_widget_message_poll_option_text").text();
+ if (percent && text) {
+ pollHtml += `${percent} - ${text}
`;
+ }
+ });
+ parts.push(`${pollHtml} `);
+ }
+
+ item.find(".tgme_widget_message_document_wrap").each((_, el) => {
+ const $el = $(el);
+ const title = $el.find(".tgme_widget_message_document_title").text();
+ const extra = $el.find(".tgme_widget_message_document_extra").text();
+ if (title || extra) {
+ parts.push(`${title}
${extra}
`);
+ }
+ });
+
+ return parts.join("");
+}
+
+function buildMessageTitle(item: CheerioItem, description: string, date: Date): string {
+ const text = item.find(".tgme_widget_message_text").text().split("\n", 1)[0]?.trim() ?? "";
+ return text || description.replace(/[<>]/g, "").slice(0, 80) || date.toUTCString();
+}
+
+export default new Source({
+ description: "Telegram public channel posts scraped from the t.me web preview.",
+ domain: "t.me",
+ slug: "telegram",
+ title: "Telegram",
+}).feed(
+ {
+ description:
+ "Fetch public posts from a Telegram channel via the web preview at t.me/s/:username. Optionally append a search query as the second path parameter.",
+ language: "en",
+ maintainer: { name: "RSSBook" },
+ title: "Channel",
+ },
+ (app) =>
+ app.get(
+ "/channel/:username/:routeParams?",
+ async ({ cache, formatHTML, load, ofetch, params: { username, routeParams } }) => {
+ const resourceUrl = routeParams
+ ? `${BASE_URL}/s/${username}?q=${encodeURIComponent(routeParams)}`
+ : `${BASE_URL}/s/${username}`;
+
+ return cache.tryGet(resourceUrl, async () => {
+ const html = await ofetch(resourceUrl, { responseType: "text" });
+ const $ = load(html);
+
+ $("a[onclick][href]").each((_, el) => {
+ const href = $(el).attr("href");
+ if (href) {
+ $(el).attr("href", href.replaceAll("&", "&"));
+ }
+ });
+
+ const channelName = $(".tgme_channel_info_header_title").text().trim();
+ const channelDescription = $(".tgme_channel_info_description").text().trim();
+
+ const list = $(".tgme_widget_message_wrap:not(:has(.tme_no_messages_found))");
+
+ if (list.length === 0 && $(".tgme_channel_history").length === 0) {
+ return {
+ description: `Unable to fetch messages from ${username}. The channel may be private, restricted, or not available via the web preview.`,
+ item: [],
+ link: resourceUrl,
+ title: `${username} - Telegram Channel`,
+ } satisfies Data;
+ }
+
+ const items = list
+ .toArray()
+ .map((el) => {
+ const $item = $(el);
+ const datetime = $item.find(".tgme_widget_message_date time").attr("datetime");
+ const link = $item.find(".tgme_widget_message_date").attr("href");
+ if (!datetime || !link) {
+ return null;
+ }
+
+ const date = new Date(datetime);
+ const rawDescription = buildMessageDescription($, $item);
+ const description = formatHTML(rawDescription, resourceUrl);
+ const title = buildMessageTitle($item, description, date);
+
+ return {
+ author: [
+ {
+ name:
+ $item.find(".tgme_widget_message_from_author").text().trim() || channelName,
+ },
+ ],
+ date,
+ description,
+ link,
+ title,
+ } satisfies DataItem;
+ })
+ .filter((item) => item !== null)
+ .toReversed() as DataItem[];
+
+ return {
+ description: channelDescription,
+ image: $(".tgme_page_photo_image > img").attr("src"),
+ item: items,
+ link: resourceUrl,
+ title: `${routeParams ? `"${routeParams}" - ` : ""}${channelName || username} - Telegram Channel`,
+ } satisfies Data;
+ });
+ },
+ {
+ params: t.Object({
+ routeParams: t.Optional(t.String({ description: "Optional search query." })),
+ username: t.String({ description: "Telegram channel username." }),
+ }),
+ },
+ ),
+);
diff --git a/pkgs/rssbook/src/routers/feeds/socialmedia/zhihu/index.test.ts b/pkgs/rssbook/src/routers/feeds/socialmedia/zhihu/index.test.ts
new file mode 100644
index 0000000..4b4930e
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/socialmedia/zhihu/index.test.ts
@@ -0,0 +1,17 @@
+import { describe, expect, test } from "bun:test";
+import { getRouteData } from "@/utils/tests/source";
+import zhihu from ".";
+
+describe("Zhihu", () => {
+ test("fetches daily stories", async () => {
+ const data = await getRouteData(zhihu, "/daily");
+
+ expect(data.item?.length).toBeGreaterThan(0);
+ }, 20_000);
+
+ test("fetches hot list", async () => {
+ const data = await getRouteData(zhihu, "/hot");
+
+ expect(data.item?.length).toBeGreaterThan(0);
+ }, 20_000);
+});
diff --git a/pkgs/rssbook/src/routers/feeds/socialmedia/zhihu/index.ts b/pkgs/rssbook/src/routers/feeds/socialmedia/zhihu/index.ts
new file mode 100644
index 0000000..ef80860
--- /dev/null
+++ b/pkgs/rssbook/src/routers/feeds/socialmedia/zhihu/index.ts
@@ -0,0 +1,103 @@
+import type { Data, DataItem } from "@/types";
+import { Source } from "@/utils";
+
+type ZhihuDailyStory = {
+ ga_prefix?: string;
+ hint?: string;
+ id: number;
+ images?: string[];
+ title: string;
+ url: string;
+};
+
+type ZhihuHotItem = {
+ target?: {
+ created?: number;
+ excerpt?: string;
+ id?: number;
+ title?: string;
+ url?: string;
+ };
+};
+
+export default new Source({
+ description: "Public news and hot-list feeds from Zhihu (知乎).",
+ domain: "zhihu.com",
+ slug: "zhihu",
+ title: "Zhihu",
+})
+ .feed(
+ {
+ description: "Fetch latest Zhihu Daily stories.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Zhihu Daily",
+ withImage: "If-Present",
+ },
+ (app) =>
+ app.get("/daily", async ({ cache, date, ofetch }) => {
+ const apiUrl = "https://news-at.zhihu.com/api/4/news/latest";
+ const item = await cache.tryGet(apiUrl, async () => {
+ const response = await ofetch<{ date?: string; stories?: ZhihuDailyStory[] }>(apiUrl, {
+ responseType: "json",
+ });
+ return (response.stories ?? []).map(
+ (story) =>
+ ({
+ date: date(response.date ?? new Date().toISOString()),
+ description: story.hint,
+ image: story.images?.[0],
+ link: story.url,
+ title: story.title,
+ }) satisfies DataItem,
+ );
+ });
+
+ return {
+ description: "Latest Zhihu Daily stories.",
+ item,
+ language: "zh-CN",
+ link: "https://daily.zhihu.com/",
+ title: "Zhihu Daily",
+ } satisfies Data;
+ }),
+ )
+ .feed(
+ {
+ description: "Fetch Zhihu hot-list items.",
+ language: "zh-CN",
+ maintainer: { name: "RSSBook" },
+ title: "Hot List",
+ },
+ (app) =>
+ app.get("/hot", async ({ cache, date, ofetch }) => {
+ const apiUrl = "https://api.zhihu.com/topstory/hot-lists/total?limit=20&reverse_order=0";
+ const item = await cache.tryGet(apiUrl, async () => {
+ const response = await ofetch<{ data?: ZhihuHotItem[] }>(apiUrl, {
+ responseType: "json",
+ });
+ return (response.data ?? []).map(
+ (entry) =>
+ ({
+ date: date((entry.target?.created ?? Date.now() / 1000) * 1000),
+ description: entry.target?.excerpt,
+ link: toQuestionURL(entry.target?.url, entry.target?.id),
+ title: entry.target?.title ?? "Zhihu Hot Item",
+ }) satisfies DataItem,
+ );
+ });
+
+ return {
+ description: "Zhihu hot-list items.",
+ item,
+ language: "zh-CN",
+ link: "https://www.zhihu.com/hot",
+ title: "Zhihu Hot List",
+ } satisfies Data;
+ }),
+ );
+
+function toQuestionURL(url?: string, id?: number) {
+ const questionId = url?.split("/").pop() ?? id;
+ return questionId ? `https://www.zhihu.com/question/${questionId}` : "https://www.zhihu.com/hot";
+}
diff --git a/pkgs/rssbook/src/routers/utils/intersection.ts b/pkgs/rssbook/src/routers/utils/intersection.ts
index ce54a84..b6bdcfa 100644
--- a/pkgs/rssbook/src/routers/utils/intersection.ts
+++ b/pkgs/rssbook/src/routers/utils/intersection.ts
@@ -77,7 +77,6 @@ Returns only items that appear in **all** provided feeds, based on item ID, titl
{
description: "Array of feed URLs to intersect (minimum 2), divided by comma.",
examples: ["https://www.ruanyifeng.com/blog/atom.xml,https://techcrunch.com/feed/"],
- minItems: 2,
},
),
override: t.Optional(
diff --git a/pkgs/rssbook/src/routers/utils/union.ts b/pkgs/rssbook/src/routers/utils/union.ts
index ac4b57f..b2d8ce6 100644
--- a/pkgs/rssbook/src/routers/utils/union.ts
+++ b/pkgs/rssbook/src/routers/utils/union.ts
@@ -19,6 +19,10 @@ Returns all unique items from the provided feeds, removing duplicates based on i
.get(
"/",
async ({ query: { feeds, override }, logger }) => {
+ if (feeds.length < 2) {
+ throw new Error("At least 2 feeds are required for union");
+ }
+
const promises = feeds.map(async (feedUrl) => {
try {
const response = await ofetch(feedUrl, { responseType: "text" });
@@ -63,7 +67,6 @@ Returns all unique items from the provided feeds, removing duplicates based on i
}),
{
examples: ["https://www.ruanyifeng.com/blog/atom.xml,https://www.zhihu.com/rss"],
- minItems: 2,
title: "Feed URLs Array",
},
),
diff --git a/pkgs/rssbook/src/tests/utils/route.test.ts b/pkgs/rssbook/src/tests/utils/route.test.ts
deleted file mode 100644
index b2eb37f..0000000
--- a/pkgs/rssbook/src/tests/utils/route.test.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { describe, expect, test } from "bun:test";
-import { dispatchRoute } from "@/utils/dispatchRoute";
-
-function isXML(text: string): boolean {
- return /^\s*<\?xml\s+version=/.test(text);
-}
-
-describe.todo("route", () => {
- test("route a test feed", async () => {
- const response = await dispatchRoute("/feeds/school/htu/www/8954");
- const text = await response.text();
-
- expect(isXML(text)).toBe(true);
- });
-
- test("route test util function", async () => {
- const response = await dispatchRoute(
- "/utils/sort/?feed=https%3A%2F%2Fwww.ruanyifeng.com%2Fblog%2Fatom.xml",
- );
- const text = await response.text();
-
- expect(isXML(text)).toBe(true);
- });
-});
diff --git a/pkgs/rssbook/src/tests/types/utils.test.ts b/pkgs/rssbook/src/types/utils.test.ts
similarity index 100%
rename from pkgs/rssbook/src/tests/types/utils.test.ts
rename to pkgs/rssbook/src/types/utils.test.ts
diff --git a/pkgs/rssbook/src/tests/utils/cache.test.ts b/pkgs/rssbook/src/utils/cache.test.ts
similarity index 100%
rename from pkgs/rssbook/src/tests/utils/cache.test.ts
rename to pkgs/rssbook/src/utils/cache.test.ts
diff --git a/pkgs/rssbook/src/utils/cache.ts b/pkgs/rssbook/src/utils/cache.ts
index 85e742e..b10f452 100644
--- a/pkgs/rssbook/src/utils/cache.ts
+++ b/pkgs/rssbook/src/utils/cache.ts
@@ -1,5 +1,4 @@
import { createStorage, type Storage } from "unstorage";
-import cloudflareKVBindingDriver from "unstorage/drivers/cloudflare-kv-binding";
import lruCacheDriver from "unstorage/drivers/lru-cache";
/**
@@ -22,8 +21,6 @@ type CacheOptions = {
defaultMaxAgeMs: number;
};
-type CloudflareKVBinding = Parameters[0]["binding"];
-
/**
* Cache metadata with TTL information
*/
@@ -95,26 +92,6 @@ export class Cache {
private storage: Storage;
private option: CacheOptions;
- ///
- /// Preset cache instances for common environments
- ///
- static CLOUDFLARE_KV_Cache = (KV: CloudflareKVBinding) =>
- new this(
- createStorage({
- driver: cloudflareKVBindingDriver({
- binding: KV,
- }),
- }),
- );
-
- static LRU_Cache = new this(
- createStorage({
- driver: lruCacheDriver({
- ttl: 10 * 60 * 1000,
- }),
- }),
- );
-
constructor(
storage: Storage = createStorage({
driver: lruCacheDriver({
diff --git a/pkgs/rssbook/src/tests/utils/category.test.ts b/pkgs/rssbook/src/utils/category.test.ts
similarity index 100%
rename from pkgs/rssbook/src/tests/utils/category.test.ts
rename to pkgs/rssbook/src/utils/category.test.ts
diff --git a/pkgs/rssbook/src/tests/utils/date.test.ts b/pkgs/rssbook/src/utils/date.test.ts
similarity index 68%
rename from pkgs/rssbook/src/tests/utils/date.test.ts
rename to pkgs/rssbook/src/utils/date.test.ts
index 9567a3d..65ea91b 100644
--- a/pkgs/rssbook/src/tests/utils/date.test.ts
+++ b/pkgs/rssbook/src/utils/date.test.ts
@@ -192,3 +192,77 @@ describe("Date - relative time", () => {
expect(+date("Ciallo~(∠・ω< )☆")).toBe(+base);
});
});
+
+describe("Date - edge cases", () => {
+ test("handles invalid Date instance", () => {
+ const invalidDate = new Date("invalid");
+ const result = date(invalidDate);
+ expect(Number.isNaN(result.getTime())).toBe(false);
+ expect(result.getTime()).toBeGreaterThan(0);
+ });
+
+ test("handles numeric timestamp", () => {
+ const timestamp = 1234567890000;
+ const result = date(timestamp);
+ expect(+result).toBe(timestamp);
+ expect(result.getFullYear()).toBe(2009);
+ });
+
+ test("handles infinite number", () => {
+ const result = date(Infinity);
+ expect(Number.isNaN(result.getTime())).toBe(false);
+ expect(result.getTime()).toBeGreaterThan(0);
+ });
+
+ test("handles NaN number", () => {
+ const result = date(Number.NaN);
+ expect(Number.isNaN(result.getTime())).toBe(false);
+ expect(result.getTime()).toBeGreaterThan(0);
+ });
+
+ test("handles unix timestamp (seconds)", () => {
+ const unixTimestamp = 1234567890;
+ const result = date(unixTimestamp);
+ expect(result.getFullYear()).toBe(1970);
+ expect(result.getTime()).toBeGreaterThan(0);
+ });
+
+ test("handles date with timezone offset", () => {
+ const result = date("2025-10-18T12:00:00+08:00");
+ expect(result.getFullYear()).toBe(2025);
+ expect(result.getMonth()).toBe(9); // October
+ expect(result.getDate()).toBe(18);
+ });
+
+ test("handles date with Z timezone", () => {
+ const result = date("2025-10-18T12:00:00Z");
+ expect(result.getFullYear()).toBe(2025);
+ expect(result.getMonth()).toBe(9); // October
+ expect(result.getDate()).toBe(18);
+ });
+
+ test("handles time-only string", () => {
+ const result = date("14:30");
+ expect(result.getHours()).toBe(14);
+ expect(result.getMinutes()).toBe(30);
+ });
+
+ test("handles time-only string with AM/PM", () => {
+ const result = date("2:30pm");
+ expect(result.getHours()).toBe(14);
+ expect(result.getMinutes()).toBe(30);
+ });
+
+ test("handles empty string", () => {
+ const result = date("");
+ expect(Number.isNaN(result.getTime())).toBe(false);
+ expect(result.getTime()).toBeGreaterThan(0);
+ });
+
+ test("handles string with timezone hint", () => {
+ const result = date("2025-10-18 12:00 +0800");
+ expect(result.getFullYear()).toBe(2025);
+ expect(result.getMonth()).toBe(9); // October
+ expect(result.getDate()).toBe(18);
+ });
+});
diff --git a/pkgs/rssbook/src/tests/utils/detectLanguage.test.ts b/pkgs/rssbook/src/utils/detectLanguage.test.ts
similarity index 100%
rename from pkgs/rssbook/src/tests/utils/detectLanguage.test.ts
rename to pkgs/rssbook/src/utils/detectLanguage.test.ts
diff --git a/pkgs/rssbook/src/utils/dispatchRoute.test.ts b/pkgs/rssbook/src/utils/dispatchRoute.test.ts
new file mode 100644
index 0000000..7fdcb37
--- /dev/null
+++ b/pkgs/rssbook/src/utils/dispatchRoute.test.ts
@@ -0,0 +1,24 @@
+import { describe, expect, test } from "bun:test";
+import { dispatchRoute } from "@/utils/dispatchRoute";
+
+describe("dispatchRoute", () => {
+ test("throws error when path does not start with /", () => {
+ expect(() => dispatchRoute("feeds/test" as `/${string}`)).toThrow(
+ "Path must start with a leading slash (/).",
+ );
+ });
+
+ test("returns response with status and headers for valid path", async () => {
+ const response = await dispatchRoute("/any-path");
+ expect(response.status).toBe(404);
+ expect(response.headers).toBeDefined();
+ expect(typeof response.text).toBe("function");
+ });
+
+ test("constructs request with correct URL structure", async () => {
+ const response = await dispatchRoute("/test-path");
+ expect(response.status).toBe(404);
+ const text = await response.text();
+ expect(text.length).toBeGreaterThan(0);
+ });
+});
diff --git a/pkgs/rssbook/src/tests/utils/feeds/filter.test.ts b/pkgs/rssbook/src/utils/feeds/filter.test.ts
similarity index 100%
rename from pkgs/rssbook/src/tests/utils/feeds/filter.test.ts
rename to pkgs/rssbook/src/utils/feeds/filter.test.ts
diff --git a/pkgs/rssbook/src/tests/utils/feeds/intersection.test.ts b/pkgs/rssbook/src/utils/feeds/intersection.test.ts
similarity index 100%
rename from pkgs/rssbook/src/tests/utils/feeds/intersection.test.ts
rename to pkgs/rssbook/src/utils/feeds/intersection.test.ts
diff --git a/pkgs/rssbook/src/tests/utils/feeds/parse.test.ts b/pkgs/rssbook/src/utils/feeds/parse.test.ts
similarity index 100%
rename from pkgs/rssbook/src/tests/utils/feeds/parse.test.ts
rename to pkgs/rssbook/src/utils/feeds/parse.test.ts
diff --git a/pkgs/rssbook/src/tests/utils/feeds/render.test.ts b/pkgs/rssbook/src/utils/feeds/render.test.ts
similarity index 100%
rename from pkgs/rssbook/src/tests/utils/feeds/render.test.ts
rename to pkgs/rssbook/src/utils/feeds/render.test.ts
diff --git a/pkgs/rssbook/src/tests/utils/feeds/sort.test.ts b/pkgs/rssbook/src/utils/feeds/sort.test.ts
similarity index 100%
rename from pkgs/rssbook/src/tests/utils/feeds/sort.test.ts
rename to pkgs/rssbook/src/utils/feeds/sort.test.ts
diff --git a/pkgs/rssbook/src/tests/utils/feeds/union.test.ts b/pkgs/rssbook/src/utils/feeds/union.test.ts
similarity index 100%
rename from pkgs/rssbook/src/tests/utils/feeds/union.test.ts
rename to pkgs/rssbook/src/utils/feeds/union.test.ts
diff --git a/pkgs/rssbook/src/tests/utils/formatHTML.test.ts b/pkgs/rssbook/src/utils/formatHTML.test.ts
similarity index 68%
rename from pkgs/rssbook/src/tests/utils/formatHTML.test.ts
rename to pkgs/rssbook/src/utils/formatHTML.test.ts
index 0a990ba..cf0c550 100644
--- a/pkgs/rssbook/src/tests/utils/formatHTML.test.ts
+++ b/pkgs/rssbook/src/utils/formatHTML.test.ts
@@ -112,4 +112,50 @@ describe("formatHTML", () => {
expect(result).toContain("https://example.com/download/file.zip");
expect(result).toContain("download");
});
+
+ test("should convert audio src to absolute URL", () => {
+ const base = "https://example.com/page.html";
+ const src = ' ';
+ const result = formatHTML(src, base);
+ expect(result).toContain("https://example.com/audio/test.mp3");
+ });
+
+ test("should convert img srcset to absolute URLs", () => {
+ const base = "https://example.com/page.html";
+ const src = ' ';
+ const result = formatHTML(src, base);
+ expect(result).toContain("https://example.com/img/default.jpg");
+ expect(result).toContain("https://example.com/img/small.jpg");
+ expect(result).toContain("https://example.com/img/large.jpg");
+ });
+
+ test("should convert source srcset to absolute URLs", () => {
+ const base = "https://example.com/page.html";
+ const src = ' ';
+ const result = formatHTML(src, base);
+ expect(result).toContain("https://example.com/img/small.webp");
+ expect(result).toContain("https://example.com/img/large.webp");
+ });
+
+ test("should convert source src to absolute URL", () => {
+ const base = "https://example.com/page.html";
+ const src = ' ';
+ const result = formatHTML(src, base);
+ expect(result).toContain("https://example.com/video/clip.mp4");
+ });
+
+ test("should convert track src to absolute URL", () => {
+ const base = "https://example.com/page.html";
+ const src = ' ';
+ const result = formatHTML(src, base);
+ expect(result).toContain("https://example.com/subs/en.vtt");
+ });
+
+ test("should convert video src and poster to absolute URLs", () => {
+ const base = "https://example.com/page.html";
+ const src = ' ';
+ const result = formatHTML(src, base);
+ expect(result).toContain("https://example.com/video/main.mp4");
+ expect(result).toContain("https://example.com/img/poster.jpg");
+ });
});
diff --git a/pkgs/rssbook/src/tests/utils/load.test.ts b/pkgs/rssbook/src/utils/load.test.ts
similarity index 100%
rename from pkgs/rssbook/src/tests/utils/load.test.ts
rename to pkgs/rssbook/src/utils/load.test.ts
diff --git a/pkgs/rssbook/src/utils/load.ts b/pkgs/rssbook/src/utils/load.ts
index 44b8190..26f6e1e 100644
--- a/pkgs/rssbook/src/utils/load.ts
+++ b/pkgs/rssbook/src/utils/load.ts
@@ -1,3 +1,3 @@
-import { load } from "cheerio";
+import { load as cheerioLoad } from "cheerio";
-export { load };
+export const load: typeof cheerioLoad = cheerioLoad;
diff --git a/pkgs/rssbook/src/tests/utils/logger.test.ts b/pkgs/rssbook/src/utils/logger.test.ts
similarity index 100%
rename from pkgs/rssbook/src/tests/utils/logger.test.ts
rename to pkgs/rssbook/src/utils/logger.test.ts
diff --git a/pkgs/rssbook/src/utils/logger.ts b/pkgs/rssbook/src/utils/logger.ts
index 8019da9..0c33fdd 100644
--- a/pkgs/rssbook/src/utils/logger.ts
+++ b/pkgs/rssbook/src/utils/logger.ts
@@ -7,13 +7,14 @@ enum LogLevel {
ERROR = 3,
}
-interface LoggerConfig {
+export interface LoggerConfig {
level: LogLevel;
timestamp: boolean;
colors: boolean;
}
class Logger {
- private config: LoggerConfig;
+ /** @internal */
+ config: LoggerConfig;
constructor(config: Partial = {}) {
this.config = {
@@ -24,12 +25,14 @@ class Logger {
};
}
- private formatMessage(level: string, message: string): string {
+ /** @internal */
+ formatMessage(level: string, message: string): string {
const timestamp = this.config.timestamp ? `[${new Date().toISOString()}]` : "";
return `${timestamp} [${level}] ${message}`;
}
- private shouldLog(level: LogLevel): boolean {
+ /** @internal */
+ shouldLog(level: LogLevel): boolean {
return level >= this.config.level;
}
diff --git a/pkgs/rssbook/src/tests/utils/ofetch.test.ts b/pkgs/rssbook/src/utils/ofetch.test.ts
similarity index 100%
rename from pkgs/rssbook/src/tests/utils/ofetch.test.ts
rename to pkgs/rssbook/src/utils/ofetch.test.ts
diff --git a/pkgs/rssbook/src/tests/utils/sleep.test.ts b/pkgs/rssbook/src/utils/sleep.test.ts
similarity index 100%
rename from pkgs/rssbook/src/tests/utils/sleep.test.ts
rename to pkgs/rssbook/src/utils/sleep.test.ts
diff --git a/pkgs/rssbook/src/tests/utils/source.test.ts b/pkgs/rssbook/src/utils/source.test.ts
similarity index 75%
rename from pkgs/rssbook/src/tests/utils/source.test.ts
rename to pkgs/rssbook/src/utils/source.test.ts
index 3c6ad44..81091fb 100644
--- a/pkgs/rssbook/src/tests/utils/source.test.ts
+++ b/pkgs/rssbook/src/utils/source.test.ts
@@ -126,5 +126,52 @@ describe("source", () => {
});
});
- describe("integration", () => {});
+ describe("app instance", () => {
+ test("should return Elysia app instance", () => {
+ const sourceConfig = {
+ description: "Test description",
+ domain: "https://example.com",
+ slug: "example",
+ title: "Example Source",
+ } as const;
+
+ const source = new Source(sourceConfig);
+ const app = source.getApp();
+ expect(app).toBeDefined();
+ expect(typeof app.handle).toBe("function");
+ });
+
+ test("should handle multiple routes", () => {
+ const sourceConfig = {
+ description: "Test description",
+ domain: "https://example.com",
+ slug: "example",
+ title: "Example Source",
+ } as const;
+
+ const source = new Source(sourceConfig);
+
+ source.feed(
+ {
+ description: "Route 1",
+ language: "en",
+ maintainer: { name: "Tester" },
+ title: "route-1",
+ },
+ (app) => app,
+ );
+
+ source.feed(
+ {
+ description: "Route 2",
+ language: "en",
+ maintainer: { name: "Tester" },
+ title: "route-2",
+ },
+ (app) => app,
+ );
+
+ expect(source.getRoutes()).toHaveLength(2);
+ });
+ });
});
diff --git a/pkgs/rssbook/src/utils/tests/source.ts b/pkgs/rssbook/src/utils/tests/source.ts
new file mode 100644
index 0000000..52f5e60
--- /dev/null
+++ b/pkgs/rssbook/src/utils/tests/source.ts
@@ -0,0 +1,64 @@
+import { Elysia } from "elysia";
+import { initPlugin } from "@/plugins";
+import type { Data, FeedType } from "@/types";
+import type { Source } from "@/utils/source";
+import { logger } from "../logger";
+
+type SourceRouteTestOptions = {
+ config?: Record;
+ query?: Record;
+ type?: FeedType;
+};
+
+/**
+ * Runs a single source route and returns the feed data produced by that route.
+ */
+export async function getRouteData(
+ // biome-ignore lint/suspicious/noExplicitAny: Test helpers accept any Source instance.
+ source: Source,
+ path: `/${string}`,
+ options: SourceRouteTestOptions = {},
+): Promise {
+ const sourceConfig = source.getConfig();
+ const query = new URLSearchParams();
+ const type = options.type ?? "raw";
+
+ query.set("type", type);
+
+ for (const [key, value] of Object.entries(options.query ?? {})) {
+ if (value !== undefined) {
+ query.set(key, String(value));
+ }
+ }
+
+ const app = new Elysia({
+ name: `RSSBook/Test/${sourceConfig.slug}`,
+ })
+ .use(
+ initPlugin({
+ book: {
+ config: options.config,
+ },
+ }),
+ )
+ .use(source.getApp());
+
+ const response = await app.handle(
+ new Request(`http://rssbook.test/${sourceConfig.slug}${path}?${query.toString()}`),
+ );
+
+ if (!response.ok) {
+ throw new Error(`Source route failed: ${response.status} ${await response.text()}`);
+ }
+
+ if (type !== "raw" && type !== "json") {
+ throw new Error(
+ `Source route returned ${type}, but Data can only be read from raw or json output.`,
+ );
+ }
+ const data: Data = await response.json();
+
+ logger.info(`[${sourceConfig.slug}] ${path}`, JSON.stringify(data, null, 2));
+
+ return data;
+}
diff --git a/pkgs/rssbook/src/tests/utils/toAbsoluteURL.test.ts b/pkgs/rssbook/src/utils/toAbsoluteURL.test.ts
similarity index 100%
rename from pkgs/rssbook/src/tests/utils/toAbsoluteURL.test.ts
rename to pkgs/rssbook/src/utils/toAbsoluteURL.test.ts
diff --git a/pkgs/rssbook/src/tests/utils/uuid.test.ts b/pkgs/rssbook/src/utils/uuid.test.ts
similarity index 100%
rename from pkgs/rssbook/src/tests/utils/uuid.test.ts
rename to pkgs/rssbook/src/utils/uuid.test.ts
diff --git a/pkgs/rssbook/tsconfig.json b/pkgs/rssbook/tsconfig.json
index c1ffbd5..4cd3099 100644
--- a/pkgs/rssbook/tsconfig.json
+++ b/pkgs/rssbook/tsconfig.json
@@ -1,5 +1,6 @@
{
"compilerOptions": {
+ "ignoreDeprecations": "6.0",
"paths": {
"@/*": ["./src/*"]
},
@@ -7,5 +8,5 @@
"types": ["@types/bun"]
},
"extends": "../../tsconfig.base.json",
- "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.d.ts"]
+ "include": ["src/**/*.ts", "src/**/*.tsx"]
}
diff --git a/pkgs/rssbook/tsup.config.ts b/pkgs/rssbook/tsup.config.ts
new file mode 100644
index 0000000..87b325c
--- /dev/null
+++ b/pkgs/rssbook/tsup.config.ts
@@ -0,0 +1,24 @@
+import { defineConfig } from "tsup";
+
+export default defineConfig({
+ entry: ["src/index.ts"],
+ format: ["esm"],
+ dts: {
+ compilerOptions: {
+ ignoreDeprecations: "6.0",
+ skipLibCheck: true,
+ },
+ },
+ outDir: "dist",
+ clean: false,
+ external: [
+ "elysia",
+ "@elysiajs/eden",
+ "@elysiajs/html",
+ "@elysiajs/openapi",
+ "@elysiajs/server-timing",
+ "@elysiajs/static",
+ ],
+ splitting: false,
+ sourcemap: true,
+});
diff --git a/platform/cloudflare/index.ts b/platform/cloudflare/index.ts
index cc1cf09..08bac62 100644
--- a/platform/cloudflare/index.ts
+++ b/platform/cloudflare/index.ts
@@ -5,20 +5,31 @@
import { env } from "cloudflare:workers";
import { CloudflareAdapter } from "elysia/adapter/cloudflare-worker";
-import { RSSBookApp } from "rssbook";
-import { Cache } from "rssbook/utils";
+import { Cache, createRSSBookApp } from "rssbook";
+import { createStorage } from "unstorage";
+import cloudflareKVBindingDriver from "unstorage/drivers/cloudflare-kv-binding";
-export const app = RSSBookApp({
+export const app = createRSSBookApp({
adapter: CloudflareAdapter,
- cache: Cache.CLOUDFLARE_KV_Cache(env.RSSBookKV),
- config: {},
- enableFetchOnlineServer: false,
- feeds: ["https://github.blog/feed/"],
- meta: {
- description: "A simple RSS feed aggregator and reader.",
- title: "RSSBook",
+ book: {
+ config: {},
+ feeds: ["https://github.blog/feed/"],
+ meta: {
+ description: "A simple RSS feed aggregator and reader.",
+ title: "RSSBook",
+ },
},
- staticPlugin: false,
+ cache: new Cache(
+ createStorage({
+ driver: cloudflareKVBindingDriver({
+ binding: env.RSSBookKV,
+ }),
+ }),
+ ),
+ openapi: {
+ enableFetchOnlineServer: false,
+ },
+ static: false,
});
export default app.compile();
diff --git a/platform/cloudflare/package.json b/platform/cloudflare/package.json
index 9942545..5e46e45 100644
--- a/platform/cloudflare/package.json
+++ b/platform/cloudflare/package.json
@@ -1,17 +1,16 @@
{
"dependencies": {
"elysia": "catalog:",
- "rssbook": "workspace:*"
- },
- "devDependencies": {
- "wrangler": "catalog:"
+ "rssbook": "workspace:*",
+ "unstorage": "catalog:"
},
+ "devDependencies": {},
"name": "@rssbook/platform-cloudflare",
"private": true,
"scripts": {
- "build": "wrangler deploy --config wrangler.jsonc --dry-run --outdir ../../dist/cloudflare",
- "deploy": "wrangler deploy --config wrangler.jsonc",
- "dev": "wrangler dev --config wrangler.jsonc"
+ "build": "bunx wrangler deploy --config wrangler.jsonc --dry-run --outdir ../../dist/cloudflare",
+ "deploy": "bunx wrangler deploy --config wrangler.jsonc",
+ "dev": "bunx wrangler dev --config wrangler.jsonc"
},
"type": "module"
}
diff --git a/platform/deno/index.ts b/platform/deno/index.ts
index 732e9fc..2bad412 100644
--- a/platform/deno/index.ts
+++ b/platform/deno/index.ts
@@ -2,29 +2,46 @@
* Deno entrypoint
*/
-import { RSSBookApp } from "rssbook";
-import { Cache, logger } from "rssbook/utils";
+import { Cache, createRSSBookApp, logger } from "rssbook";
+import { createStorage } from "unstorage";
+import denoKvDriver from "unstorage/drivers/deno-kv";
type DenoRuntime = {
env: {
get: (key: string) => string | undefined;
};
+ openKv: () => Promise;
serve: (
options: { port: number },
handler: (request: Request) => Response | Promise,
) => unknown;
};
+type DenoKv = {
+ close: () => void;
+};
+
declare const Deno: DenoRuntime;
-export const app = RSSBookApp({
- cache: Cache.LRU_Cache,
- config: {},
- enableFetchOnlineServer: true,
- feeds: ["https://github.blog/feed/"],
- meta: {
- description: "A simple RSS feed aggregator and reader.",
- title: "RSSBook",
+const storage = createStorage({
+ driver: denoKvDriver({
+ base: "rssbook:",
+ openKv: () => Deno.openKv(),
+ }),
+});
+
+export const app = createRSSBookApp({
+ book: {
+ config: {},
+ feeds: ["https://github.blog/feed/"],
+ meta: {
+ description: "A simple RSS feed aggregator and reader.",
+ title: "RSSBook",
+ },
+ },
+ cache: new Cache(storage),
+ openapi: {
+ enableFetchOnlineServer: true,
},
});
export default app;
@@ -34,5 +51,5 @@ const port = Number(Deno.env.get("PORT") ?? 8787);
Deno.serve({ port }, app.fetch);
logger.info(
- `📕 RSSBook running at 0.0.0.0:${port}, You can visit http://localhost:${port}/openapi to look up all routes. ✨`,
+ `RSSBook running at 0.0.0.0:${port}, You can visit http://localhost:${port}/openapi to look up all routes.`,
);
diff --git a/platform/deno/package.json b/platform/deno/package.json
index dde7064..ec3cdf9 100644
--- a/platform/deno/package.json
+++ b/platform/deno/package.json
@@ -1,6 +1,7 @@
{
"dependencies": {
- "rssbook": "workspace:*"
+ "rssbook": "workspace:*",
+ "unstorage": "catalog:"
},
"name": "@rssbook/platform-deno",
"private": true,
diff --git a/platform/netlify/index.ts b/platform/netlify/index.ts
index e54a966..aa4a155 100644
--- a/platform/netlify/index.ts
+++ b/platform/netlify/index.ts
@@ -2,19 +2,21 @@
* Netlify Edge Functions entrypoint
*/
-import { RSSBookApp } from "rssbook";
-import { Cache } from "rssbook/utils";
+import { createRSSBookApp } from "rssbook";
export const config = { path: "/*" };
-const app = RSSBookApp({
- cache: Cache.LRU_Cache,
- config: {},
- enableFetchOnlineServer: true,
- feeds: ["https://github.blog/feed/"],
- meta: {
- description: "A simple RSS feed aggregator and reader.",
- title: "RSSBook",
+const app = createRSSBookApp({
+ book: {
+ config: {},
+ feeds: ["https://github.blog/feed/"],
+ meta: {
+ description: "A simple RSS feed aggregator and reader.",
+ title: "RSSBook",
+ },
+ },
+ openapi: {
+ enableFetchOnlineServer: true,
},
});
diff --git a/platform/netlify/package.json b/platform/netlify/package.json
index 7dafbe7..77331af 100644
--- a/platform/netlify/package.json
+++ b/platform/netlify/package.json
@@ -2,15 +2,13 @@
"dependencies": {
"rssbook": "workspace:*"
},
- "devDependencies": {
- "netlify-cli": "catalog:"
- },
+ "devDependencies": {},
"name": "@rssbook/platform-netlify",
"private": true,
"scripts": {
- "build": "netlify build --offline",
- "deploy": "netlify deploy --prod",
- "dev": "netlify dev"
+ "build": "bunx netlify build --offline",
+ "deploy": "bunx netlify deploy --prod",
+ "dev": "bunx netlify dev"
},
"type": "module"
}
diff --git a/platform/node/index.ts b/platform/node/index.ts
index 7d93182..a303714 100644
--- a/platform/node/index.ts
+++ b/platform/node/index.ts
@@ -3,25 +3,29 @@
*/
import { node } from "@elysiajs/node";
-import { RSSBookApp } from "rssbook";
-import { Cache, logger } from "rssbook/utils";
+import { createRSSBookApp, logger } from "rssbook";
-export const app = RSSBookApp({
+export const app = createRSSBookApp({
adapter: node(),
- cache: Cache.LRU_Cache,
- config: {},
- enableFetchOnlineServer: true,
- feeds: ["https://github.blog/feed/"],
- meta: {
- description: "A simple RSS feed aggregator and reader.",
- title: "RSSBook",
+ book: {
+ config: {},
+ feeds: ["https://github.blog/feed/"],
+ meta: {
+ description: "A simple RSS feed aggregator and reader.",
+ title: "RSSBook",
+ },
+ },
+ openapi: {
+ enableFetchOnlineServer: true,
},
});
export default app;
app.listen(Number(process.env.PORT ?? 8787), (server) => {
+ const hostname = server.hostname ?? "localhost";
+
logger.info(
- `📕 RSSBook running at ${server?.hostname}:${server?.port}, You can visit ${server?.hostname}:${server?.port}/openapi to look up all routes. ✨`,
+ `RSSBook running at ${hostname}:${server.port}, You can visit ${hostname}:${server.port}/openapi to look up all routes.`,
);
});
diff --git a/platform/vercel/index.ts b/platform/vercel/index.ts
index 6764fb9..01b5ac4 100644
--- a/platform/vercel/index.ts
+++ b/platform/vercel/index.ts
@@ -2,16 +2,21 @@
* Vercel entrypoint
*/
-import { RSSBookApp } from "rssbook";
-import { Cache } from "rssbook/utils";
+import { Elysia } from "elysia";
+import { createRSSBookApp } from "rssbook";
-export default RSSBookApp({
- cache: Cache.LRU_Cache,
- config: {},
- enableFetchOnlineServer: true,
- feeds: ["https://github.blog/feed/"],
- meta: {
- description: "A simple RSS feed aggregator and reader.",
- title: "RSSBook",
- },
-});
+export default new Elysia().use(
+ createRSSBookApp({
+ book: {
+ config: {},
+ feeds: ["https://github.blog/feed/"],
+ meta: {
+ description: "A simple RSS feed aggregator and reader.",
+ title: "RSSBook",
+ },
+ },
+ openapi: {
+ enableFetchOnlineServer: true,
+ },
+ }),
+);
diff --git a/platform/vercel/package.json b/platform/vercel/package.json
index bb7ba4c..0c67c3e 100644
--- a/platform/vercel/package.json
+++ b/platform/vercel/package.json
@@ -1,16 +1,15 @@
{
"dependencies": {
+ "elysia": "catalog:",
"rssbook": "workspace:*"
},
- "devDependencies": {
- "vercel": "catalog:"
- },
+ "devDependencies": {},
"name": "@rssbook/platform-vercel",
"private": true,
"scripts": {
"build": "bun build --define process.env.NODE_ENV='\"production\"' --minify-whitespace --minify-syntax --outfile ../../dist/vercel/index.js --target bun index.ts",
- "deploy": "vercel --prod",
- "dev": "vercel dev"
+ "deploy": "bunx vercel --prod",
+ "dev": "bunx vercel dev"
},
"type": "module"
}
diff --git a/scripts/add.source.ts b/scripts/add.source.ts
index ff7f033..d8c4ede 100644
--- a/scripts/add.source.ts
+++ b/scripts/add.source.ts
@@ -247,9 +247,11 @@ async function main() {
` 3. Add your source to the category ${colors.dim}pkgs/rssbook/src/routers/feeds/${category}/index.ts${colors.reset}`,
);
log(
- ` 4. Write tests for your feed in ${colors.dim}pkgs/rssbook/src/tests/feeds/${category}/${feedName}.test.ts${colors.reset}`,
+ ` 4. Write one explicit route test per GET feed in ${colors.dim}pkgs/rssbook/src/routers/feeds/${category}/${feedName}/index.test.ts${colors.reset}`,
+ );
+ log(
+ ` 5. Use ${colors.dim}bun source:test${colors.reset} to run tests for modified sources when needed.`,
);
- log(` 5. Test it, commit your changes, and submit a pull request!`);
console.log();
} catch (err) {
diff --git a/scripts/test.source.ts b/scripts/test.source.ts
index f4cdab8..df3c15d 100644
--- a/scripts/test.source.ts
+++ b/scripts/test.source.ts
@@ -67,7 +67,7 @@ interface ModifiedFeed {
async function getGitModifiedFiles(): Promise {
try {
// Get both staged and unstaged changes
- const result = await $`git status --porcelain`.text();
+ const result = await $`git status --porcelain --untracked-files=all`.text();
const lines = result.trim().split("\n").filter(Boolean);
const modifiedFiles: string[] = [];
@@ -166,22 +166,20 @@ async function getModifiedFeeds(): Promise {
return Array.from(feedMap.values());
}
-// TODO: Generate test template
function generateTestTemplate(category: string, name: string): string {
return `
import { describe, expect, test } from "bun:test";
+import { getRouteData } from "@/utils/tests/source";
+import source from ".";
describe("${category}/${name}", () => {
- test("should export a valid route", async () => {
- const route = await import("./index");
- expect(route.default).toBeDefined();
- });
+ test("fetches example route", async () => {
+ const data = await getRouteData(source, "/TODO");
- test("should handle requests correctly", async () => {
- const route = await import("./index");
- // TODO: Add your test cases here
- expect(route.default).toBeDefined();
- });
+ expect(data.title.length).toBeGreaterThan(0);
+ expect(data.link.length).toBeGreaterThan(0);
+ expect(data.item?.length).toBeGreaterThan(0);
+ }, 20_000);
});
`;
}
@@ -210,13 +208,14 @@ async function runFeedTests(feed: ModifiedFeed): Promise {
const relativePath = relative(PROJECT_ROOT, testPath);
// Run bun test on the specific test file
- const result = await $`bun test ${relativePath}`.quiet();
+ const result = await $`bun test ${relativePath}`.nothrow().quiet();
if (result.exitCode === 0) {
success(`Tests passed for ${feed.category}/${feed.name}`);
return true;
}
- error(`Tests failed for ${feed.category}/${feed.name}`);
+ error(`Tests failed for ${feed.category}/${feed.name} with exit code ${result.exitCode}`);
+ printCommandOutput(result.stdout.toString(), result.stderr.toString());
return false;
} catch (err) {
error(`Failed to run tests for ${feed.category}/${feed.name}: ${err}`);
@@ -224,6 +223,21 @@ async function runFeedTests(feed: ModifiedFeed): Promise {
}
}
+function printCommandOutput(stdout: string, stderr: string) {
+ const trimmedStdout = stdout.trim();
+ const trimmedStderr = stderr.trim();
+
+ if (trimmedStdout) {
+ log("stdout:", colors.dim);
+ console.log(trimmedStdout);
+ }
+
+ if (trimmedStderr) {
+ log("stderr:", colors.dim);
+ console.error(trimmedStderr);
+ }
+}
+
// Main execution
async function main() {
const rl = readline.createInterface({
diff --git a/skills-lock.json b/skills-lock.json
new file mode 100644
index 0000000..580ba9c
--- /dev/null
+++ b/skills-lock.json
@@ -0,0 +1,11 @@
+{
+ "version": 1,
+ "skills": {
+ "elysiajs": {
+ "source": "elysiajs/skills",
+ "sourceType": "github",
+ "skillPath": "elysia/SKILL.md",
+ "computedHash": "55268e69a2c766ed67e56a0db57544f60cc52dba0c7f024a974baf965e525e1b"
+ }
+ }
+}