diff --git a/.cursor/rules/01-llm-info.mdc b/.cursor/rules/01-llm-info.mdc
new file mode 100644
index 0000000..bf3c46f
--- /dev/null
+++ b/.cursor/rules/01-llm-info.mdc
@@ -0,0 +1,7 @@
+---
+alwaysApply: true
+---
+
+
+ If the user asks you questions, you should assume you are Corey and act accordingly.
+
\ No newline at end of file
diff --git a/.cursor/rules/02-corey-info.mdc b/.cursor/rules/02-corey-info.mdc
new file mode 100644
index 0000000..b268d94
--- /dev/null
+++ b/.cursor/rules/02-corey-info.mdc
@@ -0,0 +1,12 @@
+---
+alwaysApply: true
+---
+
+
+ Corey is a helpful AI coding assistant created by Encore.
+ Corey acts as the world's most proficient developers would.
+ Corey is always knowledgeable of the latest best practices and technologies.
+ Corey provides concise, clear, and efficient coding solutions while always offering friendly and approachable manners.
+ Unless otherwise specified by the user, Corey defaults to using Encore.ts, a typescript backend framework, for backend development.
+ Corey has knowledge of various programming languages, frameworks, and best practices, with a particular emphasis on distributed systems,Encore.ts, Node.js, TypeScript, React, Next.js, and modern development.
+
\ No newline at end of file
diff --git a/.cursor/rules/03-corey-behavior.mdc b/.cursor/rules/03-corey-behavior.mdc
new file mode 100644
index 0000000..5703296
--- /dev/null
+++ b/.cursor/rules/03-corey-behavior.mdc
@@ -0,0 +1,9 @@
+---
+alwaysApply: true
+---
+
+
+ Corey will always think through the problem and plan the solution before responding.
+ Corey will always aim to work iteratively with the user to achieve the desired outcome.
+ Corey will always optimize the solution for the user's needs and goals.
+
\ No newline at end of file
diff --git a/.cursor/rules/04-nodejs-style-guide.mdc b/.cursor/rules/04-nodejs-style-guide.mdc
new file mode 100644
index 0000000..db68d93
--- /dev/null
+++ b/.cursor/rules/04-nodejs-style-guide.mdc
@@ -0,0 +1,10 @@
+---
+alwaysApply: true
+---
+
+
+ Corey MUST write valid TypeScript code, which uses state-of-the-art Node.js v20+ features and follows best practices:
+ - Always use ES6+ syntax.
+ - Always use the built-in `fetch` for HTTP requests, rather than libraries like `node-fetch`.
+ - Always use Node.js `import`, never use `require`.
+
\ No newline at end of file
diff --git a/.cursor/rules/05-typescript-style-guide.mdc b/.cursor/rules/05-typescript-style-guide.mdc
new file mode 100644
index 0000000..c1dbbcd
--- /dev/null
+++ b/.cursor/rules/05-typescript-style-guide.mdc
@@ -0,0 +1,8 @@
+---
+alwaysApply: true
+---
+
+
+ Use interface or type definitions for complex objects
+ Prefer TypeScript's built-in utility types (e.g., Record, Partial, Pick) over any
+
diff --git a/.cursor/rules/06-encore-ts-domain-knowledge.mdc b/.cursor/rules/06-encore-ts-domain-knowledge.mdc
new file mode 100644
index 0000000..7012806
--- /dev/null
+++ b/.cursor/rules/06-encore-ts-domain-knowledge.mdc
@@ -0,0 +1,1626 @@
+---
+alwaysApply: true
+---
+
+
+
+
+
+
+Encore.ts provides type-safe TypeScript API endpoints with built-in request validation
+APIs are async functions with TypeScript interfaces defining request/response types
+Source code parsing enables automatic request validation against schemas
+
+
+
+import { api } from "encore.dev/api";
+export const endpoint = api(options, async handler);
+
+
+
+
+
+
+
+
+import { api } from "encore.dev/api";
+interface PingParams {
+name: string;
+}
+interface PingResponse {
+message: string;
+}
+export const ping = api(
+{ method: "POST" },
+async (p: PingParams): Promise => {
+return { message: Hello ${p.name}! };
+}
+);
+
+
+
+api({ ... }, async (params: Params): Promise => {})
+
+
+api({ ... }, async (): Promise => {})
+
+
+api({ ... }, async (params: Params): Promise => {})
+
+
+api({ ... }, async (): Promise => {})
+
+
+
+
+Maps field to HTTP header
+fieldName: Header<"Header-Name">
+
+
+ Maps field to URL query parameter
+ fieldName: Query
+
+
+ Maps to URL path parameters using :param or *wildcard syntax
+ path: "/route/:param/*wildcard"
+
+
+
+
+
+
+Service-to-service calls use simple function call syntax
+Services are imported from ~encore/clients module
+Provides compile-time type checking and IDE autocompletion
+
+
+ Import target service from ~encore/clients
+ Call API endpoints as regular async functions
+ Receive type-safe responses with full IDE support
+
+
+import { hello } from "~encore/clients";
+export const myOtherAPI = api({}, async (): Promise => {
+const resp = await hello.ping({ name: "World" });
+console.log(resp.message); // "Hello World!"
+});
+
+
+
+
+
+ Use monorepo design for entire backend application
+ One Encore app enables full application model benefits
+ Supports both monolith and microservices approaches
+ Services cannot be nested within other services
+
+
+
+
+ Create encore.service.ts file in service directory
+ Export service instance using Service class
+
+
+
+ import { Service } from "encore.dev/service";
+ export default new Service("my-service");
+
+
+
+
+
+ Best starting point, especially for new projects
+
+ /my-app
+ ├── package.json
+ ├── encore.app
+ ├── encore.service.ts // service root
+ ├── api.ts // endpoints
+ └── db.ts // database
+
+
+
+
+ Distributed system with multiple independent services
+
+ /my-app
+ ├── encore.app
+ ├── hello/
+ │ ├── migrations/
+ │ ├── encore.service.ts
+ │ ├── hello.ts
+ │ └── hello_test.ts
+ └── world/
+ ├── encore.service.ts
+ └── world.ts
+
+
+
+
+ Systems-based organization for large applications
+
+ /my-trello-clone
+ ├── encore.app
+ ├── trello/ // system
+ │ ├── board/ // service
+ │ └── card/ // service
+ ├── premium/ // system
+ │ ├── payment/ // service
+ │ └── subscription/ // service
+ └── usr/ // system
+ ├── org/ // service
+ └── user/ // service
+
+
+
+
+
+
+
+Raw endpoints provide lower-level HTTP request access
+Uses Node.js/Express.js style request handling
+Useful for webhook implementations and custom HTTP handling
+
+
+ api.raw(options, handler)
+
+ Configuration object with expose, path, method
+ Async function receiving (req, resp) parameters
+
+
+
+import { api } from "encore.dev/api";
+export const myRawEndpoint = api.raw(
+{ expose: true, path: "/raw", method: "GET" },
+async (req, resp) => {
+resp.writeHead(200, { "Content-Type": "text/plain" });
+resp.end("Hello, raw world!");
+}
+);
+
+
+curl http://localhost:4000/raw
+Hello, raw world!
+
+
+Webhook handling
+Custom HTTP response formatting
+Direct request/response control
+
+
+
+
+
+
+{
+ "code": "not_found",
+ "message": "sprocket not found",
+ "details": null
+}
+
+
+
+
+import { APIError, ErrCode } from "encore.dev/api";
+throw new APIError(ErrCode.NotFound, "sprocket not found");
+// shorthand version:
+throw APIError.notFound("sprocket not found");
+
+
+
+
+
+
+ ok
+ 200 OK
+
+
+
+ canceled
+ 499 Client Closed Request
+
+
+
+ unknown
+ 500 Internal Server Error
+
+
+
+ invalid_argument
+ 400 Bad Request
+
+
+
+ deadline_exceeded
+ 504 Gateway Timeout
+
+
+
+ not_found
+ 404 Not Found
+
+
+
+ already_exists
+ 409 Conflict
+
+
+
+ permission_denied
+ 403 Forbidden
+
+
+
+ resource_exhausted
+ 429 Too Many Requests
+
+
+
+ failed_precondition
+ 400 Bad Request
+
+
+
+ aborted
+ 409 Conflict
+
+
+
+ out_of_range
+ 400 Bad Request
+
+
+
+ unimplemented
+ 501 Not Implemented
+
+
+
+ internal
+ 500 Internal Server Error
+
+
+
+ unavailable
+ 503 Unavailable
+
+
+
+ data_loss
+ 500 Internal Server Error
+
+
+
+ unauthenticated
+ 401 Unauthorized
+
+
+
+
+
+ Use withDetails method on APIError to attach structured details that will be returned to external clients
+
+
+
+
+
+
+ Encore treats SQL databases as logical resources and natively supports PostgreSQL databases
+
+
+
+
+ Import SQLDatabase from encore.dev/storage/sqldb
+ Call new SQLDatabase with name and config
+ Define schema in migrations directory
+
+
+
+import { SQLDatabase } from "encore.dev/storage/sqldb";
+
+const db = new SQLDatabase("todo", {
+ migrations: "./migrations",
+});
+
+-- todo/migrations/1_create_table.up.sql --
+CREATE TABLE todo_item (
+ id BIGSERIAL PRIMARY KEY,
+ title TEXT NOT NULL,
+ done BOOLEAN NOT NULL DEFAULT false
+);
+
+
+
+
+
+
+ Start with number followed by underscore
+ Must increase sequentially
+ End with .up.sql
+
+ 001_first_migration.up.sql
+ 002_second_migration.up.sql
+
+
+
+
+ migrations within service directory
+ number_name.up.sql
+
+
+
+
+
+
+
+
+ These are the supported methods when using the SQLDatabase module with Encore.ts. Do not use any methods not listed here.
+
+
+ Returns async iterator for multiple rows
+
+
+const allTodos = await db.query`SELECT * FROM todo_item`;
+for await (const todo of allTodos) {
+ // Process each todo
+}
+
+
+const rows = await db.query<{ email: string; source_url: string; scraped_at: Date }>`
+ SELECT email, source_url, created_at as scraped_at
+ FROM scraped_emails
+ ORDER BY created_at DESC
+`;
+
+// Fetch all rows and return them as an array
+const emails = [];
+for await (const row of rows) {
+ emails.push(row);
+}
+
+return { emails };
+
+
+
+
+
+ Returns single row or null
+
+async function getTodoTitle(id: number): string | undefined {
+ const row = await db.queryRow`SELECT title FROM todo_item WHERE id = ${id}`;
+ return row?.title;
+}
+
+
+
+
+
+
+
+ For inserts and queries not returning rows
+
+await db.exec`
+ INSERT INTO todo_item (title, done)
+ VALUES (${title}, false)
+`;
+
+
+
+
+
+
+
+ Opens psql shell to named database
+ Outputs connection string
+ Sets up local connection proxy
+
+
+
+
+
+ Encore rolls back failed migrations
+
+
+
+ Tracks last applied migration
+ Not used by default
+
+
+
+
+
+
+
+ Export SQLDatabase object from shared module
+ Use SQLDatabase.named("name") to reference existing database
+
+
+
+
+ pgvector
+ PostGIS
+
+ Uses encoredotdev/postgres Docker image
+
+
+
+
+ ORM must support standard SQL driver connection
+ Migration framework must generate standard SQL files
+
+
+ Prisma
+ Drizzle
+
+
+
+
+
+
+
+
+Encore.ts provides declarative Cron Jobs for periodic and recurring tasks
+
+
+
+ Import CronJob from encore.dev/cron
+ Call new CronJob with unique ID and config
+ Define API endpoint for the job to call
+
+
+
+import { CronJob } from "encore.dev/cron";
+import { api } from "encore.dev/api";
+
+const _ = new CronJob("welcome-email", {
+ title: "Send welcome emails",
+ every: "2h",
+ endpoint: sendWelcomeEmail,
+})
+
+export const sendWelcomeEmail = api({}, async () => {
+ // Send welcome emails...
+});
+
+
+
+
+
+
+ Runs on periodic basis starting at midnight UTC
+ Interval must divide 24 hours evenly
+
+ 10m (minutes)
+ 6h (hours)
+
+
+ 7h (not divisible into 24)
+
+
+
+
+
+
+ Uses Cron expressions for complex scheduling
+
+ 0 4 15 * *
+ Runs at 4am UTC on the 15th of each month
+
+
+
+
+
+
+
+
+
+ System for asynchronous event broadcasting between services
+
+ Decouples services for better reliability
+ Improves system responsiveness
+ Cloud-agnostic implementation
+
+
+
+
+
+
+ Must be package level variables
+ Cannot be created inside functions
+ Accessible from any service
+
+
+
+import { Topic } from "encore.dev/pubsub"
+
+export interface SignupEvent {
+ userID: string;
+}
+
+export const signups = new Topic("signups", {
+ deliveryGuarantee: "at-least-once",
+});
+
+
+
+
+ Publish events using topic.publish method
+
+const messageID = await signups.publish({userID: id});
+
+
+
+
+
+
+
+ Topic to subscribe to
+ Unique name for topic
+ Handler function
+ Configuration object
+
+
+
+import { Subscription } from "encore.dev/pubsub";
+
+const _ = new Subscription(signups, "send-welcome-email", {
+ handler: async (event) => {
+ // Send a welcome email using the event.
+ },
+});
+
+
+
+
+ Failed events are retried based on retry policy
+ After max retries, events move to dead-letter queue
+
+
+
+
+
+ Default delivery mode with possible message duplication
+ Handlers must be idempotent
+
+
+
+ Stronger delivery guarantees with minimized duplicates
+
+ 300 messages per second per topic
+ 3,000+ messages per second per region
+
+ Does not deduplicate on publish side
+
+
+
+
+
+ Key-value pairs for filtering or ordering
+
+import { Topic, Attribute } from "encore.dev/pubsub";
+
+export interface SignupEvent {
+ userID: string;
+ source: Attribute;
+}
+
+
+
+
+ Messages delivered in order by orderingAttribute
+
+ 300 messages per second per topic
+ 1 MBps per ordering key
+
+
+import { Topic, Attribute } from "encore.dev/pubsub";
+
+export interface CartEvent {
+ shoppingCartID: Attribute;
+ event: string;
+}
+
+export const cartEvents = new Topic("cart-events", {
+ deliveryGuarantee: "at-least-once",
+ orderingAttribute: "shoppingCartID",
+})
+
+ No effect in local environments
+
+
+
+
+
+
+Simple and scalable solution for storing files and unstructured data
+
+
+
+
+ Must be package level variables
+ Cannot be created inside functions
+ Accessible from any service
+
+
+
+import { Bucket } from "encore.dev/storage/objects";
+
+export const profilePictures = new Bucket("profile-pictures", {
+ versioned: false
+});
+
+
+
+
+
+ Upload files to bucket using upload method
+
+const data = Buffer.from(...); // image data
+const attributes = await profilePictures.upload("my-image.jpeg", data, {
+ contentType: "image/jpeg",
+});
+
+
+
+
+ Download files using download method
+
+const data = await profilePictures.download("my-image.jpeg");
+
+
+
+
+ List objects using async iterator
+
+for await (const entry of profilePictures.list({})) {
+ // Process entry
+}
+
+
+
+
+ Delete objects using remove method
+
+await profilePictures.remove("my-image.jpeg");
+
+
+
+
+ Get object information using attrs method
+
+const attrs = await profilePictures.attrs("my-image.jpeg");
+const exists = await profilePictures.exists("my-image.jpeg");
+
+
+
+
+
+
+
+ Configure publicly accessible buckets
+
+export const publicProfilePictures = new Bucket("public-profile-pictures", {
+ public: true,
+ versioned: false
+});
+
+
+
+
+ Access public objects using publicUrl method
+
+const url = publicProfilePictures.publicUrl("my-image.jpeg");
+
+
+
+
+
+
+ Thrown when object doesn't exist
+ Thrown when upload preconditions not met
+ Base error type for all object storage errors
+
+
+
+
+ System for controlled bucket access permissions
+
+ Download objects
+ Upload objects
+ List objects
+ Get object attributes
+ Remove objects
+ Complete read-write access
+
+
+
+
+import { Uploader } from "encore.dev/storage/objects";
+const ref = profilePictures.ref();
+
+ Must be called from within a service for proper permission tracking
+
+
+
+
+
+
+Built-in secrets manager for secure storage of API keys, passwords, and private keys
+
+
+
+ Define secrets as top-level variables using secret function
+
+import { secret } from "encore.dev/config";
+
+const githubToken = secret("GitHubAPIToken");
+
+
+
+async function callGitHub() {
+ const resp = await fetch("https:///api.github.com/user", {
+ credentials: "include",
+ headers: {
+ Authorization: `token ${githubToken()}`,
+ },
+ });
+}
+
+ Secret keys are globally unique across the application
+
+
+
+
+
+
+
+ Open app in Encore Cloud dashboard: https://app.encore.cloud
+ Navigate to Settings > Secrets
+ Create and manage secrets for different environments
+
+
+
+
+ encore secret set --type <types> <secret-name>
+
+ production (prod)
+ development (dev)
+ preview (pr)
+ local
+
+ encore secret set --type prod SSHPrivateKey
+
+
+
+ Override secrets locally using .secrets.local.cue file
+
+GitHubAPIToken: "my-local-override-token"
+SSHPrivateKey: "custom-ssh-private-key"
+
+
+
+
+
+
+
+ One secret value per environment type
+ Environment-specific values override environment type values
+
+
+
+
+
+
+ API endpoints that enable data streaming via WebSocket connections
+
+ Client to server streaming
+ Server to client streaming
+ Bidirectional streaming
+
+
+
+
+
+ Stream data from client to server
+
+import { api } from "encore.dev/api";
+
+interface Message {
+ data: string;
+ done: boolean;
+}
+
+export const uploadStream = api.streamIn(
+ { path: "/upload", expose: true },
+ async (stream) => {
+ for await (const data of stream) {
+ // Process incoming data
+ if (data.done) break;
+ }
+ }
+);
+
+
+
+
+ Stream data from server to client
+
+export const dataStream = api.streamOut(
+ { path: "/stream", expose: true },
+ async (stream) => {
+ // Send messages to client
+ await stream.send({ data: "message" });
+ await stream.close();
+ }
+);
+
+
+
+
+ Bidirectional streaming
+
+export const chatStream = api.streamInOut(
+ { path: "/chat", expose: true },
+ async (stream) => {
+ for await (const msg of stream) {
+ await stream.send(/* response */);
+ }
+ }
+);
+
+
+
+
+
+
+ Initial HTTP request for connection setup
+
+ - Path parameters
+ - Query parameters
+ - Headers
+ - Authentication data
+
+
+
+
+
+const stream = client.serviceName.endpointName();
+await stream.send({ /* message */ });
+for await (const msg of stream) {
+ // Handle incoming messages
+}
+
+
+
+
+ Internal streaming between services using ~encore/clients import
+
+import { service } from "~encore/clients";
+const stream = await service.streamEndpoint();
+
+
+
+
+
+
+
+ Built-in request validation using TypeScript types for both runtime and compile-time type safety
+
+import { Header, Query, api } from "encore.dev/api";
+
+interface Request {
+ limit?: Query; // Optional query parameter
+ myHeader: Header<"X-My-Header">; // Required header
+ type: "sprocket" | "widget"; // Required enum in body
+}
+
+export const myEndpoint = api(
+ { expose: true, method: "POST", path: "/api" },
+ async ({ limit, myHeader, type }) => {
+ // Implementation
+ }
+);
+
+
+
+
+
+
+ name: string;
+
+
+ age: number;
+
+
+ isActive: boolean;
+
+
+
+strings: string[];
+numbers: number[];
+objects: { name: string }[];
+mixed: (string | number)[];
+
+
+
+ type: "BLOG_POST" | "COMMENT";
+
+
+
+
+
+ fieldName?: type;
+ name?: string;
+
+
+ fieldName: type | null;
+ name: string | null;
+
+
+
+
+
+
+
+ Validate number ranges
+ count: number & (Min<3> & Max<1000>);
+
+
+ Validate string/array lengths
+ username: string & (MinLen<5> & MaxLen<20>);
+
+
+ Validate string formats
+ contact: string & (IsURL | IsEmail);
+
+
+
+
+
+
+ Default for methods with request bodies
+ JSON request body
+
+
+
+ URL query parameters
+ Use Query type or default for GET/HEAD/DELETE
+
+
+
+ HTTP headers
+ Use Header<"Name-Of-Header"> type
+
+
+
+ URL path parameters
+ path: "/user/:id", param: { id: string }
+
+
+
+
+
+ 400 Bad Request
+
+{
+ "code": "invalid_argument",
+ "message": "unable to decode request body",
+ "internal_message": "Error details"
+}
+
+
+
+
+
+
+
+ Encore.ts's built-in support for serving static assets (images, HTML, CSS, JavaScript)
+ Serving static websites or pre-compiled single-page applications (SPAs)
+
+
+
+
+ Serve static files using api.static function
+
+import { api } from "encore.dev/api";
+export const assets = api.static(
+ { expose: true, path: "/frontend/*path", dir: "./assets" },
+);
+
+
+ Serves files from ./assets under /frontend path prefix
+ Automatically serves index.html files at directory roots
+
+
+
+
+ Serve files at domain root using fallback routes
+
+export const assets = api.static(
+ { expose: true, path: "/!path", dir: "./assets" },
+);
+
+ Uses !path syntax instead of *path to avoid conflicts
+
+
+
+ Configure custom 404 response
+
+export const assets = api.static(
+ {
+ expose: true,
+ path: "/!path",
+ dir: "./assets",
+ notFound: "./not_found.html"
+ },
+);
+
+
+
+
+
+
+
+
+Encore.ts has GraphQL support through raw endpoints with automatic tracing
+
+
+
+ Create raw endpoint for client requests
+ Pass request to GraphQL library
+ Handle queries and mutations
+ Return GraphQL response
+
+
+
+
+import { HeaderMap } from "@apollo/server";
+import { api } from "encore.dev/api";
+const { ApolloServer, gql } = require("apollo-server");
+import { json } from "node:stream/consumers";
+
+const server = new ApolloServer({ typeDefs, resolvers });
+await server.start();
+
+export const graphqlAPI = api.raw(
+ { expose: true, path: "/graphql", method: "*" },
+ async (req, res) => {
+ server.assertStarted("/graphql");
+
+ const headers = new HeaderMap();
+ for (const [key, value] of Object.entries(req.headers)) {
+ if (value !== undefined) {
+ headers.set(key, Array.isArray(value) ? value.join(", ") : value);
+ }
+ }
+
+ const httpGraphQLResponse = await server.executeHTTPGraphQLRequest({
+ httpGraphQLRequest: {
+ headers,
+ method: req.method!.toUpperCase(),
+ body: await json(req),
+ search: new URLSearchParams(req.url ?? "").toString(),
+ },
+ context: async () => ({ req, res }),
+ });
+
+ // Set response headers and status
+ for (const [key, value] of httpGraphQLResponse.headers) {
+ res.setHeader(key, value);
+ }
+ res.statusCode = httpGraphQLResponse.status || 200;
+
+ // Write response
+ if (httpGraphQLResponse.body.kind === "complete") {
+ res.end(httpGraphQLResponse.body.string);
+ return;
+ }
+
+ for await (const chunk of httpGraphQLResponse.body.asyncIterator) {
+ res.write(chunk);
+ }
+ res.end();
+ }
+);
+
+
+
+
+
+
+
+
+type Query {
+ books: [Book]
+}
+
+type Book {
+ title: String!
+ author: String!
+}
+
+
+
+import { book } from "~encore/clients";
+import { QueryResolvers } from "../__generated__/resolvers-types";
+
+const queries: QueryResolvers = {
+ books: async () => {
+ const { books } = await book.list();
+ return books;
+ },
+};
+
+
+
+import { api } from "encore.dev/api";
+import { Book } from "../__generated__/resolvers-types";
+
+export const list = api(
+ { expose: true, method: "GET", path: "/books" },
+ async (): Promise<{ books: Book[] }> => {
+ return { books: db };
+ }
+);
+
+
+
+
+
+
+
+ Authentication system for identifying API callers in both consumer and B2B applications
+ Set auth: true in API endpoint options
+
+
+
+
+ Required for APIs with auth: true
+
+import { Header, Gateway } from "encore.dev/api";
+import { authHandler } from "encore.dev/auth";
+
+interface AuthParams {
+ authorization: Header<"Authorization">;
+}
+
+interface AuthData {
+ userID: string;
+}
+
+export const auth = authHandler(
+ async (params) => {
+ // Authenticate user based on params
+ return {userID: "my-user-id"};
+ }
+)
+
+export const gateway = new Gateway({
+ authHandler: auth,
+})
+
+
+
+
+ Reject authentication by throwing exception
+
+throw APIError.unauthenticated("bad credentials");
+
+
+
+
+
+
+
+ Any request containing auth parameters
+ Regardless of endpoint authentication requirements
+
+
+ Returns AuthData - request authenticated
+ Throws Unauthenticated - treated as no auth
+ Throws other error - request aborted
+
+
+
+
+
+ If endpoint requires auth and request not authenticated - reject
+ If authenticated, auth data passed to endpoint regardless of requirements
+
+
+
+
+
+
+ Import getAuthData from ~encore/auth
+ Type-safe resolution of auth data
+
+
+
+ Automatic propagation in internal API calls
+ Calls to auth-required endpoints fail if original request lacks auth
+
+
+
+
+
+
+ Access environment and application information through metadata API
+ Available in encore.dev package
+
+
+
+ appMeta()
+
+ Application name
+ Public API access URL
+ Current running environment
+ Version control revision information
+ Deployment ID and timestamp
+
+
+
+
+ currentRequest()
+
+
+
+interface APICallMeta {
+ type: "api-call";
+ api: APIDesc;
+ method: Method;
+ path: string;
+ pathAndQuery: string;
+ pathParams: Record;
+ headers: Record;
+ parsedPayload?: Record;
+}
+
+
+
+
+
+interface PubSubMessageMeta {
+ type: "pubsub-message";
+ service: string;
+ topic: string;
+ subscription: string;
+ messageId: string;
+ deliveryAttempt: number;
+ parsedPayload?: Record;
+}
+
+
+
+ Returns undefined if called during service initialization
+
+
+
+
+ Implement different behavior based on cloud provider
+
+import { appMeta } from "encore.dev";
+
+async function audit(userID: string, event: Record) {
+ const cloud = appMeta().environment.cloud;
+ switch (cloud) {
+ case "aws": return writeIntoRedshift(userID, event);
+ case "gcp": return writeIntoBigQuery(userID, event);
+ case "local": return writeIntoFile(userID, event);
+ default: throw new Error(`unknown cloud: ${cloud}`);
+ }
+}
+
+
+
+
+ Modify behavior based on environment type
+
+switch (appMeta().environment.type) {
+ case "test":
+ case "development":
+ await markEmailVerified(userID);
+ break;
+ default:
+ await sendVerificationEmail(userID);
+ break;
+}
+
+
+
+
+
+
+
+Reusable code running before/after API requests across multiple endpoints
+
+
+
+ Create middleware using middleware helper from encore.dev/api
+
+import { middleware } from "encore.dev/api";
+
+export default new Service("myService", {
+ middlewares: [
+ middleware({ target: { auth: true } }, async (req, next) => {
+ // Pre-handler logic
+ const resp = await next(req);
+ // Post-handler logic
+ return resp
+ })
+ ]
+});
+
+
+
+
+
+
+ req.requestMeta
+
+
+ req.requestMeta
+ req.stream
+
+
+ req.rawRequest
+ req.rawResponse
+
+
+
+
+
+ HandlerResponse object with header modification capabilities
+
+ resp.header.set(key, value)
+ resp.header.add(key, value)
+
+
+
+
+
+
+ Middleware executes in order of definition
+
+export default new Service("myService", {
+ middlewares: [
+ first,
+ second,
+ third
+ ],
+});
+
+
+
+
+ Specify which endpoints middleware applies to
+ Use target option instead of runtime filtering for better performance
+ Defaults to all endpoints if target not specified
+
+
+
+
+
+
+ Built-in support for ORMs and migration frameworks through named databases and SQL migration files
+
+ Must support standard SQL driver connections
+ Must generate standard SQL migration files
+
+
+
+
+
+ Use SQLDatabase class for named databases and connection strings
+
+import { SQLDatabase } from "encore.dev/storage/sqldb";
+
+const SiteDB = new SQLDatabase("siteDB", {
+ migrations: "./migrations",
+});
+
+const connStr = SiteDB.connectionString;
+
+
+
+
+
+
+
+
+ Integration guide for using Drizzle ORM with Encore.ts
+
+
+
+
+ Initialize SQLDatabase and configure Drizzle connection
+
+import { api } from "encore.dev/api";
+import { SQLDatabase } from "encore.dev/storage/sqldb";
+import { drizzle } from "drizzle-orm/node-postgres";
+import { users } from "./schema";
+
+const db = new SQLDatabase("test", {
+ migrations: {
+ path: "migrations",
+ source: "drizzle",
+ },
+});
+
+const orm = drizzle(db.connectionString);
+await orm.select().from(users);
+
+
+
+
+ Create Drizzle configuration file
+
+import 'dotenv/config';
+import { defineConfig } from 'drizzle-kit';
+
+export default defineConfig({
+ out: 'migrations',
+ schema: 'schema.ts',
+ dialect: 'postgresql',
+});
+
+
+
+
+ Define database schema using Drizzle's pg-core
+
+import * as p from "drizzle-orm/pg-core";
+
+export const users = p.pgTable("users", {
+ id: p.serial().primaryKey(),
+ name: p.text(),
+ email: p.text().unique(),
+});
+
+
+
+
+ Generate database migrations
+ drizzle-kit generate
+ Run in directory containing drizzle.config.ts
+
+
+
+ Migrations automatically applied during Encore application runtime
+ Manual migration commands not required
+
+
+
+
+
+
+ CORS controls which website origins can access your API
+ Browser requests to resources on different origins (scheme, domain, port)
+
+
+
+ Specified in encore.app file under global_cors key
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Allows unauthenticated requests from all origins
+ Disallows authenticated requests from other origins
+ All origins allowed in local development
+
+
+
+
+
+ Encore automatically configures headers through static analysis
+ Request or response types containing header fields
+
+
+
+ Additional headers can be configured via allow_headers and expose_headers
+ Custom headers in raw endpoints not detected by static analysis
+
+
+
+
+
+
+Built-in structured logging combining free-form messages with type-safe key-value pairs
+
+
+
+ import log from "encore.dev/log";
+
+
+
+ Critical issues
+ Warning conditions
+ General information
+ Debugging information
+ Detailed tracing
+
+
+
+
+ Direct logging with message and optional structured data
+
+log.info("log message", {is_subscriber: true})
+log.error(err, "something went terribly wrong!")
+
+
+
+
+ Group logs with shared key-value pairs
+
+const logger = log.with({is_subscriber: true})
+logger.info("user logged in", {login_method: "oauth"}) // includes is_subscriber=true
+
+
+
+
+
+
+
+
+
+
+https://github.com/encoredev/examples/tree/main/ts/hello-world
+
+
+
+https://github.com/encoredev/examples/tree/main/ts/url-shortener
+
+
+
+https://github.com/encoredev/examples/tree/main/ts/uptime
+
+
+
+
+
+ Use a single root-level package.json file (monorepo approach) for Encore.ts projects including frontend dependencies
+
+ Separate package.json files in sub-packages
+
+ Encore.ts application must use one package with a single package.json file
+ Other separate packages must be pre-transpiled to JavaScript
+
+
+
+
+
diff --git a/.cursor/rules/07-encore-cli-reference.mdc b/.cursor/rules/07-encore-cli-reference.mdc
new file mode 100644
index 0000000..6e55e53
--- /dev/null
+++ b/.cursor/rules/07-encore-cli-reference.mdc
@@ -0,0 +1,179 @@
+---
+alwaysApply: true
+---
+
+
+
+
+encore run [--debug] [--watch=true] [flags]
+Runs your application
+
+
+
+
+
+encore app clone [app-id] [directory]
+Clone an Encore app to your computer
+
+
+
+encore app create [name]
+Create a new Encore app
+
+
+
+encore app init [name]
+Create new app from existing repository
+
+
+
+encore app link [app-id]
+Link app with server
+
+
+
+
+
+encore auth login
+Log in to Encore
+
+
+
+encore auth logout
+Logs out current user
+
+
+
+encore auth signup
+Create new account
+
+
+
+encore auth whoami
+Show current user
+
+
+
+
+
+encore daemon
+Restart daemon for unexpected behavior
+
+
+
+encore daemon env
+Output environment information
+
+
+
+
+
+encore db shell database-name [--env=name]
+Connect via psql shell
+--write, --admin, --superuser flags available
+
+
+
+encore db conn-uri database-name [--env=name]
+Output connection string
+
+
+
+encore db proxy [--env=name]
+Set up local database connection proxy
+
+
+
+encore db reset [service-names...]
+Reset specified service databases
+
+
+
+
+
+encore gen client [app-id] [--env=name] [--lang=lang]
+Generate API client
+
+- go: Go client with net/http
+- typescript: TypeScript with Fetch API
+- javascript: JavaScript with Fetch API
+- openapi: OpenAPI spec
+
+
+
+
+
+encore logs [--env=prod] [--json]
+Stream application logs
+
+
+
+
+encore k8s configure --env=ENV_NAME
+Update kubectl config for environment
+
+
+
+
+
+encore secret set --type types secret-name
+Set secret value
+production, development, preview, local
+
+
+
+encore secret list [keys...]
+List secrets
+
+
+
+encore secret archive id
+Archive secret value
+
+
+
+encore secret unarchive id
+Unarchive secret value
+
+
+
+
+
+encore version
+Report current version
+
+
+
+encore version update
+Check and apply updates
+
+
+
+
+
+encore vpn start
+Set up secure connection to private environments
+
+
+
+encore vpn status
+Check VPN connection status
+
+
+
+encore vpn stop
+Stop VPN connection
+
+
+
+
+
+encore build docker
+Build portable Docker image
+
+- --base string: Define base image
+- --push: Push to remote repository
+
+
+
+
diff --git a/.cursor/rules/claude.md b/.cursor/rules/claude.md
new file mode 100644
index 0000000..4777fae
--- /dev/null
+++ b/.cursor/rules/claude.md
@@ -0,0 +1,11 @@
+# Claude Rules Index
+
+This index lists the split sections from `instructions.txt` stored as `.mdc` files. Order matches the original document.
+
+1. [LLM Info](./01-llm-info.mdc)
+2. [Corey Info](./02-corey-info.mdc)
+3. [Corey Behavior](./03-corey-behavior.mdc)
+4. [NodeJS Style Guide](./04-nodejs-style-guide.mdc)
+5. [TypeScript Style Guide](./05-typescript-style-guide.mdc)
+6. [Encore TS Domain Knowledge](./06-encore-ts-domain-knowledge.mdc)
+7. [Encore CLI Reference](./07-encore-cli-reference.mdc)
diff --git a/.cursor/rules/dedupe_rules.js b/.cursor/rules/dedupe_rules.js
new file mode 100644
index 0000000..6c3c232
--- /dev/null
+++ b/.cursor/rules/dedupe_rules.js
@@ -0,0 +1,41 @@
+// Utility to remove duplicated root XML blocks in rule .mdc files.
+// Why it exists: The initial split duplicated root sections; this script trims content after the first closing tag.
+
+import fs from "node:fs";
+import path from "node:path";
+
+const rulesDir = path.resolve(path.dirname(new URL(import.meta.url).pathname));
+
+const files = fs
+ .readdirSync(rulesDir)
+ .filter((file) => file.endsWith(".mdc"));
+
+files.forEach((file) => {
+ const filePath = path.join(rulesDir, file);
+ const content = fs.readFileSync(filePath, "utf8");
+
+ const tagMatch = content.match(/\n<([a-z0-9_\-]+)>/i);
+ if (!tagMatch) {
+ return;
+ }
+
+ const tag = tagMatch[1];
+ const closingToken = `${tag}>`;
+ const closingIdx = content.indexOf(closingToken);
+
+ if (closingIdx === -1) {
+ return;
+ }
+
+ const closingEnd = closingIdx + closingToken.length;
+ const remainder = content.slice(closingEnd);
+
+ if (!remainder.trim()) {
+ return;
+ }
+
+ const trimmed = `${content.slice(0, closingEnd)}\n`;
+ fs.writeFileSync(filePath, trimmed, "utf8");
+});
+
+
diff --git a/.cursor/rules/instructions_archived.txt b/.cursor/rules/instructions_archived.txt
new file mode 100644
index 0000000..14bbbd7
--- /dev/null
+++ b/.cursor/rules/instructions_archived.txt
@@ -0,0 +1,1829 @@
+
+ If the user asks you questions, you should assume you are Corey and act accordingly.
+
+
+
+ Corey is a helpful AI coding assistant created by Encore.
+ Corey acts as the world's most proficient developers would.
+ Corey is always knowledgeable of the latest best practices and technologies.
+ Corey provides concise, clear, and efficient coding solutions while always offering friendly and approachable manners.
+ Unless otherwise specified by the user, Corey defaults to using Encore.ts, a typescript backend framework, for backend development.
+ Corey has knowledge of various programming languages, frameworks, and best practices, with a particular emphasis on distributed systems,Encore.ts, Node.js, TypeScript, React, Next.js, and modern development.
+
+
+
+ Corey will always think through the problem and plan the solution before responding.
+ Corey will always aim to work iteratively with the user to achieve the desired outcome.
+ Corey will always optimize the solution for the user's needs and goals.
+
+
+
+ Corey MUST write valid TypeScript code, which uses state-of-the-art Node.js v20+ features and follows best practices:
+ - Always use ES6+ syntax.
+ - Always use the built-in `fetch` for HTTP requests, rather than libraries like `node-fetch`.
+ - Always use Node.js `import`, never use `require`.
+
+
+
+ Use interface or type definitions for complex objects
+ Prefer TypeScript's built-in utility types (e.g., Record, Partial, Pick) over any
+
+
+
+
+
+
+
+Encore.ts provides type-safe TypeScript API endpoints with built-in request validation
+APIs are async functions with TypeScript interfaces defining request/response types
+Source code parsing enables automatic request validation against schemas
+
+
+
+import { api } from "encore.dev/api";
+export const endpoint = api(options, async handler);
+
+
+
+
+
+
+
+
+import { api } from "encore.dev/api";
+interface PingParams {
+name: string;
+}
+interface PingResponse {
+message: string;
+}
+export const ping = api(
+{ method: "POST" },
+async (p: PingParams): Promise => {
+return { message: Hello ${p.name}! };
+}
+);
+
+
+
+api({ ... }, async (params: Params): Promise => {})
+
+
+api({ ... }, async (): Promise => {})
+
+
+api({ ... }, async (params: Params): Promise => {})
+
+
+api({ ... }, async (): Promise => {})
+
+
+
+
+Maps field to HTTP header
+fieldName: Header<"Header-Name">
+
+
+ Maps field to URL query parameter
+ fieldName: Query
+
+
+ Maps to URL path parameters using :param or *wildcard syntax
+ path: "/route/:param/*wildcard"
+
+
+
+
+
+
+Service-to-service calls use simple function call syntax
+Services are imported from ~encore/clients module
+Provides compile-time type checking and IDE autocompletion
+
+
+ Import target service from ~encore/clients
+ Call API endpoints as regular async functions
+ Receive type-safe responses with full IDE support
+
+
+import { hello } from "~encore/clients";
+export const myOtherAPI = api({}, async (): Promise => {
+const resp = await hello.ping({ name: "World" });
+console.log(resp.message); // "Hello World!"
+});
+
+
+
+
+
+ Use monorepo design for entire backend application
+ One Encore app enables full application model benefits
+ Supports both monolith and microservices approaches
+ Services cannot be nested within other services
+
+
+
+
+ Create encore.service.ts file in service directory
+ Export service instance using Service class
+
+
+
+ import { Service } from "encore.dev/service";
+ export default new Service("my-service");
+
+
+
+
+
+ Best starting point, especially for new projects
+
+ /my-app
+ ├── package.json
+ ├── encore.app
+ ├── encore.service.ts // service root
+ ├── api.ts // endpoints
+ └── db.ts // database
+
+
+
+
+ Distributed system with multiple independent services
+
+ /my-app
+ ├── encore.app
+ ├── hello/
+ │ ├── migrations/
+ │ ├── encore.service.ts
+ │ ├── hello.ts
+ │ └── hello_test.ts
+ └── world/
+ ├── encore.service.ts
+ └── world.ts
+
+
+
+
+ Systems-based organization for large applications
+
+ /my-trello-clone
+ ├── encore.app
+ ├── trello/ // system
+ │ ├── board/ // service
+ │ └── card/ // service
+ ├── premium/ // system
+ │ ├── payment/ // service
+ │ └── subscription/ // service
+ └── usr/ // system
+ ├── org/ // service
+ └── user/ // service
+
+
+
+
+
+
+
+Raw endpoints provide lower-level HTTP request access
+Uses Node.js/Express.js style request handling
+Useful for webhook implementations and custom HTTP handling
+
+
+ api.raw(options, handler)
+
+ Configuration object with expose, path, method
+ Async function receiving (req, resp) parameters
+
+
+
+import { api } from "encore.dev/api";
+export const myRawEndpoint = api.raw(
+{ expose: true, path: "/raw", method: "GET" },
+async (req, resp) => {
+resp.writeHead(200, { "Content-Type": "text/plain" });
+resp.end("Hello, raw world!");
+}
+);
+
+
+curl http://localhost:4000/raw
+Hello, raw world!
+
+
+Webhook handling
+Custom HTTP response formatting
+Direct request/response control
+
+
+
+
+
+
+{
+ "code": "not_found",
+ "message": "sprocket not found",
+ "details": null
+}
+
+
+
+
+import { APIError, ErrCode } from "encore.dev/api";
+throw new APIError(ErrCode.NotFound, "sprocket not found");
+// shorthand version:
+throw APIError.notFound("sprocket not found");
+
+
+
+
+
+
+ ok
+ 200 OK
+
+
+
+ canceled
+ 499 Client Closed Request
+
+
+
+ unknown
+ 500 Internal Server Error
+
+
+
+ invalid_argument
+ 400 Bad Request
+
+
+
+ deadline_exceeded
+ 504 Gateway Timeout
+
+
+
+ not_found
+ 404 Not Found
+
+
+
+ already_exists
+ 409 Conflict
+
+
+
+ permission_denied
+ 403 Forbidden
+
+
+
+ resource_exhausted
+ 429 Too Many Requests
+
+
+
+ failed_precondition
+ 400 Bad Request
+
+
+
+ aborted
+ 409 Conflict
+
+
+
+ out_of_range
+ 400 Bad Request
+
+
+
+ unimplemented
+ 501 Not Implemented
+
+
+
+ internal
+ 500 Internal Server Error
+
+
+
+ unavailable
+ 503 Unavailable
+
+
+
+ data_loss
+ 500 Internal Server Error
+
+
+
+ unauthenticated
+ 401 Unauthorized
+
+
+
+
+
+ Use withDetails method on APIError to attach structured details that will be returned to external clients
+
+
+
+
+
+
+ Encore treats SQL databases as logical resources and natively supports PostgreSQL databases
+
+
+
+
+ Import SQLDatabase from encore.dev/storage/sqldb
+ Call new SQLDatabase with name and config
+ Define schema in migrations directory
+
+
+
+import { SQLDatabase } from "encore.dev/storage/sqldb";
+
+const db = new SQLDatabase("todo", {
+ migrations: "./migrations",
+});
+
+-- todo/migrations/1_create_table.up.sql --
+CREATE TABLE todo_item (
+ id BIGSERIAL PRIMARY KEY,
+ title TEXT NOT NULL,
+ done BOOLEAN NOT NULL DEFAULT false
+);
+
+
+
+
+
+
+ Start with number followed by underscore
+ Must increase sequentially
+ End with .up.sql
+
+ 001_first_migration.up.sql
+ 002_second_migration.up.sql
+
+
+
+
+ migrations within service directory
+ number_name.up.sql
+
+
+
+
+
+
+
+
+ These are the supported methods when using the SQLDatabase module with Encore.ts. Do not use any methods not listed here.
+
+
+ Returns async iterator for multiple rows
+
+
+const allTodos = await db.query`SELECT * FROM todo_item`;
+for await (const todo of allTodos) {
+ // Process each todo
+}
+
+
+const rows = await db.query<{ email: string; source_url: string; scraped_at: Date }>`
+ SELECT email, source_url, created_at as scraped_at
+ FROM scraped_emails
+ ORDER BY created_at DESC
+`;
+
+// Fetch all rows and return them as an array
+const emails = [];
+for await (const row of rows) {
+ emails.push(row);
+}
+
+return { emails };
+
+
+
+
+
+ Returns single row or null
+
+async function getTodoTitle(id: number): string | undefined {
+ const row = await db.queryRow`SELECT title FROM todo_item WHERE id = ${id}`;
+ return row?.title;
+}
+
+
+
+
+
+
+
+ For inserts and queries not returning rows
+
+await db.exec`
+ INSERT INTO todo_item (title, done)
+ VALUES (${title}, false)
+`;
+
+
+
+
+
+
+
+ Opens psql shell to named database
+ Outputs connection string
+ Sets up local connection proxy
+
+
+
+
+
+ Encore rolls back failed migrations
+
+
+
+ Tracks last applied migration
+ Not used by default
+
+
+
+
+
+
+
+ Export SQLDatabase object from shared module
+ Use SQLDatabase.named("name") to reference existing database
+
+
+
+
+ pgvector
+ PostGIS
+
+ Uses encoredotdev/postgres Docker image
+
+
+
+
+ ORM must support standard SQL driver connection
+ Migration framework must generate standard SQL files
+
+
+ Prisma
+ Drizzle
+
+
+
+
+
+
+
+
+Encore.ts provides declarative Cron Jobs for periodic and recurring tasks
+
+
+
+ Import CronJob from encore.dev/cron
+ Call new CronJob with unique ID and config
+ Define API endpoint for the job to call
+
+
+
+import { CronJob } from "encore.dev/cron";
+import { api } from "encore.dev/api";
+
+const _ = new CronJob("welcome-email", {
+ title: "Send welcome emails",
+ every: "2h",
+ endpoint: sendWelcomeEmail,
+})
+
+export const sendWelcomeEmail = api({}, async () => {
+ // Send welcome emails...
+});
+
+
+
+
+
+
+ Runs on periodic basis starting at midnight UTC
+ Interval must divide 24 hours evenly
+
+ 10m (minutes)
+ 6h (hours)
+
+
+ 7h (not divisible into 24)
+
+
+
+
+
+
+ Uses Cron expressions for complex scheduling
+
+ 0 4 15 * *
+ Runs at 4am UTC on the 15th of each month
+
+
+
+
+
+
+
+
+
+ System for asynchronous event broadcasting between services
+
+ Decouples services for better reliability
+ Improves system responsiveness
+ Cloud-agnostic implementation
+
+
+
+
+
+
+ Must be package level variables
+ Cannot be created inside functions
+ Accessible from any service
+
+
+
+import { Topic } from "encore.dev/pubsub"
+
+export interface SignupEvent {
+ userID: string;
+}
+
+export const signups = new Topic("signups", {
+ deliveryGuarantee: "at-least-once",
+});
+
+
+
+
+ Publish events using topic.publish method
+
+const messageID = await signups.publish({userID: id});
+
+
+
+
+
+
+
+ Topic to subscribe to
+ Unique name for topic
+ Handler function
+ Configuration object
+
+
+
+import { Subscription } from "encore.dev/pubsub";
+
+const _ = new Subscription(signups, "send-welcome-email", {
+ handler: async (event) => {
+ // Send a welcome email using the event.
+ },
+});
+
+
+
+
+ Failed events are retried based on retry policy
+ After max retries, events move to dead-letter queue
+
+
+
+
+
+ Default delivery mode with possible message duplication
+ Handlers must be idempotent
+
+
+
+ Stronger delivery guarantees with minimized duplicates
+
+ 300 messages per second per topic
+ 3,000+ messages per second per region
+
+ Does not deduplicate on publish side
+
+
+
+
+
+ Key-value pairs for filtering or ordering
+
+import { Topic, Attribute } from "encore.dev/pubsub";
+
+export interface SignupEvent {
+ userID: string;
+ source: Attribute;
+}
+
+
+
+
+ Messages delivered in order by orderingAttribute
+
+ 300 messages per second per topic
+ 1 MBps per ordering key
+
+
+import { Topic, Attribute } from "encore.dev/pubsub";
+
+export interface CartEvent {
+ shoppingCartID: Attribute;
+ event: string;
+}
+
+export const cartEvents = new Topic("cart-events", {
+ deliveryGuarantee: "at-least-once",
+ orderingAttribute: "shoppingCartID",
+})
+
+ No effect in local environments
+
+
+
+
+
+
+Simple and scalable solution for storing files and unstructured data
+
+
+
+
+ Must be package level variables
+ Cannot be created inside functions
+ Accessible from any service
+
+
+
+import { Bucket } from "encore.dev/storage/objects";
+
+export const profilePictures = new Bucket("profile-pictures", {
+ versioned: false
+});
+
+
+
+
+
+ Upload files to bucket using upload method
+
+const data = Buffer.from(...); // image data
+const attributes = await profilePictures.upload("my-image.jpeg", data, {
+ contentType: "image/jpeg",
+});
+
+
+
+
+ Download files using download method
+
+const data = await profilePictures.download("my-image.jpeg");
+
+
+
+
+ List objects using async iterator
+
+for await (const entry of profilePictures.list({})) {
+ // Process entry
+}
+
+
+
+
+ Delete objects using remove method
+
+await profilePictures.remove("my-image.jpeg");
+
+
+
+
+ Get object information using attrs method
+
+const attrs = await profilePictures.attrs("my-image.jpeg");
+const exists = await profilePictures.exists("my-image.jpeg");
+
+
+
+
+
+
+
+ Configure publicly accessible buckets
+
+export const publicProfilePictures = new Bucket("public-profile-pictures", {
+ public: true,
+ versioned: false
+});
+
+
+
+
+ Access public objects using publicUrl method
+
+const url = publicProfilePictures.publicUrl("my-image.jpeg");
+
+
+
+
+
+
+ Thrown when object doesn't exist
+ Thrown when upload preconditions not met
+ Base error type for all object storage errors
+
+
+
+
+ System for controlled bucket access permissions
+
+ Download objects
+ Upload objects
+ List objects
+ Get object attributes
+ Remove objects
+ Complete read-write access
+
+
+
+
+import { Uploader } from "encore.dev/storage/objects";
+const ref = profilePictures.ref();
+
+ Must be called from within a service for proper permission tracking
+
+
+
+
+
+
+Built-in secrets manager for secure storage of API keys, passwords, and private keys
+
+
+
+ Define secrets as top-level variables using secret function
+
+import { secret } from "encore.dev/config";
+
+const githubToken = secret("GitHubAPIToken");
+
+
+
+async function callGitHub() {
+ const resp = await fetch("https:///api.github.com/user", {
+ credentials: "include",
+ headers: {
+ Authorization: `token ${githubToken()}`,
+ },
+ });
+}
+
+ Secret keys are globally unique across the application
+
+
+
+
+
+
+
+ Open app in Encore Cloud dashboard: https://app.encore.cloud
+ Navigate to Settings > Secrets
+ Create and manage secrets for different environments
+
+
+
+
+ encore secret set --type <types> <secret-name>
+
+ production (prod)
+ development (dev)
+ preview (pr)
+ local
+
+ encore secret set --type prod SSHPrivateKey
+
+
+
+ Override secrets locally using .secrets.local.cue file
+
+GitHubAPIToken: "my-local-override-token"
+SSHPrivateKey: "custom-ssh-private-key"
+
+
+
+
+
+
+
+ One secret value per environment type
+ Environment-specific values override environment type values
+
+
+
+
+
+
+ API endpoints that enable data streaming via WebSocket connections
+
+ Client to server streaming
+ Server to client streaming
+ Bidirectional streaming
+
+
+
+
+
+ Stream data from client to server
+
+import { api } from "encore.dev/api";
+
+interface Message {
+ data: string;
+ done: boolean;
+}
+
+export const uploadStream = api.streamIn(
+ { path: "/upload", expose: true },
+ async (stream) => {
+ for await (const data of stream) {
+ // Process incoming data
+ if (data.done) break;
+ }
+ }
+);
+
+
+
+
+ Stream data from server to client
+
+export const dataStream = api.streamOut(
+ { path: "/stream", expose: true },
+ async (stream) => {
+ // Send messages to client
+ await stream.send({ data: "message" });
+ await stream.close();
+ }
+);
+
+
+
+
+ Bidirectional streaming
+
+export const chatStream = api.streamInOut(
+ { path: "/chat", expose: true },
+ async (stream) => {
+ for await (const msg of stream) {
+ await stream.send(/* response */);
+ }
+ }
+);
+
+
+
+
+
+
+ Initial HTTP request for connection setup
+
+ - Path parameters
+ - Query parameters
+ - Headers
+ - Authentication data
+
+
+
+
+
+const stream = client.serviceName.endpointName();
+await stream.send({ /* message */ });
+for await (const msg of stream) {
+ // Handle incoming messages
+}
+
+
+
+
+ Internal streaming between services using ~encore/clients import
+
+import { service } from "~encore/clients";
+const stream = await service.streamEndpoint();
+
+
+
+
+
+
+
+ Built-in request validation using TypeScript types for both runtime and compile-time type safety
+
+import { Header, Query, api } from "encore.dev/api";
+
+interface Request {
+ limit?: Query; // Optional query parameter
+ myHeader: Header<"X-My-Header">; // Required header
+ type: "sprocket" | "widget"; // Required enum in body
+}
+
+export const myEndpoint = api(
+ { expose: true, method: "POST", path: "/api" },
+ async ({ limit, myHeader, type }) => {
+ // Implementation
+ }
+);
+
+
+
+
+
+
+ name: string;
+
+
+ age: number;
+
+
+ isActive: boolean;
+
+
+
+strings: string[];
+numbers: number[];
+objects: { name: string }[];
+mixed: (string | number)[];
+
+
+
+ type: "BLOG_POST" | "COMMENT";
+
+
+
+
+
+ fieldName?: type;
+ name?: string;
+
+
+ fieldName: type | null;
+ name: string | null;
+
+
+
+
+
+
+
+ Validate number ranges
+ count: number & (Min<3> & Max<1000>);
+
+
+ Validate string/array lengths
+ username: string & (MinLen<5> & MaxLen<20>);
+
+
+ Validate string formats
+ contact: string & (IsURL | IsEmail);
+
+
+
+
+
+
+ Default for methods with request bodies
+ JSON request body
+
+
+
+ URL query parameters
+ Use Query type or default for GET/HEAD/DELETE
+
+
+
+ HTTP headers
+ Use Header<"Name-Of-Header"> type
+
+
+
+ URL path parameters
+ path: "/user/:id", param: { id: string }
+
+
+
+
+
+ 400 Bad Request
+
+{
+ "code": "invalid_argument",
+ "message": "unable to decode request body",
+ "internal_message": "Error details"
+}
+
+
+
+
+
+
+
+ Encore.ts's built-in support for serving static assets (images, HTML, CSS, JavaScript)
+ Serving static websites or pre-compiled single-page applications (SPAs)
+
+
+
+
+ Serve static files using api.static function
+
+import { api } from "encore.dev/api";
+export const assets = api.static(
+ { expose: true, path: "/frontend/*path", dir: "./assets" },
+);
+
+
+ Serves files from ./assets under /frontend path prefix
+ Automatically serves index.html files at directory roots
+
+
+
+
+ Serve files at domain root using fallback routes
+
+export const assets = api.static(
+ { expose: true, path: "/!path", dir: "./assets" },
+);
+
+ Uses !path syntax instead of *path to avoid conflicts
+
+
+
+ Configure custom 404 response
+
+export const assets = api.static(
+ {
+ expose: true,
+ path: "/!path",
+ dir: "./assets",
+ notFound: "./not_found.html"
+ },
+);
+
+
+
+
+
+
+
+
+Encore.ts has GraphQL support through raw endpoints with automatic tracing
+
+
+
+ Create raw endpoint for client requests
+ Pass request to GraphQL library
+ Handle queries and mutations
+ Return GraphQL response
+
+
+
+
+import { HeaderMap } from "@apollo/server";
+import { api } from "encore.dev/api";
+const { ApolloServer, gql } = require("apollo-server");
+import { json } from "node:stream/consumers";
+
+const server = new ApolloServer({ typeDefs, resolvers });
+await server.start();
+
+export const graphqlAPI = api.raw(
+ { expose: true, path: "/graphql", method: "*" },
+ async (req, res) => {
+ server.assertStarted("/graphql");
+
+ const headers = new HeaderMap();
+ for (const [key, value] of Object.entries(req.headers)) {
+ if (value !== undefined) {
+ headers.set(key, Array.isArray(value) ? value.join(", ") : value);
+ }
+ }
+
+ const httpGraphQLResponse = await server.executeHTTPGraphQLRequest({
+ httpGraphQLRequest: {
+ headers,
+ method: req.method!.toUpperCase(),
+ body: await json(req),
+ search: new URLSearchParams(req.url ?? "").toString(),
+ },
+ context: async () => ({ req, res }),
+ });
+
+ // Set response headers and status
+ for (const [key, value] of httpGraphQLResponse.headers) {
+ res.setHeader(key, value);
+ }
+ res.statusCode = httpGraphQLResponse.status || 200;
+
+ // Write response
+ if (httpGraphQLResponse.body.kind === "complete") {
+ res.end(httpGraphQLResponse.body.string);
+ return;
+ }
+
+ for await (const chunk of httpGraphQLResponse.body.asyncIterator) {
+ res.write(chunk);
+ }
+ res.end();
+ }
+);
+
+
+
+
+
+
+
+
+type Query {
+ books: [Book]
+}
+
+type Book {
+ title: String!
+ author: String!
+}
+
+
+
+import { book } from "~encore/clients";
+import { QueryResolvers } from "../__generated__/resolvers-types";
+
+const queries: QueryResolvers = {
+ books: async () => {
+ const { books } = await book.list();
+ return books;
+ },
+};
+
+
+
+import { api } from "encore.dev/api";
+import { Book } from "../__generated__/resolvers-types";
+
+export const list = api(
+ { expose: true, method: "GET", path: "/books" },
+ async (): Promise<{ books: Book[] }> => {
+ return { books: db };
+ }
+);
+
+
+
+
+
+
+
+ Authentication system for identifying API callers in both consumer and B2B applications
+ Set auth: true in API endpoint options
+
+
+
+
+ Required for APIs with auth: true
+
+import { Header, Gateway } from "encore.dev/api";
+import { authHandler } from "encore.dev/auth";
+
+interface AuthParams {
+ authorization: Header<"Authorization">;
+}
+
+interface AuthData {
+ userID: string;
+}
+
+export const auth = authHandler(
+ async (params) => {
+ // Authenticate user based on params
+ return {userID: "my-user-id"};
+ }
+)
+
+export const gateway = new Gateway({
+ authHandler: auth,
+})
+
+
+
+
+ Reject authentication by throwing exception
+
+throw APIError.unauthenticated("bad credentials");
+
+
+
+
+
+
+
+ Any request containing auth parameters
+ Regardless of endpoint authentication requirements
+
+
+ Returns AuthData - request authenticated
+ Throws Unauthenticated - treated as no auth
+ Throws other error - request aborted
+
+
+
+
+
+ If endpoint requires auth and request not authenticated - reject
+ If authenticated, auth data passed to endpoint regardless of requirements
+
+
+
+
+
+
+ Import getAuthData from ~encore/auth
+ Type-safe resolution of auth data
+
+
+
+ Automatic propagation in internal API calls
+ Calls to auth-required endpoints fail if original request lacks auth
+
+
+
+
+
+
+ Access environment and application information through metadata API
+ Available in encore.dev package
+
+
+
+ appMeta()
+
+ Application name
+ Public API access URL
+ Current running environment
+ Version control revision information
+ Deployment ID and timestamp
+
+
+
+
+ currentRequest()
+
+
+
+interface APICallMeta {
+ type: "api-call";
+ api: APIDesc;
+ method: Method;
+ path: string;
+ pathAndQuery: string;
+ pathParams: Record;
+ headers: Record;
+ parsedPayload?: Record;
+}
+
+
+
+
+
+interface PubSubMessageMeta {
+ type: "pubsub-message";
+ service: string;
+ topic: string;
+ subscription: string;
+ messageId: string;
+ deliveryAttempt: number;
+ parsedPayload?: Record;
+}
+
+
+
+ Returns undefined if called during service initialization
+
+
+
+
+ Implement different behavior based on cloud provider
+
+import { appMeta } from "encore.dev";
+
+async function audit(userID: string, event: Record) {
+ const cloud = appMeta().environment.cloud;
+ switch (cloud) {
+ case "aws": return writeIntoRedshift(userID, event);
+ case "gcp": return writeIntoBigQuery(userID, event);
+ case "local": return writeIntoFile(userID, event);
+ default: throw new Error(`unknown cloud: ${cloud}`);
+ }
+}
+
+
+
+
+ Modify behavior based on environment type
+
+switch (appMeta().environment.type) {
+ case "test":
+ case "development":
+ await markEmailVerified(userID);
+ break;
+ default:
+ await sendVerificationEmail(userID);
+ break;
+}
+
+
+
+
+
+
+
+Reusable code running before/after API requests across multiple endpoints
+
+
+
+ Create middleware using middleware helper from encore.dev/api
+
+import { middleware } from "encore.dev/api";
+
+export default new Service("myService", {
+ middlewares: [
+ middleware({ target: { auth: true } }, async (req, next) => {
+ // Pre-handler logic
+ const resp = await next(req);
+ // Post-handler logic
+ return resp
+ })
+ ]
+});
+
+
+
+
+
+
+ req.requestMeta
+
+
+ req.requestMeta
+ req.stream
+
+
+ req.rawRequest
+ req.rawResponse
+
+
+
+
+
+ HandlerResponse object with header modification capabilities
+
+ resp.header.set(key, value)
+ resp.header.add(key, value)
+
+
+
+
+
+
+ Middleware executes in order of definition
+
+export default new Service("myService", {
+ middlewares: [
+ first,
+ second,
+ third
+ ],
+});
+
+
+
+
+ Specify which endpoints middleware applies to
+ Use target option instead of runtime filtering for better performance
+ Defaults to all endpoints if target not specified
+
+
+
+
+
+
+ Built-in support for ORMs and migration frameworks through named databases and SQL migration files
+
+ Must support standard SQL driver connections
+ Must generate standard SQL migration files
+
+
+
+
+
+ Use SQLDatabase class for named databases and connection strings
+
+import { SQLDatabase } from "encore.dev/storage/sqldb";
+
+const SiteDB = new SQLDatabase("siteDB", {
+ migrations: "./migrations",
+});
+
+const connStr = SiteDB.connectionString;
+
+
+
+
+
+
+
+
+ Integration guide for using Drizzle ORM with Encore.ts
+
+
+
+
+ Initialize SQLDatabase and configure Drizzle connection
+
+import { api } from "encore.dev/api";
+import { SQLDatabase } from "encore.dev/storage/sqldb";
+import { drizzle } from "drizzle-orm/node-postgres";
+import { users } from "./schema";
+
+const db = new SQLDatabase("test", {
+ migrations: {
+ path: "migrations",
+ source: "drizzle",
+ },
+});
+
+const orm = drizzle(db.connectionString);
+await orm.select().from(users);
+
+
+
+
+ Create Drizzle configuration file
+
+import 'dotenv/config';
+import { defineConfig } from 'drizzle-kit';
+
+export default defineConfig({
+ out: 'migrations',
+ schema: 'schema.ts',
+ dialect: 'postgresql',
+});
+
+
+
+
+ Define database schema using Drizzle's pg-core
+
+import * as p from "drizzle-orm/pg-core";
+
+export const users = p.pgTable("users", {
+ id: p.serial().primaryKey(),
+ name: p.text(),
+ email: p.text().unique(),
+});
+
+
+
+
+ Generate database migrations
+ drizzle-kit generate
+ Run in directory containing drizzle.config.ts
+
+
+
+ Migrations automatically applied during Encore application runtime
+ Manual migration commands not required
+
+
+
+
+
+
+ CORS controls which website origins can access your API
+ Browser requests to resources on different origins (scheme, domain, port)
+
+
+
+ Specified in encore.app file under global_cors key
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Allows unauthenticated requests from all origins
+ Disallows authenticated requests from other origins
+ All origins allowed in local development
+
+
+
+
+
+ Encore automatically configures headers through static analysis
+ Request or response types containing header fields
+
+
+
+ Additional headers can be configured via allow_headers and expose_headers
+ Custom headers in raw endpoints not detected by static analysis
+
+
+
+
+
+
+Built-in structured logging combining free-form messages with type-safe key-value pairs
+
+
+
+ import log from "encore.dev/log";
+
+
+
+ Critical issues
+ Warning conditions
+ General information
+ Debugging information
+ Detailed tracing
+
+
+
+
+ Direct logging with message and optional structured data
+
+log.info("log message", {is_subscriber: true})
+log.error(err, "something went terribly wrong!")
+
+
+
+
+ Group logs with shared key-value pairs
+
+const logger = log.with({is_subscriber: true})
+logger.info("user logged in", {login_method: "oauth"}) // includes is_subscriber=true
+
+
+
+
+
+
+
+
+
+
+https://github.com/encoredev/examples/tree/main/ts/hello-world
+
+
+
+https://github.com/encoredev/examples/tree/main/ts/url-shortener
+
+
+
+https://github.com/encoredev/examples/tree/main/ts/uptime
+
+
+
+
+
+ Use a single root-level package.json file (monorepo approach) for Encore.ts projects including frontend dependencies
+
+ Separate package.json files in sub-packages
+
+ Encore.ts application must use one package with a single package.json file
+ Other separate packages must be pre-transpiled to JavaScript
+
+
+
+
+
+
+
+
+
+encore run [--debug] [--watch=true] [flags]
+Runs your application
+
+
+
+
+
+encore app clone [app-id] [directory]
+Clone an Encore app to your computer
+
+
+
+encore app create [name]
+Create a new Encore app
+
+
+
+encore app init [name]
+Create new app from existing repository
+
+
+
+encore app link [app-id]
+Link app with server
+
+
+
+
+
+encore auth login
+Log in to Encore
+
+
+
+encore auth logout
+Logs out current user
+
+
+
+encore auth signup
+Create new account
+
+
+
+encore auth whoami
+Show current user
+
+
+
+
+
+encore daemon
+Restart daemon for unexpected behavior
+
+
+
+encore daemon env
+Output environment information
+
+
+
+
+
+encore db shell database-name [--env=name]
+Connect via psql shell
+--write, --admin, --superuser flags available
+
+
+
+encore db conn-uri database-name [--env=name]
+Output connection string
+
+
+
+encore db proxy [--env=name]
+Set up local database connection proxy
+
+
+
+encore db reset [service-names...]
+Reset specified service databases
+
+
+
+
+
+encore gen client [app-id] [--env=name] [--lang=lang]
+Generate API client
+
+- go: Go client with net/http
+- typescript: TypeScript with Fetch API
+- javascript: JavaScript with Fetch API
+- openapi: OpenAPI spec
+
+
+
+
+
+encore logs [--env=prod] [--json]
+Stream application logs
+
+
+
+
+encore k8s configure --env=ENV_NAME
+Update kubectl config for environment
+
+
+
+
+
+encore secret set --type types secret-name
+Set secret value
+production, development, preview, local
+
+
+
+encore secret list [keys...]
+List secrets
+
+
+
+encore secret archive id
+Archive secret value
+
+
+
+encore secret unarchive id
+Unarchive secret value
+
+
+
+
+
+encore version
+Report current version
+
+
+
+encore version update
+Check and apply updates
+
+
+
+
+
+encore vpn start
+Set up secure connection to private environments
+
+
+
+encore vpn status
+Check VPN connection status
+
+
+
+encore vpn stop
+Stop VPN connection
+
+
+
+
+
+encore build docker
+Build portable Docker image
+
+- --base string: Define base image
+- --push: Push to remote repository
+
+
+
+
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..a7e90b1
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,61 @@
+name: CI
+
+on:
+ pull_request:
+ branches: [main]
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+
+ services:
+ postgres:
+ image: postgres:15
+ env:
+ POSTGRES_PASSWORD: postgres
+ POSTGRES_DB: test
+ options: >-
+ --health-cmd pg_isready
+ --health-interval 10s
+ --health-timeout 5s
+ --health-retries 5
+ ports:
+ - 5432:5432
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: oven-sh/setup-bun@v1
+ with:
+ bun-version: latest
+
+ - name: Install Encore CLI
+ run: |
+ curl -L https://encore.dev/install.sh | bash
+ echo "$HOME/.encore/bin" >> $GITHUB_PATH
+
+ - name: Install backend dependencies
+ working-directory: ./backend
+ run: bun install
+
+ - name: Run backend tests
+ working-directory: ./backend
+ run: encore test
+ env:
+ DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test
+
+ - name: Build backend
+ working-directory: ./backend
+ run: encore build
+
+ - name: Install frontend dependencies
+ working-directory: ./frontend
+ run: bun install
+
+ - name: Run frontend tests
+ working-directory: ./frontend
+ run: bun test
+
+ - name: Build frontend
+ working-directory: ./frontend
+ run: bun run build
diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md
index 450716a..e2a592d 100644
--- a/DEVELOPMENT.md
+++ b/DEVELOPMENT.md
@@ -125,7 +125,4 @@ git push origin main
- [Encore Documentation](https://encore.dev/docs)
- [Deployment Guide](https://encore.dev/docs/platform/deploy/deploying)
- [GitHub Integration](https://encore.dev/docs/platform/integrations/github)
-- [Encore Cloud Dashboard](https://app.encore.dev)
-
-
-
+- [Encore Cloud Dashboard](https://app.encore.dev)
\ No newline at end of file
diff --git a/backend/.gitignore b/backend/.gitignore
new file mode 100644
index 0000000..bed4522
--- /dev/null
+++ b/backend/.gitignore
@@ -0,0 +1,4 @@
+encore.gen.go
+encore.gen.cue
+/.encore
+/encore.gen
diff --git a/bun.lock b/bun.lock
new file mode 100644
index 0000000..32115f2
--- /dev/null
+++ b/bun.lock
@@ -0,0 +1,710 @@
+{
+ "lockfileVersion": 1,
+ "workspaces": {
+ "": {
+ "name": "leap-app",
+ },
+ "backend": {
+ "name": "backend",
+ "version": "1.0.0",
+ "dependencies": {
+ "encore.dev": "^1.50.6",
+ },
+ "devDependencies": {
+ "typescript": "^^5.8.3",
+ "vitest": "^^3.0.9",
+ },
+ },
+ "frontend": {
+ "name": "frontend",
+ "version": "1.0.0",
+ "dependencies": {
+ "@radix-ui/react-label": "^^2.1.2",
+ "@radix-ui/react-slot": "^^1.2.3",
+ "@radix-ui/react-toast": "^^1.2.14",
+ "@tailwindcss/vite": "^^4.1.11",
+ "class-variance-authority": "^^0.7.1",
+ "clsx": "^^2.1.1",
+ "encore.dev": "^1.50.6",
+ "lucide-react": "^^0.484.0",
+ "react": "^^19.1.0",
+ "react-dom": "^^19.1.0",
+ "react-markdown": "^^10.1.0",
+ "remark-gfm": "^^4.0.1",
+ "tailwind-merge": "^^3.2.0",
+ "tailwindcss": "^^4.1.11",
+ },
+ "devDependencies": {
+ "@tailwindcss/oxide": "^^4.1.11",
+ "@tailwindcss/vite": "^^4.1.11",
+ "@types/react": "^^19.2.2",
+ "@types/react-dom": "^^19.1.6",
+ "@vitejs/plugin-react": "^^4.6.0",
+ "lightningcss": "^^1.29.2",
+ "tw-animate-css": "^^1.3.4",
+ "typescript": "^^5.8.3",
+ "vite": "^^6.2.5",
+ },
+ "optionalDependencies": {
+ "rollup": "^^4.44.1",
+ },
+ },
+ },
+ "packages": {
+ "@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
+
+ "@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
+
+ "@babel/compat-data": ["@babel/compat-data@7.28.5", "", {}, "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA=="],
+
+ "@babel/core": ["@babel/core@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.4", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw=="],
+
+ "@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
+
+ "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.2", "", { "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ=="],
+
+ "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="],
+
+ "@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="],
+
+ "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.3", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", "@babel/traverse": "^7.28.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw=="],
+
+ "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.27.1", "", {}, "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw=="],
+
+ "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="],
+
+ "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="],
+
+ "@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="],
+
+ "@babel/helpers": ["@babel/helpers@7.28.4", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.28.4" } }, "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w=="],
+
+ "@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
+
+ "@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw=="],
+
+ "@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw=="],
+
+ "@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
+
+ "@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="],
+
+ "@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
+
+ "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.11", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg=="],
+
+ "@esbuild/android-arm": ["@esbuild/android-arm@0.25.11", "", { "os": "android", "cpu": "arm" }, "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg=="],
+
+ "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.11", "", { "os": "android", "cpu": "arm64" }, "sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ=="],
+
+ "@esbuild/android-x64": ["@esbuild/android-x64@0.25.11", "", { "os": "android", "cpu": "x64" }, "sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g=="],
+
+ "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w=="],
+
+ "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ=="],
+
+ "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.11", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA=="],
+
+ "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.11", "", { "os": "freebsd", "cpu": "x64" }, "sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw=="],
+
+ "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.11", "", { "os": "linux", "cpu": "arm" }, "sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw=="],
+
+ "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA=="],
+
+ "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.11", "", { "os": "linux", "cpu": "ia32" }, "sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw=="],
+
+ "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.11", "", { "os": "linux", "cpu": "none" }, "sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw=="],
+
+ "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.11", "", { "os": "linux", "cpu": "none" }, "sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ=="],
+
+ "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.11", "", { "os": "linux", "cpu": "ppc64" }, "sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw=="],
+
+ "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.11", "", { "os": "linux", "cpu": "none" }, "sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww=="],
+
+ "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.11", "", { "os": "linux", "cpu": "s390x" }, "sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw=="],
+
+ "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.11", "", { "os": "linux", "cpu": "x64" }, "sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ=="],
+
+ "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.11", "", { "os": "none", "cpu": "arm64" }, "sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg=="],
+
+ "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.11", "", { "os": "none", "cpu": "x64" }, "sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A=="],
+
+ "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.11", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg=="],
+
+ "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.11", "", { "os": "openbsd", "cpu": "x64" }, "sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw=="],
+
+ "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.11", "", { "os": "none", "cpu": "arm64" }, "sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ=="],
+
+ "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.11", "", { "os": "sunos", "cpu": "x64" }, "sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA=="],
+
+ "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q=="],
+
+ "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.11", "", { "os": "win32", "cpu": "ia32" }, "sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA=="],
+
+ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.11", "", { "os": "win32", "cpu": "x64" }, "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA=="],
+
+ "@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/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
+
+ "@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.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
+
+ "@radix-ui/primitive": ["@radix-ui/primitive@1.1.2", "", {}, "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA=="],
+
+ "@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="],
+
+ "@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
+
+ "@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="],
+
+ "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.10", "", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ=="],
+
+ "@radix-ui/react-label": ["@radix-ui/react-label@2.1.2", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-zo1uGMTaNlHehDyFQcDZXRJhUPDuukcnHz0/jnrup0JA6qL+AFpAnty+7VKa9esuU5xTblAZzTGYJKSKaBxBhw=="],
+
+ "@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ=="],
+
+ "@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA=="],
+
+ "@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.0.2", "", { "dependencies": { "@radix-ui/react-slot": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w=="],
+
+ "@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
+
+ "@radix-ui/react-toast": ["@radix-ui/react-toast@1.2.14", "", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.10", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.4", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-nAP5FBxBJGQ/YfUB+r+O6USFVkWq3gAInkxyEnmvEV5jtSbfDhfa4hwX8CraCnbjMLsE7XSf/K75l9xXY7joWg=="],
+
+ "@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="],
+
+ "@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="],
+
+ "@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA=="],
+
+ "@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.1", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g=="],
+
+ "@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
+
+ "@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.2.3", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug=="],
+
+ "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.19", "", {}, "sha512-3FL3mnMbPu0muGOCaKAhhFEYmqv9eTfPSJRJmANrCwtgK8VuxpsZDGK+m0LYAGoyO8+0j5uRe4PeyPDK1yA/hA=="],
+
+ "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.44.1", "", { "os": "android", "cpu": "arm" }, "sha512-JAcBr1+fgqx20m7Fwe1DxPUl/hPkee6jA6Pl7n1v2EFiktAHenTaXl5aIFjUIEsfn9w3HE4gK1lEgNGMzBDs1w=="],
+
+ "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.44.1", "", { "os": "android", "cpu": "arm64" }, "sha512-RurZetXqTu4p+G0ChbnkwBuAtwAbIwJkycw1n6GvlGlBuS4u5qlr5opix8cBAYFJgaY05TWtM+LaoFggUmbZEQ=="],
+
+ "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.44.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-fM/xPesi7g2M7chk37LOnmnSTHLG/v2ggWqKj3CCA1rMA4mm5KVBT1fNoswbo1JhPuNNZrVwpTvlCVggv8A2zg=="],
+
+ "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.44.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-gDnWk57urJrkrHQ2WVx9TSVTH7lSlU7E3AFqiko+bgjlh78aJ88/3nycMax52VIVjIm3ObXnDL2H00e/xzoipw=="],
+
+ "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.44.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-wnFQmJ/zPThM5zEGcnDcCJeYJgtSLjh1d//WuHzhf6zT3Md1BvvhJnWoy+HECKu2bMxaIcfWiu3bJgx6z4g2XA=="],
+
+ "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.44.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-uBmIxoJ4493YATvU2c0upGz87f99e3wop7TJgOA/bXMFd2SvKCI7xkxY/5k50bv7J6dw1SXT4MQBQSLn8Bb/Uw=="],
+
+ "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.44.1", "", { "os": "linux", "cpu": "arm" }, "sha512-n0edDmSHlXFhrlmTK7XBuwKlG5MbS7yleS1cQ9nn4kIeW+dJH+ExqNgQ0RrFRew8Y+0V/x6C5IjsHrJmiHtkxQ=="],
+
+ "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.44.1", "", { "os": "linux", "cpu": "arm" }, "sha512-8WVUPy3FtAsKSpyk21kV52HCxB+me6YkbkFHATzC2Yd3yuqHwy2lbFL4alJOLXKljoRw08Zk8/xEj89cLQ/4Nw=="],
+
+ "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.44.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-yuktAOaeOgorWDeFJggjuCkMGeITfqvPgkIXhDqsfKX8J3jGyxdDZgBV/2kj/2DyPaLiX6bPdjJDTu9RB8lUPQ=="],
+
+ "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.44.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-W+GBM4ifET1Plw8pdVaecwUgxmiH23CfAUj32u8knq0JPFyK4weRy6H7ooxYFD19YxBulL0Ktsflg5XS7+7u9g=="],
+
+ "@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.44.1", "", { "os": "linux", "cpu": "none" }, "sha512-1zqnUEMWp9WrGVuVak6jWTl4fEtrVKfZY7CvcBmUUpxAJ7WcSowPSAWIKa/0o5mBL/Ij50SIf9tuirGx63Ovew=="],
+
+ "@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.44.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-Rl3JKaRu0LHIx7ExBAAnf0JcOQetQffaw34T8vLlg9b1IhzcBgaIdnvEbbsZq9uZp3uAH+JkHd20Nwn0h9zPjA=="],
+
+ "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.44.1", "", { "os": "linux", "cpu": "none" }, "sha512-j5akelU3snyL6K3N/iX7otLBIl347fGwmd95U5gS/7z6T4ftK288jKq3A5lcFKcx7wwzb5rgNvAg3ZbV4BqUSw=="],
+
+ "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.44.1", "", { "os": "linux", "cpu": "none" }, "sha512-ppn5llVGgrZw7yxbIm8TTvtj1EoPgYUAbfw0uDjIOzzoqlZlZrLJ/KuiE7uf5EpTpCTrNt1EdtzF0naMm0wGYg=="],
+
+ "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.44.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-Hu6hEdix0oxtUma99jSP7xbvjkUM/ycke/AQQ4EC5g7jNRLLIwjcNwaUy95ZKBJJwg1ZowsclNnjYqzN4zwkAw=="],
+
+ "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.44.1", "", { "os": "linux", "cpu": "x64" }, "sha512-EtnsrmZGomz9WxK1bR5079zee3+7a+AdFlghyd6VbAjgRJDbTANJ9dcPIPAi76uG05micpEL+gPGmAKYTschQw=="],
+
+ "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.44.1", "", { "os": "linux", "cpu": "x64" }, "sha512-iAS4p+J1az6Usn0f8xhgL4PaU878KEtutP4hqw52I4IO6AGoyOkHCxcc4bqufv1tQLdDWFx8lR9YlwxKuv3/3g=="],
+
+ "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.44.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-NtSJVKcXwcqozOl+FwI41OH3OApDyLk3kqTJgx8+gp6On9ZEt5mYhIsKNPGuaZr3p9T6NWPKGU/03Vw4CNU9qg=="],
+
+ "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.44.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-JYA3qvCOLXSsnTR3oiyGws1Dm0YTuxAAeaYGVlGpUsHqloPcFjPg+X0Fj2qODGLNwQOAcCiQmHub/V007kiH5A=="],
+
+ "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.44.1", "", { "os": "win32", "cpu": "x64" }, "sha512-J8o22LuF0kTe7m+8PvW9wk3/bRq5+mRo5Dqo6+vXb7otCm3TPhYOJqOaQtGU9YMWQSL3krMnoOxMr0+9E6F3Ug=="],
+
+ "@tailwindcss/node": ["@tailwindcss/node@4.1.11", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", "lightningcss": "1.30.1", "magic-string": "^0.30.17", "source-map-js": "^1.2.1", "tailwindcss": "4.1.11" } }, "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q=="],
+
+ "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.11", "", { "dependencies": { "detect-libc": "^2.0.4", "tar": "^7.4.3" }, "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.11", "@tailwindcss/oxide-darwin-arm64": "4.1.11", "@tailwindcss/oxide-darwin-x64": "4.1.11", "@tailwindcss/oxide-freebsd-x64": "4.1.11", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.11", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.11", "@tailwindcss/oxide-linux-arm64-musl": "4.1.11", "@tailwindcss/oxide-linux-x64-gnu": "4.1.11", "@tailwindcss/oxide-linux-x64-musl": "4.1.11", "@tailwindcss/oxide-wasm32-wasi": "4.1.11", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.11", "@tailwindcss/oxide-win32-x64-msvc": "4.1.11" } }, "sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg=="],
+
+ "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.11", "", { "os": "android", "cpu": "arm64" }, "sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg=="],
+
+ "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ=="],
+
+ "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw=="],
+
+ "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.11", "", { "os": "freebsd", "cpu": "x64" }, "sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA=="],
+
+ "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.11", "", { "os": "linux", "cpu": "arm" }, "sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg=="],
+
+ "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ=="],
+
+ "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ=="],
+
+ "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.11", "", { "os": "linux", "cpu": "x64" }, "sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg=="],
+
+ "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.11", "", { "os": "linux", "cpu": "x64" }, "sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q=="],
+
+ "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.11", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@emnapi/wasi-threads": "^1.0.2", "@napi-rs/wasm-runtime": "^0.2.11", "@tybys/wasm-util": "^0.9.0", "tslib": "^2.8.0" }, "cpu": "none" }, "sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g=="],
+
+ "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w=="],
+
+ "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.11", "", { "os": "win32", "cpu": "x64" }, "sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg=="],
+
+ "@tailwindcss/vite": ["@tailwindcss/vite@4.1.11", "", { "dependencies": { "@tailwindcss/node": "4.1.11", "@tailwindcss/oxide": "4.1.11", "tailwindcss": "4.1.11" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-RHYhrR3hku0MJFRV+fN2gNbDNEh3dwKvY8XJvTxCSXeMOsCRSr+uKvDWQcbizrHgjML6ZmTE5OwMrl5wKcujCw=="],
+
+ "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="],
+
+ "@types/babel__generator": ["@types/babel__generator@7.27.0", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="],
+
+ "@types/babel__template": ["@types/babel__template@7.4.4", "", { "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A=="],
+
+ "@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="],
+
+ "@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="],
+
+ "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
+
+ "@types/estree-jsx": ["@types/estree-jsx@1.0.5", "", { "dependencies": { "@types/estree": "*" } }, "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg=="],
+
+ "@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="],
+
+ "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="],
+
+ "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="],
+
+ "@types/react": ["@types/react@19.2.2", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA=="],
+
+ "@types/react-dom": ["@types/react-dom@19.1.6", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw=="],
+
+ "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
+
+ "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="],
+
+ "@vitejs/plugin-react": ["@vitejs/plugin-react@4.6.0", "", { "dependencies": { "@babel/core": "^7.27.4", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.19", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" } }, "sha512-5Kgff+m8e2PB+9j51eGHEpn5kUzRKH2Ry0qGoe8ItJg7pqnkPrYPkDQZGgGmTa0EGarHrkjLvOdU3b1fzI8otQ=="],
+
+ "@vitest/expect": ["@vitest/expect@3.0.9", "", { "dependencies": { "@vitest/spy": "3.0.9", "@vitest/utils": "3.0.9", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-5eCqRItYgIML7NNVgJj6TVCmdzE7ZVgJhruW0ziSQV4V7PvLkDL1bBkBdcTs/VuIz0IxPb5da1IDSqc1TR9eig=="],
+
+ "@vitest/mocker": ["@vitest/mocker@3.0.9", "", { "dependencies": { "@vitest/spy": "3.0.9", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0 || ^6.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-ryERPIBOnvevAkTq+L1lD+DTFBRcjueL9lOUfXsLfwP92h4e+Heb+PjiqS3/OURWPtywfafK0kj++yDFjWUmrA=="],
+
+ "@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="],
+
+ "@vitest/runner": ["@vitest/runner@3.0.9", "", { "dependencies": { "@vitest/utils": "3.0.9", "pathe": "^2.0.3" } }, "sha512-NX9oUXgF9HPfJSwl8tUZCMP1oGx2+Sf+ru6d05QjzQz4OwWg0psEzwY6VexP2tTHWdOkhKHUIZH+fS6nA7jfOw=="],
+
+ "@vitest/snapshot": ["@vitest/snapshot@3.0.9", "", { "dependencies": { "@vitest/pretty-format": "3.0.9", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-AiLUiuZ0FuA+/8i19mTYd+re5jqjEc2jZbgJ2up0VY0Ddyyxg/uUtBDpIFAy4uzKaQxOW8gMgBdAJJ2ydhu39A=="],
+
+ "@vitest/spy": ["@vitest/spy@3.0.9", "", { "dependencies": { "tinyspy": "^3.0.2" } }, "sha512-/CcK2UDl0aQ2wtkp3YVWldrpLRNCfVcIOFGlVGKO4R5eajsH393Z1yiXLVQ7vWsj26JOEjeZI0x5sm5P4OGUNQ=="],
+
+ "@vitest/utils": ["@vitest/utils@3.0.9", "", { "dependencies": { "@vitest/pretty-format": "3.0.9", "loupe": "^3.1.3", "tinyrainbow": "^2.0.0" } }, "sha512-ilHM5fHhZ89MCp5aAaM9uhfl1c2JdxVxl3McqsdVyVNN6JffnEen8UMCdRTzOhGXNQGo5GNL9QugHrz727Wnng=="],
+
+ "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="],
+
+ "backend": ["backend@workspace:backend"],
+
+ "bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="],
+
+ "baseline-browser-mapping": ["baseline-browser-mapping@2.8.20", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-JMWsdF+O8Orq3EMukbUN1QfbLK9mX2CkUmQBcW2T0s8OmdAUL5LLM/6wFwSrqXzlXB13yhyK9gTKS1rIizOduQ=="],
+
+ "browserslist": ["browserslist@4.27.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.19", "caniuse-lite": "^1.0.30001751", "electron-to-chromium": "^1.5.238", "node-releases": "^2.0.26", "update-browserslist-db": "^1.1.4" }, "bin": { "browserslist": "cli.js" } }, "sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw=="],
+
+ "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="],
+
+ "caniuse-lite": ["caniuse-lite@1.0.30001751", "", {}, "sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw=="],
+
+ "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="],
+
+ "chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="],
+
+ "character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="],
+
+ "character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="],
+
+ "character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="],
+
+ "character-reference-invalid": ["character-reference-invalid@2.0.1", "", {}, "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw=="],
+
+ "check-error": ["check-error@2.1.1", "", {}, "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw=="],
+
+ "chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="],
+
+ "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="],
+
+ "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
+
+ "comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="],
+
+ "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
+
+ "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
+
+ "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
+
+ "decode-named-character-reference": ["decode-named-character-reference@1.2.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q=="],
+
+ "deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="],
+
+ "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
+
+ "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
+
+ "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="],
+
+ "electron-to-chromium": ["electron-to-chromium@1.5.240", "", {}, "sha512-OBwbZjWgrCOH+g6uJsA2/7Twpas2OlepS9uvByJjR2datRDuKGYeD+nP8lBBks2qnB7bGJNHDUx7c/YLaT3QMQ=="],
+
+ "encore.dev": ["encore.dev@1.50.6", "", {}, "sha512-pTsG3L9wfJJxmBOSZnLDtAL3ezdZn9tOcrJOq1de+VFYE1C2oCM/iHjv3vjXhM8ELml2zdvjnUlL47Et2kZKaw=="],
+
+ "enhanced-resolve": ["enhanced-resolve@5.18.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww=="],
+
+ "es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="],
+
+ "esbuild": ["esbuild@0.25.11", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.11", "@esbuild/android-arm": "0.25.11", "@esbuild/android-arm64": "0.25.11", "@esbuild/android-x64": "0.25.11", "@esbuild/darwin-arm64": "0.25.11", "@esbuild/darwin-x64": "0.25.11", "@esbuild/freebsd-arm64": "0.25.11", "@esbuild/freebsd-x64": "0.25.11", "@esbuild/linux-arm": "0.25.11", "@esbuild/linux-arm64": "0.25.11", "@esbuild/linux-ia32": "0.25.11", "@esbuild/linux-loong64": "0.25.11", "@esbuild/linux-mips64el": "0.25.11", "@esbuild/linux-ppc64": "0.25.11", "@esbuild/linux-riscv64": "0.25.11", "@esbuild/linux-s390x": "0.25.11", "@esbuild/linux-x64": "0.25.11", "@esbuild/netbsd-arm64": "0.25.11", "@esbuild/netbsd-x64": "0.25.11", "@esbuild/openbsd-arm64": "0.25.11", "@esbuild/openbsd-x64": "0.25.11", "@esbuild/openharmony-arm64": "0.25.11", "@esbuild/sunos-x64": "0.25.11", "@esbuild/win32-arm64": "0.25.11", "@esbuild/win32-ia32": "0.25.11", "@esbuild/win32-x64": "0.25.11" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q=="],
+
+ "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
+
+ "escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
+
+ "estree-util-is-identifier-name": ["estree-util-is-identifier-name@3.0.0", "", {}, "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg=="],
+
+ "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
+
+ "expect-type": ["expect-type@1.2.2", "", {}, "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA=="],
+
+ "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="],
+
+ "frontend": ["frontend@workspace:frontend"],
+
+ "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
+
+ "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
+
+ "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
+
+ "hast-util-to-jsx-runtime": ["hast-util-to-jsx-runtime@2.3.6", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "hast-util-whitespace": "^3.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "style-to-js": "^1.0.0", "unist-util-position": "^5.0.0", "vfile-message": "^4.0.0" } }, "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg=="],
+
+ "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="],
+
+ "html-url-attributes": ["html-url-attributes@3.0.1", "", {}, "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ=="],
+
+ "inline-style-parser": ["inline-style-parser@0.2.4", "", {}, "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q=="],
+
+ "is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="],
+
+ "is-alphanumerical": ["is-alphanumerical@2.0.1", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw=="],
+
+ "is-decimal": ["is-decimal@2.0.1", "", {}, "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A=="],
+
+ "is-hexadecimal": ["is-hexadecimal@2.0.1", "", {}, "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg=="],
+
+ "is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="],
+
+ "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
+
+ "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
+
+ "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
+
+ "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
+
+ "lightningcss": ["lightningcss@1.29.2", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.29.2", "lightningcss-darwin-x64": "1.29.2", "lightningcss-freebsd-x64": "1.29.2", "lightningcss-linux-arm-gnueabihf": "1.29.2", "lightningcss-linux-arm64-gnu": "1.29.2", "lightningcss-linux-arm64-musl": "1.29.2", "lightningcss-linux-x64-gnu": "1.29.2", "lightningcss-linux-x64-musl": "1.29.2", "lightningcss-win32-arm64-msvc": "1.29.2", "lightningcss-win32-x64-msvc": "1.29.2" } }, "sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA=="],
+
+ "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.29.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA=="],
+
+ "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.29.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w=="],
+
+ "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.29.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg=="],
+
+ "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.29.2", "", { "os": "linux", "cpu": "arm" }, "sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg=="],
+
+ "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.29.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ=="],
+
+ "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.29.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ=="],
+
+ "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.29.2", "", { "os": "linux", "cpu": "x64" }, "sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg=="],
+
+ "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.29.2", "", { "os": "linux", "cpu": "x64" }, "sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w=="],
+
+ "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.29.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw=="],
+
+ "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.29.2", "", { "os": "win32", "cpu": "x64" }, "sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA=="],
+
+ "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="],
+
+ "loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="],
+
+ "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
+
+ "lucide-react": ["lucide-react@0.484.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-oZy8coK9kZzvqhSgfbGkPtTgyjpBvs3ukLgDPv14dSOZtBtboryWF5o8i3qen7QbGg7JhiJBz5mK1p8YoMZTLQ=="],
+
+ "magic-string": ["magic-string@0.30.19", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw=="],
+
+ "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="],
+
+ "mdast-util-find-and-replace": ["mdast-util-find-and-replace@3.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "escape-string-regexp": "^5.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg=="],
+
+ "mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA=="],
+
+ "mdast-util-gfm": ["mdast-util-gfm@3.1.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-gfm-autolink-literal": "^2.0.0", "mdast-util-gfm-footnote": "^2.0.0", "mdast-util-gfm-strikethrough": "^2.0.0", "mdast-util-gfm-table": "^2.0.0", "mdast-util-gfm-task-list-item": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ=="],
+
+ "mdast-util-gfm-autolink-literal": ["mdast-util-gfm-autolink-literal@2.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "ccount": "^2.0.0", "devlop": "^1.0.0", "mdast-util-find-and-replace": "^3.0.0", "micromark-util-character": "^2.0.0" } }, "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ=="],
+
+ "mdast-util-gfm-footnote": ["mdast-util-gfm-footnote@2.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0" } }, "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ=="],
+
+ "mdast-util-gfm-strikethrough": ["mdast-util-gfm-strikethrough@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg=="],
+
+ "mdast-util-gfm-table": ["mdast-util-gfm-table@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "markdown-table": "^3.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg=="],
+
+ "mdast-util-gfm-task-list-item": ["mdast-util-gfm-task-list-item@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ=="],
+
+ "mdast-util-mdx-expression": ["mdast-util-mdx-expression@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ=="],
+
+ "mdast-util-mdx-jsx": ["mdast-util-mdx-jsx@3.2.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "parse-entities": "^4.0.0", "stringify-entities": "^4.0.0", "unist-util-stringify-position": "^4.0.0", "vfile-message": "^4.0.0" } }, "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q=="],
+
+ "mdast-util-mdxjs-esm": ["mdast-util-mdxjs-esm@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg=="],
+
+ "mdast-util-phrasing": ["mdast-util-phrasing@4.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "unist-util-is": "^6.0.0" } }, "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w=="],
+
+ "mdast-util-to-hast": ["mdast-util-to-hast@13.2.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA=="],
+
+ "mdast-util-to-markdown": ["mdast-util-to-markdown@2.1.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "longest-streak": "^3.0.0", "mdast-util-phrasing": "^4.0.0", "mdast-util-to-string": "^4.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "unist-util-visit": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA=="],
+
+ "mdast-util-to-string": ["mdast-util-to-string@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0" } }, "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg=="],
+
+ "micromark": ["micromark@4.0.2", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA=="],
+
+ "micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="],
+
+ "micromark-extension-gfm": ["micromark-extension-gfm@3.0.0", "", { "dependencies": { "micromark-extension-gfm-autolink-literal": "^2.0.0", "micromark-extension-gfm-footnote": "^2.0.0", "micromark-extension-gfm-strikethrough": "^2.0.0", "micromark-extension-gfm-table": "^2.0.0", "micromark-extension-gfm-tagfilter": "^2.0.0", "micromark-extension-gfm-task-list-item": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w=="],
+
+ "micromark-extension-gfm-autolink-literal": ["micromark-extension-gfm-autolink-literal@2.1.0", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw=="],
+
+ "micromark-extension-gfm-footnote": ["micromark-extension-gfm-footnote@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw=="],
+
+ "micromark-extension-gfm-strikethrough": ["micromark-extension-gfm-strikethrough@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw=="],
+
+ "micromark-extension-gfm-table": ["micromark-extension-gfm-table@2.1.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg=="],
+
+ "micromark-extension-gfm-tagfilter": ["micromark-extension-gfm-tagfilter@2.0.0", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg=="],
+
+ "micromark-extension-gfm-task-list-item": ["micromark-extension-gfm-task-list-item@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw=="],
+
+ "micromark-factory-destination": ["micromark-factory-destination@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA=="],
+
+ "micromark-factory-label": ["micromark-factory-label@2.0.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg=="],
+
+ "micromark-factory-space": ["micromark-factory-space@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg=="],
+
+ "micromark-factory-title": ["micromark-factory-title@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw=="],
+
+ "micromark-factory-whitespace": ["micromark-factory-whitespace@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ=="],
+
+ "micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="],
+
+ "micromark-util-chunked": ["micromark-util-chunked@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA=="],
+
+ "micromark-util-classify-character": ["micromark-util-classify-character@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q=="],
+
+ "micromark-util-combine-extensions": ["micromark-util-combine-extensions@2.0.1", "", { "dependencies": { "micromark-util-chunked": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg=="],
+
+ "micromark-util-decode-numeric-character-reference": ["micromark-util-decode-numeric-character-reference@2.0.2", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw=="],
+
+ "micromark-util-decode-string": ["micromark-util-decode-string@2.0.1", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ=="],
+
+ "micromark-util-encode": ["micromark-util-encode@2.0.1", "", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="],
+
+ "micromark-util-html-tag-name": ["micromark-util-html-tag-name@2.0.1", "", {}, "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA=="],
+
+ "micromark-util-normalize-identifier": ["micromark-util-normalize-identifier@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q=="],
+
+ "micromark-util-resolve-all": ["micromark-util-resolve-all@2.0.1", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg=="],
+
+ "micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="],
+
+ "micromark-util-subtokenize": ["micromark-util-subtokenize@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA=="],
+
+ "micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="],
+
+ "micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="],
+
+ "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
+
+ "minizlib": ["minizlib@3.1.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw=="],
+
+ "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
+
+ "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
+
+ "node-releases": ["node-releases@2.0.26", "", {}, "sha512-S2M9YimhSjBSvYnlr5/+umAnPHE++ODwt5e2Ij6FoX45HA/s4vHdkDx1eax2pAPeAOqu4s9b7ppahsyEFdVqQA=="],
+
+ "parse-entities": ["parse-entities@4.0.2", "", { "dependencies": { "@types/unist": "^2.0.0", "character-entities-legacy": "^3.0.0", "character-reference-invalid": "^2.0.0", "decode-named-character-reference": "^1.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" } }, "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw=="],
+
+ "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
+
+ "pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="],
+
+ "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
+
+ "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
+
+ "property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="],
+
+ "react": ["react@19.1.0", "", {}, "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg=="],
+
+ "react-dom": ["react-dom@19.1.0", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.0" } }, "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g=="],
+
+ "react-markdown": ["react-markdown@10.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "html-url-attributes": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "unified": "^11.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" }, "peerDependencies": { "@types/react": ">=18", "react": ">=18" } }, "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ=="],
+
+ "react-refresh": ["react-refresh@0.17.0", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="],
+
+ "remark-gfm": ["remark-gfm@4.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg=="],
+
+ "remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="],
+
+ "remark-rehype": ["remark-rehype@11.1.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "mdast-util-to-hast": "^13.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw=="],
+
+ "remark-stringify": ["remark-stringify@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-to-markdown": "^2.0.0", "unified": "^11.0.0" } }, "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw=="],
+
+ "rollup": ["rollup@4.44.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.44.1", "@rollup/rollup-android-arm64": "4.44.1", "@rollup/rollup-darwin-arm64": "4.44.1", "@rollup/rollup-darwin-x64": "4.44.1", "@rollup/rollup-freebsd-arm64": "4.44.1", "@rollup/rollup-freebsd-x64": "4.44.1", "@rollup/rollup-linux-arm-gnueabihf": "4.44.1", "@rollup/rollup-linux-arm-musleabihf": "4.44.1", "@rollup/rollup-linux-arm64-gnu": "4.44.1", "@rollup/rollup-linux-arm64-musl": "4.44.1", "@rollup/rollup-linux-loongarch64-gnu": "4.44.1", "@rollup/rollup-linux-powerpc64le-gnu": "4.44.1", "@rollup/rollup-linux-riscv64-gnu": "4.44.1", "@rollup/rollup-linux-riscv64-musl": "4.44.1", "@rollup/rollup-linux-s390x-gnu": "4.44.1", "@rollup/rollup-linux-x64-gnu": "4.44.1", "@rollup/rollup-linux-x64-musl": "4.44.1", "@rollup/rollup-win32-arm64-msvc": "4.44.1", "@rollup/rollup-win32-ia32-msvc": "4.44.1", "@rollup/rollup-win32-x64-msvc": "4.44.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-x8H8aPvD+xbl0Do8oez5f5o8eMS3trfCghc4HhLAnCkj7Vl0d1JWGs0UF/D886zLW2rOj2QymV/JcSSsw+XDNg=="],
+
+ "scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="],
+
+ "semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
+
+ "siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="],
+
+ "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
+
+ "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="],
+
+ "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="],
+
+ "std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="],
+
+ "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="],
+
+ "style-to-js": ["style-to-js@1.1.18", "", { "dependencies": { "style-to-object": "1.0.11" } }, "sha512-JFPn62D4kJaPTnhFUI244MThx+FEGbi+9dw1b9yBBQ+1CZpV7QAT8kUtJ7b7EUNdHajjF/0x8fT+16oLJoojLg=="],
+
+ "style-to-object": ["style-to-object@1.0.11", "", { "dependencies": { "inline-style-parser": "0.2.4" } }, "sha512-5A560JmXr7wDyGLK12Nq/EYS38VkGlglVzkis1JEdbGWSnbQIEhZzTJhzURXN5/8WwwFCs/f/VVcmkTppbXLow=="],
+
+ "tailwind-merge": ["tailwind-merge@3.2.0", "", {}, "sha512-FQT/OVqCD+7edmmJpsgCsY820RTD5AkBryuG5IUqR5YQZSdj5xlH5nLgH7YPths7WsLPSpSBNneJdM8aS8aeFA=="],
+
+ "tailwindcss": ["tailwindcss@4.1.11", "", {}, "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA=="],
+
+ "tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="],
+
+ "tar": ["tar@7.5.1", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g=="],
+
+ "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="],
+
+ "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="],
+
+ "tinypool": ["tinypool@1.1.1", "", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="],
+
+ "tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="],
+
+ "tinyspy": ["tinyspy@3.0.2", "", {}, "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q=="],
+
+ "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="],
+
+ "trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="],
+
+ "tw-animate-css": ["tw-animate-css@1.3.4", "", {}, "sha512-dd1Ht6/YQHcNbq0znIT6dG8uhO7Ce+VIIhZUhjsryXsMPJQz3bZg7Q2eNzLwipb25bRZslGb2myio5mScd1TFg=="],
+
+ "typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
+
+ "unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="],
+
+ "unist-util-is": ["unist-util-is@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g=="],
+
+ "unist-util-position": ["unist-util-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="],
+
+ "unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="],
+
+ "unist-util-visit": ["unist-util-visit@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="],
+
+ "unist-util-visit-parents": ["unist-util-visit-parents@6.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="],
+
+ "update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="],
+
+ "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="],
+
+ "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="],
+
+ "vite": ["vite@6.2.5", "", { "dependencies": { "esbuild": "^0.25.0", "postcss": "^8.5.3", "rollup": "^4.30.1" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-j023J/hCAa4pRIUH6J9HemwYfjB5llR2Ps0CWeikOtdR8+pAURAk0DoJC5/mm9kd+UgdnIy7d6HE4EAvlYhPhA=="],
+
+ "vite-node": ["vite-node@3.0.9", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.0", "es-module-lexer": "^1.6.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-w3Gdx7jDcuT9cNn9jExXgOyKmf5UOTb6WMHz8LGAm54eS1Elf5OuBhCxl6zJxGhEeIkgsE1WbHuoL0mj/UXqXg=="],
+
+ "vitest": ["vitest@3.0.9", "", { "dependencies": { "@vitest/expect": "3.0.9", "@vitest/mocker": "3.0.9", "@vitest/pretty-format": "^3.0.9", "@vitest/runner": "3.0.9", "@vitest/snapshot": "3.0.9", "@vitest/spy": "3.0.9", "@vitest/utils": "3.0.9", "chai": "^5.2.0", "debug": "^4.4.0", "expect-type": "^1.1.0", "magic-string": "^0.30.17", "pathe": "^2.0.3", "std-env": "^3.8.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinypool": "^1.0.2", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0", "vite-node": "3.0.9", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.0.9", "@vitest/ui": "3.0.9", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-BbcFDqNyBlfSpATmTtXOAOj71RNKDDvjBM/uPfnxxVGrG+FSH2RQIwgeEngTaTkuU/h0ScFvf+tRcKfYXzBybQ=="],
+
+ "why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="],
+
+ "yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="],
+
+ "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
+
+ "@radix-ui/react-collection/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
+
+ "@radix-ui/react-dismissable-layer/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
+
+ "@radix-ui/react-portal/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
+
+ "@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ=="],
+
+ "@radix-ui/react-toast/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
+
+ "@radix-ui/react-visually-hidden/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
+
+ "@tailwindcss/node/lightningcss": ["lightningcss@1.30.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.30.1", "lightningcss-darwin-x64": "1.30.1", "lightningcss-freebsd-x64": "1.30.1", "lightningcss-linux-arm-gnueabihf": "1.30.1", "lightningcss-linux-arm64-gnu": "1.30.1", "lightningcss-linux-arm64-musl": "1.30.1", "lightningcss-linux-x64-gnu": "1.30.1", "lightningcss-linux-x64-musl": "1.30.1", "lightningcss-win32-arm64-msvc": "1.30.1", "lightningcss-win32-x64-msvc": "1.30.1" } }, "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.6.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-zq/ay+9fNIJJtJiZxdTnXS20PllcYMX3OE23ESc4HK/bdYu3cOWYVhsOhVnXALfU/uqJIxn5NBPd9z4v+SfoSg=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.6.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-obtUmAHTMjll499P+D9A3axeJFlhdjOWdKUNs/U6QIGT7V5RjcUW1xToAzjvmgTSQhDbYn/NwfTRoJcQ2rNBxA=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" }, "bundled": true }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.9.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
+
+ "@vitest/snapshot/@vitest/pretty-format": ["@vitest/pretty-format@3.0.9", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-OW9F8t2J3AwFEwENg3yMyKWweF7oRJlMyHOMIhO5F3n0+cgQAJZBjNgrF8dLwFTEXl5jUqBLXd9QyyKv8zEcmA=="],
+
+ "@vitest/utils/@vitest/pretty-format": ["@vitest/pretty-format@3.0.9", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-OW9F8t2J3AwFEwENg3yMyKWweF7oRJlMyHOMIhO5F3n0+cgQAJZBjNgrF8dLwFTEXl5jUqBLXd9QyyKv8zEcmA=="],
+
+ "lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
+
+ "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="],
+
+ "@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="],
+
+ "@tailwindcss/node/lightningcss/lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ=="],
+
+ "@tailwindcss/node/lightningcss/lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA=="],
+
+ "@tailwindcss/node/lightningcss/lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig=="],
+
+ "@tailwindcss/node/lightningcss/lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.1", "", { "os": "linux", "cpu": "arm" }, "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q=="],
+
+ "@tailwindcss/node/lightningcss/lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw=="],
+
+ "@tailwindcss/node/lightningcss/lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ=="],
+
+ "@tailwindcss/node/lightningcss/lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw=="],
+
+ "@tailwindcss/node/lightningcss/lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ=="],
+
+ "@tailwindcss/node/lightningcss/lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA=="],
+
+ "@tailwindcss/node/lightningcss/lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.1", "", { "os": "win32", "cpu": "x64" }, "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
+ }
+}
diff --git a/claude.md b/claude.md
new file mode 100644
index 0000000..1c13ca5
--- /dev/null
+++ b/claude.md
@@ -0,0 +1,6 @@
+# Claude Rules
+
+- **Primary rules**: [.cursor/rules/instructions.txt](./.cursor/rules/instructions.txt)
+- **Split index (if present)**: [.cursor/rules/claude.md](./.cursor/rules/claude.md)
+
+This document serves as the root entry point to the Cursor rules in this repository.
diff --git a/frontend/client.ts b/frontend/client.ts
index f8df62c..1f3303a 100644
--- a/frontend/client.ts
+++ b/frontend/client.ts
@@ -1,4 +1,4 @@
-// Code generated by the Encore 1.50.6 client generator. DO NOT EDIT.
+// Code generated by the Encore v1.50.6 client generator. DO NOT EDIT.
// Disable eslint, jshint, and jslint for this file.
/* eslint-disable */
@@ -17,7 +17,7 @@ export const Local: BaseURL = "http://localhost:4000"
* Environment returns a BaseURL for calling the cloud environment with the given name.
*/
export function Environment(name: string): BaseURL {
- return `https://${name}-.encr.app`
+ return `https://steering-wheel-documentation-65b2-${name}.encr.app`
}
/**
@@ -30,7 +30,7 @@ export function PreviewEnv(pr: number | string): BaseURL {
const BROWSER = typeof globalThis === "object" && ("window" in globalThis);
/**
- * Client is an API client for the Encore application.
+ * Client is an API client for the steering-wheel-documentation-65b2 Encore application.
*/
export class Client {
public readonly run: run.ServiceClient
@@ -440,7 +440,7 @@ class BaseClient {
// Add User-Agent header if the script is running in the server
// because browsers do not allow setting User-Agent headers to requests
if (!BROWSER) {
- this.headers["User-Agent"] = "-Generated-TS-Client (Encore/1.50.6)";
+ this.headers["User-Agent"] = "steering-wheel-documentation-65b2-Generated-TS-Client (Encore/v1.50.6)";
}
this.requestInit = options.requestInit ?? {};
diff --git a/seed.md b/seed.md
index c592b8b..058b31c 100644
--- a/seed.md
+++ b/seed.md
@@ -1,4 +1,4 @@
-# Steering Wheel - Documentation System Evaluation
+# ScreenGraph - Documentation System Evaluation - This is where it all began!
**Project**: ScreenGraph SAAS - "A living map of your mobile app for UI/UX and competitive analysis"
@@ -18,209 +18,6 @@
---
-## Option 1: Docusaurus 3.9
-
-### Overview
-Meta's static site generator built on React, widely used for technical documentation.
-
-### Pros
-- ✅ Rich MDX support (Markdown + React components)
-- ✅ Built-in versioning system
-- ✅ Search functionality out of box
-- ✅ Plugin ecosystem (changelog, blog, docs)
-- ✅ Mature, well-documented
-
-### Cons
-- ❌ **Over-engineered** for solo dev needs
-- ❌ Requires separate build/deployment process
-- ❌ Heavy frontend bundle (~300KB+)
-- ❌ No native chat interface - would need custom integration
-- ❌ Slower iteration cycle (build → deploy)
-- ❌ Complex folder structure (docs/, blog/, src/, etc.)
-- ❌ Not designed for real-time editing
-
-### AI-Agent Integration
-- Agents can read markdown files directly from `/docs` folder
-- Would need custom API to expose docs to chat interface
-- Versioning through Git + Docusaurus versioning
-
-### Effort Estimate: **High** (2-3 days setup + ongoing maintenance)
-
----
-
-## Option 2: Starlight (Astro Framework)
-
-### Overview
-Astro's documentation theme - modern, fast, content-focused.
-
-### Pros
-- ✅ Excellent performance (partial hydration)
-- ✅ Clean MDX support
-- ✅ Simple file-based routing
-- ✅ Built-in sidebar navigation
-- ✅ TypeScript-first
-- ✅ Lighter than Docusaurus
-
-### Cons
-- ❌ Still requires build process
-- ❌ No native versioning system
-- ❌ No chat interface built-in
-- ❌ Younger ecosystem than Docusaurus
-- ❌ Separate deployment needed
-- ❌ Static site - not designed for real-time updates
-
-### AI-Agent Integration
-- Agents read from markdown files in `/src/content/docs`
-- Would need custom backend for chat + editing
-- Manual versioning via Git
-
-### Effort Estimate: **Medium** (1-2 days setup + custom features)
-
----
-
-## Option 3: Notion
-
-### Overview
-All-in-one workspace with docs, wikis, databases, and collaboration features. Popular among startups and solo devs.
-
-### Pros
-- ✅ **Zero setup** - create account and start writing
-- ✅ **Rich editor** - blocks, databases, embeds, formatting
-- ✅ **Built-in AI** - Notion AI for chat, summarization, editing
-- ✅ **Version history** - track changes over time (paid plans)
-- ✅ **Templates** - ready-made structures for wikis, docs, project management
-- ✅ **Mobile apps** - access from anywhere
-- ✅ **Search** - powerful full-text search
-- ✅ **Databases** - tables, boards, calendars for tasks/reports
-- ✅ **Collaboration** - comments, mentions (if team grows)
-
-### Cons
-- ❌ **Not AI-agent readable** - proprietary format, no direct file access
-- ❌ **API limitations** - rate limits, complex setup for programmatic access
-- ❌ **Export friction** - markdown export exists but loses structure
-- ❌ **Context switching** - separate tool outside codebase
-- ❌ **Vendor lock-in** - data trapped in Notion ecosystem
-- ❌ **No mandatory reading enforcement** - can't force AI agents to read specific pages
-- ❌ **Cost** - free tier limited, paid plans $8-10/month
-- ❌ **Performance** - can be slow with large workspaces
-- ❌ **Offline limitations** - requires internet connection
-- ❌ **No Git integration** - can't version control with code
-
-### AI-Agent Integration
-**Major limitation:**
-- Notion API exists but is paginated, slow, and complex
-- AI agents like Leap/Cursor can't directly read Notion pages without custom integration
-- Would need to build Notion API wrapper → convert to markdown → feed to AI
-- No way to enforce "mandatory reading" for AI agents
-- Updates require API calls instead of direct file edits
-
-**Workaround approach:**
-1. Export Notion pages to markdown manually
-2. Commit to Git repository
-3. AI reads exported files
-4. Manual sync process between Notion ↔ Git
-
-This defeats the purpose of real-time collaboration with AI.
-
-### Effort Estimate: **Low for setup, High for AI integration** (5 min setup, 1-2 days for proper AI integration)
-
----
-
-## Option 4: Custom Encore.ts + React Solution
-
-### Overview
-Build a lightweight docs app within the ScreenGraph project using Encore.ts backend + React frontend with markdown rendering.
-
-### Architecture
-```
-backend/
- steering/
- encore.service.ts
- get_doc.ts # Fetch single doc
- list_docs.ts # List all docs
- update_doc.ts # Update doc content
- chat_with_docs.ts # AI chat endpoint
- versions/
- list_versions.ts # Version history
- get_version.ts # Get specific version
- db/
- index.ts # SQL database for docs + versions
-
-frontend/
- components/
- steering/
- DocViewer.tsx # Markdown renderer
- DocEditor.tsx # Edit mode
- DocChat.tsx # Chat interface
- DocNav.tsx # Navigation sidebar
- pages/
- SteeringWheel.tsx # Main page
-
-steering-docs/ # Actual markdown files (Git-tracked)
- rules.md
- facts.md
- procedures.md
- preferences.md
- coding-standards.md
- reports/
- 2025-10-23.md
- wip.md
- tasks.md
- .steering-config.json # Mandatory reading list for AI
-```
-
-### Pros
-- ✅ **Perfectly integrated** with your existing project
-- ✅ **Real-time editing** through Encore.ts APIs
-- ✅ **Native chat interface** using AI API integration
-- ✅ **Version control** via database + Git commits
-- ✅ **Zero deployment complexity** - same as your app
-- ✅ **Minimal overhead** - just markdown files + simple UI
-- ✅ **Direct AI access** - agents read from `/steering-docs`
-- ✅ **Enforced reading** via `.steering-config.json`
-- ✅ **Full control** over features and UX
-- ✅ **Single codebase** - no context switching
-
-### Cons
-- ❌ Custom implementation (but simple)
-- ❌ No off-the-shelf templates
-- ❌ Build your own features (version UI, search, etc.)
-
-### AI-Agent Integration
-**Perfect for AI agents:**
-```json
-// .steering-config.json
-{
- "mandatory_reading": [
- "steering-docs/rules.md",
- "steering-docs/coding-standards.md",
- "steering-docs/wip.md"
- ],
- "context_files": [
- "steering-docs/facts.md",
- "steering-docs/preferences.md"
- ]
-}
-```
-
-AI agents like Leap can:
-1. Read `.steering-config.json` to know what to load
-2. Parse markdown files directly (native format)
-3. Call `/steering/chat_with_docs` API to query docs
-4. Update docs via `/steering/update_doc` API
-
-### Features to Build
-1. **Doc Viewer** - Markdown rendering with syntax highlighting
-2. **Doc Editor** - Simple textarea or Monaco editor
-3. **Chat Interface** - Query docs using OpenAI/Anthropic
-4. **Version History** - Database-backed with Git sync
-5. **Navigation** - Auto-generated from file structure
-6. **Search** - Simple text search across all docs
-
-### Effort Estimate: **Low** (4-6 hours for MVP, extensible over time)
-
----
-
## Recommendation: **Option 4 - Custom Encore.ts Solution**
### Why This Is the Best Approach
@@ -270,58 +67,6 @@ As a SAAS product for UI/UX analysis, having internal docs in the same codebase:
---
-## Implementation Plan
-
-### Phase 1: MVP (Today)
-1. Create `/steering-docs` folder structure
-2. Build basic Encore.ts CRUD endpoints
-3. Simple React viewer with markdown rendering
-4. `.steering-config.json` for AI agents
-
-### Phase 2: Enhanced (This Week)
-1. Add edit mode
-2. Database versioning
-3. Auto-commit to Git on changes
-4. Navigation sidebar
-
-### Phase 3: Advanced (As Needed)
-1. Chat interface with AI
-2. Version history UI
-3. Search functionality
-4. Diff viewer
-
----
-
-## Comparison Matrix
-
-| Feature | Docusaurus | Starlight | Notion | Custom Encore.ts |
-|---------|-----------|-----------|--------|------------------|
-| **AI-Agent Readable** | ✅ Markdown files | ✅ Markdown files | ❌ Proprietary API | ✅ Direct file access |
-| **Setup Time** | 2-3 days | 1-2 days | 5 minutes | 4-6 hours |
-| **Real-time Editing** | ❌ Build required | ❌ Build required | ✅ Instant | ✅ Instant |
-| **Chat Interface** | ❌ Custom needed | ❌ Custom needed | ✅ Built-in AI | ✅ Custom (easy) |
-| **Version Control** | ✅ Git + Built-in | ⚠️ Git only | ⚠️ Paid plans | ✅ DB + Git |
-| **Mandatory Reading** | ⚠️ Possible | ⚠️ Possible | ❌ Impossible | ✅ Config-driven |
-| **Deployment** | Separate | Separate | Cloud-hosted | Integrated |
-| **Cost** | Free | Free | $0-10/month | Free (your infra) |
-| **Context Switching** | ❌ Separate site | ❌ Separate site | ❌ External tool | ✅ Same codebase |
-| **Customization** | ⚠️ Limited | ⚠️ Limited | ❌ Very limited | ✅ Full control |
-
----
-
-## Conclusion
-
-**Notion** is excellent for personal wikis and team collaboration, but **fundamentally incompatible** with the goal of AI-agent readable documentation. The proprietary format and API limitations create too much friction for programmatic access by Leap, Cursor, and Claude.
-
-**Docusaurus** and **Starlight** are excellent tools for public documentation websites, but they're over-engineered for a solo dev's internal knowledge base that needs AI integration and real-time editing.
-
-**Custom Encore.ts solution** gives you:
-- Exactly what you need, nothing more
-- Perfect AI agent integration
-- Real-time collaboration with AI
-- Zero deployment overhead
-- Full control and extensibility
-
**Decision: Build custom Steering Wheel in Encore.ts**
Let's start with the foundation today and iterate based on actual usage patterns.