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 + + schema_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 = ``; + 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 + + schema_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.