From 4aa53c1ee9407b02a94b503678418bc704a706d9 Mon Sep 17 00:00:00 2001 From: Devarshi Shimpi Date: Tue, 22 Sep 2026 01:29:40 +0530 Subject: [PATCH 01/16] feat(node): wire up BullMQ worker and Redis adapters --- apps/node/src/adapters/node-orchestrator.ts | 38 +++++++++++++ apps/node/src/adapters/platform.ts | 13 +++++ apps/node/src/adapters/redis-session-store.ts | 36 +++++++++++++ apps/node/src/adapters/services.ts | 40 ++++++++++++++ apps/node/src/adapters/telemetry.ts | 11 ++++ apps/node/src/api-deps.ts | 5 +- apps/node/src/env.ts | 6 +-- apps/node/src/index.ts | 39 +++++++++----- apps/node/src/runtime.ts | 53 +++++++++++++++++++ apps/node/src/worker.ts | 37 +++++++++++++ package-lock.json | 31 ++++++++--- package.json | 1 + 12 files changed, 284 insertions(+), 26 deletions(-) create mode 100644 apps/node/src/adapters/node-orchestrator.ts create mode 100644 apps/node/src/adapters/platform.ts create mode 100644 apps/node/src/adapters/redis-session-store.ts create mode 100644 apps/node/src/adapters/services.ts create mode 100644 apps/node/src/adapters/telemetry.ts create mode 100644 apps/node/src/runtime.ts create mode 100644 apps/node/src/worker.ts diff --git a/apps/node/src/adapters/node-orchestrator.ts b/apps/node/src/adapters/node-orchestrator.ts new file mode 100644 index 00000000..32fe8f4c --- /dev/null +++ b/apps/node/src/adapters/node-orchestrator.ts @@ -0,0 +1,38 @@ +import type { JobOrchestrator, ReviewRuntime } from '@codraoss/core'; +import type { ReviewJobMessage } from '@codraoss/schema'; +import { runReview } from '@codraoss/core'; +import { runWithDb } from '@codraoss/db/client'; +import type { DbEnv } from '@codraoss/db/env'; +import { setTimeout } from 'node:timers/promises'; + +export class NodeOrchestrator implements JobOrchestrator { + constructor(private readonly env: ReviewRuntime & DbEnv) {} + + async startReviewJob(id: string, params: ReviewJobMessage): Promise { + return runWithDb(this.env, async () => { + let currentParams = { ...params }; + let phase = currentParams.phase ?? 'prepare'; + + while (phase) { + currentParams.phase = phase; + const result = await runReview(this.env, currentParams); + + if (result.action === 'next_phase') { + phase = result.phase; + if (result.jobId) { + currentParams.jobId = result.jobId; + } + if (result.delaySeconds > 0) { + await setTimeout(result.delaySeconds * 1000); + } + } else if (result.action === 'retry') { + if (result.delaySeconds > 0) { + await setTimeout(result.delaySeconds * 1000); + } + } else if (result.action === 'ack') { + break; + } + } + }); + } +} diff --git a/apps/node/src/adapters/platform.ts b/apps/node/src/adapters/platform.ts new file mode 100644 index 00000000..f3b8855b --- /dev/null +++ b/apps/node/src/adapters/platform.ts @@ -0,0 +1,13 @@ +import type { Clock, IdGenerator, KvStore } from '@codraoss/core/ports'; +import type { NodeAppBindings } from '../env'; + +export function makeKvStore(env: NodeAppBindings): KvStore { + return { + get: (key) => env.APP_KV.get(key), + put: (key, value, options) => env.APP_KV.put(key, value, options), + }; +} + +export const systemClock: Clock = { now: () => Date.now() }; + +export const cryptoIds: IdGenerator = { randomUUID: () => crypto.randomUUID() }; diff --git a/apps/node/src/adapters/redis-session-store.ts b/apps/node/src/adapters/redis-session-store.ts new file mode 100644 index 00000000..d4c75e71 --- /dev/null +++ b/apps/node/src/adapters/redis-session-store.ts @@ -0,0 +1,36 @@ +import type { DashboardSessionUser, SessionStore } from '@codraoss/core/ports'; +import type Redis from 'ioredis'; +import { customAlphabet } from 'nanoid'; + +const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', 21); + +export class RedisSessionStore implements SessionStore { + constructor(private readonly redis: Redis) {} + + private sessionKey(id: string): string { + return `session:${id}`; + } + + async createSession(session: DashboardSessionUser): Promise { + const id = nanoid(); + await this.redis.setex(this.sessionKey(id), 60 * 60 * 24 * 7, JSON.stringify(session)); // 7 days expiration + return id; + } + + async readSession(token: string): Promise { + const sessionString = await this.redis.get(this.sessionKey(token)); + if (!sessionString) { + return null; + } + return JSON.parse(sessionString) as DashboardSessionUser; + } + + async destroySession(token: string): Promise { + await this.redis.del(this.sessionKey(token)); + } + + async renewSession(token: string): Promise { + // Renew by extending the expiration, if it exists + await this.redis.expire(this.sessionKey(token), 60 * 60 * 24 * 7); // 7 days expiration + } +} diff --git a/apps/node/src/adapters/services.ts b/apps/node/src/adapters/services.ts new file mode 100644 index 00000000..6be696d0 --- /dev/null +++ b/apps/node/src/adapters/services.ts @@ -0,0 +1,40 @@ +import type { GitProviderFactory, ModelErrorClassifier, ReviewFormatter, ReviewGitProvider, ReviewModel } from '@codraoss/core/ports'; +import type { TokenTracker } from '@codraoss/core/token-tracker'; +import type { NodeAppBindings } from '../env'; +import { GitHubService } from '@codraoss/provider-github'; +import { isRetryableModelError, ModelRunner, nextChainIndexOf } from '@codraoss/models'; +import { FormatterService } from '@codraoss/core/formatter'; +import { getResolvedModelConfig } from '@codraoss/db/model-configs'; + +export function makeGitHubFactory(env: NodeAppBindings) { + return (installationId: string, tracker: TokenTracker): ReviewGitProvider => new GitHubService(env, installationId, tracker); +} + +export function makeModelFactory(env: NodeAppBindings) { + return (jobId: string, tracker: TokenTracker): ReviewModel => new ModelRunner({ + kv: env.APP_KV as any, + secretStore: { + getSecret: async (key) => env[key as keyof NodeAppBindings] as string || process.env[key] || null, + }, + getConfig: (modelId) => getResolvedModelConfig({ HYPERDRIVE: env.HYPERDRIVE, APP_KV: env.APP_KV }, modelId), + aiBinding: undefined, + tracker, + jobId, + }); +} + +export function makeFormatterFactory(env: NodeAppBindings) { + return (): ReviewFormatter => new FormatterService(env.APP_URL); +} + +export function makeGitHubClientFactory(env: NodeAppBindings): GitProviderFactory { + return { + forInstallation(installationId: string): ReviewGitProvider { + return new GitHubService(env, installationId); + }, + }; +} + +export function makeModelErrorClassifier(): ModelErrorClassifier { + return { isRetryableModelError, nextChainIndexOf }; +} diff --git a/apps/node/src/adapters/telemetry.ts b/apps/node/src/adapters/telemetry.ts new file mode 100644 index 00000000..9c1e28c3 --- /dev/null +++ b/apps/node/src/adapters/telemetry.ts @@ -0,0 +1,11 @@ +import type { TelemetrySink } from '@codraoss/core/ports'; +import type { NodeAppBindings } from '../env'; + +export function makeTelemetrySink(_env: NodeAppBindings): TelemetrySink { + return { + send: async (_event) => { + // In a real setup this would batch and send to a telemetry endpoint + // For now, in Node open-source, telemetry can be safely stubbed or sent to logs + } + }; +} diff --git a/apps/node/src/api-deps.ts b/apps/node/src/api-deps.ts index 00d52bcd..930c5af2 100644 --- a/apps/node/src/api-deps.ts +++ b/apps/node/src/api-deps.ts @@ -1,6 +1,7 @@ import { createSharedApiDeps } from '@codraoss/api'; import type { NodeAppBindings } from './env'; import { logger } from '@codraoss/api/logger'; +import { createReviewRuntime } from './runtime'; export function createNodeApiDeps(env: NodeAppBindings) { return createSharedApiDeps({ @@ -18,9 +19,7 @@ export function createNodeApiDeps(env: NodeAppBindings) { scheduleBestEffortJobMaintenance: () => { // In node, this is a long running process, we can just spawn a promise. }, - createReviewRuntime: () => { - throw new Error('Review runtime not implemented for Node yet.'); - }, + createReviewRuntime: () => createReviewRuntime(env), getOrFetchRawDiffForCompletedJob: async () => '', logger, getSecret: async (key) => process.env[key] ?? null, diff --git a/apps/node/src/env.ts b/apps/node/src/env.ts index 400d9741..4605dac7 100644 --- a/apps/node/src/env.ts +++ b/apps/node/src/env.ts @@ -1,8 +1,7 @@ -import type { SessionStore, IdentityProvider, KeyValueStore, QueueProducer, JobOrchestrator } from '@codraoss/core'; +import type { SessionStore, IdentityProvider, KeyValueStore, QueueProducer, JobOrchestrator } from '@codraoss/core/ports'; import type { ReviewJobMessage } from '@codraoss/schema'; export interface NodeAppBindings { - // ── Platform stores (in-memory stubs for now) ── SESSION_STORE: SessionStore; IDENTITY_PROVIDER?: IdentityProvider; APP_KV: KeyValueStore; @@ -31,7 +30,7 @@ export function createNodeEnv(stubs: { SESSION_STORE: SessionStore; APP_KV: KeyValueStore; REVIEW_QUEUE: QueueProducer; - REVIEW_ORCHESTRATOR: JobOrchestrator; + REVIEW_ORCHESTRATOR?: JobOrchestrator; // Mark as optional }): NodeAppBindings { const requireEnv = (key: string) => { const val = process.env[key]; @@ -41,6 +40,7 @@ export function createNodeEnv(stubs: { return { ...stubs, + REVIEW_ORCHESTRATOR: stubs.REVIEW_ORCHESTRATOR!, HYPERDRIVE: { connectionString: requireEnv('DATABASE_URL') }, APP_PRIVATE_KEY: requireEnv('APP_PRIVATE_KEY'), GITHUB_APP_ID: requireEnv('GITHUB_APP_ID'), diff --git a/apps/node/src/index.ts b/apps/node/src/index.ts index c1dc488f..2590d44c 100644 --- a/apps/node/src/index.ts +++ b/apps/node/src/index.ts @@ -5,7 +5,6 @@ dotenv.config({ path: path.resolve(process.cwd(), '.dev.vars') }); // Root level import { serve } from '@hono/node-server'; import { createApiRouter } from '@codraoss/api'; import { runWithDb } from '@codraoss/db/client'; -import { InMemoryOrchestrator, InMemorySessionStore } from '@codraoss/core/ports'; import { createNodeApiDeps } from './api-deps'; import { createNodeEnv } from './env'; import { logger } from '@codraoss/api/logger'; @@ -13,6 +12,10 @@ import Redis from 'ioredis'; import { RedisKVAdapter } from './adapters/redis-kv'; import { Queue } from 'bullmq'; import { RedisQueueAdapter } from './adapters/redis-queue'; +import { NodeOrchestrator } from './adapters/node-orchestrator'; +import { startWorker } from './worker'; +import { createReviewRuntime } from './runtime'; +import { RedisSessionStore } from './adapters/redis-session-store'; const redisUrl = process.env.REDIS_URL || 'redis://localhost:6379'; const redisClient = new Redis(redisUrl, { maxRetriesPerRequest: null }); // maxRetriesPerRequest: null is required for bullmq @@ -23,13 +26,14 @@ redisClient.on('error', (err) => { const reviewQueue = new Queue('codra-reviews', { connection: redisClient }); const stubs = { - SESSION_STORE: new InMemorySessionStore(), + SESSION_STORE: new RedisSessionStore(redisClient), APP_KV: new RedisKVAdapter(redisClient), REVIEW_QUEUE: new RedisQueueAdapter(reviewQueue), - REVIEW_ORCHESTRATOR: new InMemoryOrchestrator(), + REVIEW_ORCHESTRATOR: undefined, // Will be set to NodeOrchestrator below }; const env = createNodeEnv(stubs); +env.REVIEW_ORCHESTRATOR = new NodeOrchestrator(createReviewRuntime(env)); import fs from 'node:fs'; import { serveStatic } from '@hono/node-server/serve-static'; @@ -63,7 +67,13 @@ app.use('/*.svg', serveStatic({ root: process.cwd().endsWith('node') ? '../../di app.use('/*.ico', serveStatic({ root: process.cwd().endsWith('node') ? '../../dist/client' : 'dist/client' })); const port = parseInt(process.env.PORT || '3000', 10); -serve({ +let worker: ReturnType | undefined; +if (process.env.START_WORKER !== 'false') { + worker = startWorker(env, redisUrl); + logger.info('BullMQ Background Worker started.'); +} + +const server = serve({ fetch: async (request) => { const apiEnv = { ...envWithAssets, @@ -76,16 +86,17 @@ serve({ logger.info(`Codra Node server running on http://localhost:${info.port}`); }); +const shutdown = async () => { + logger.info('Shutting down Codra Node server...'); + if (worker) { + logger.info('Waiting for active BullMQ jobs to finish...'); + await worker.close(); + logger.info('BullMQ Worker closed gracefully.'); + } + process.exit(0); +}; - - - - - - - - - - +process.on('SIGTERM', shutdown); +process.on('SIGINT', shutdown); // Trigger restart for env vars diff --git a/apps/node/src/runtime.ts b/apps/node/src/runtime.ts new file mode 100644 index 00000000..e6e2b8c7 --- /dev/null +++ b/apps/node/src/runtime.ts @@ -0,0 +1,53 @@ +import type { ReviewRuntime } from '@codraoss/core/ports'; +import type { DbEnv } from '@codraoss/db/env'; +import { TokenTracker } from '@codraoss/core/token-tracker'; +import type { NodeAppBindings } from './env'; + +// Adapters from @codraoss/db/repositories +import { makeJobStore, makeFileReviewStore, makeLearningStore, makeModelConfigReader, makeReviewSettingsReader, makeWebhookDeliveryReader } from '@codraoss/db/repositories'; + +// Config +import { loadRepoConfig } from '@codraoss/api/platform'; + +// Node-specific adapters +import { cryptoIds, systemClock } from './adapters/platform'; +import { makeTelemetrySink } from './adapters/telemetry'; +import { + makeFormatterFactory, + makeGitHubClientFactory, + makeGitHubFactory, + makeModelErrorClassifier, + makeModelFactory, +} from './adapters/services'; + +export function createReviewRuntime(env: NodeAppBindings): ReviewRuntime & DbEnv { + const dbEnv = { HYPERDRIVE: env.HYPERDRIVE, APP_KV: env.APP_KV, workerMode: false }; + + return { + ...dbEnv, + kv: env.APP_KV, + clock: systemClock, + ids: cryptoIds, + + botUsername: env.BOT_USERNAME, + + jobs: makeJobStore(dbEnv), + fileReviews: makeFileReviewStore(dbEnv), + settings: makeReviewSettingsReader(dbEnv), + webhooks: makeWebhookDeliveryReader(dbEnv), + learning: makeLearningStore(dbEnv), + modelConfigs: makeModelConfigReader(dbEnv), + repoConfig: { + loadRepoConfig: (context) => loadRepoConfig(env.APP_KV, dbEnv, context) + }, + telemetry: makeTelemetrySink(env), + + createTokenTracker: () => new TokenTracker(), + createGitHub: makeGitHubFactory(env), + createModel: makeModelFactory(env), + createFormatter: makeFormatterFactory(env), + + githubClients: makeGitHubClientFactory(env), + modelErrors: makeModelErrorClassifier(), + }; +} diff --git a/apps/node/src/worker.ts b/apps/node/src/worker.ts new file mode 100644 index 00000000..3da94dac --- /dev/null +++ b/apps/node/src/worker.ts @@ -0,0 +1,37 @@ +import { Worker, type Job } from 'bullmq'; +import { reviewJobMessageSchema } from '@codraoss/schema'; +import { NodeOrchestrator } from './adapters/node-orchestrator'; +import { logger } from '@codraoss/api/logger'; +import type { NodeAppBindings } from './env'; +import Redis from 'ioredis'; +import { createReviewRuntime } from './runtime'; + +export function startWorker(env: NodeAppBindings, redisUrl: string): Worker { + const connection = new Redis(redisUrl, { maxRetriesPerRequest: null }); + + const worker = new Worker( + 'codra-reviews', + async (job: Job) => { + const parseResult = reviewJobMessageSchema.safeParse(job.data); + if (!parseResult.success) { + logger.error(`[Worker] Invalid job payload for job ${job.id}`, parseResult.error); + throw new Error('Invalid job payload'); + } + + const reviewRuntime = createReviewRuntime(env); + const orchestrator = new NodeOrchestrator(reviewRuntime); + await orchestrator.startReviewJob(job.id ?? 'unknown', parseResult.data); + }, + { connection } + ); + + worker.on('completed', (job) => { + logger.info(`[Worker] Job ${job.id} completed successfully`); + }); + + worker.on('failed', (job, err) => { + logger.error(`[Worker] Job ${job?.id} failed`, err); + }); + + return worker; +} diff --git a/package-lock.json b/package-lock.json index 9b2b88d9..b88d9a71 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,6 +28,7 @@ "lenis": "^1.3.26", "lucide-react": "^1.8.0", "motion": "^12.42.2", + "nanoid": "^6.0.1", "postgres": "^3.4.9", "react": "^19.2.8", "react-dom": "^19.2.8", @@ -8505,10 +8506,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.18", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", - "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", - "dev": true, + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-6.0.1.tgz", + "integrity": "sha512-3wVS3i51pE2pi1k5FFL/95BGfVS0kSsvDVuGXHOtxox/TywUmtgq+3qiTOTbs9J7KfHaXPiN171k/A6dBnaXFw==", "funding": [ { "type": "github", @@ -8517,10 +8517,10 @@ ], "license": "MIT", "bin": { - "nanoid": "bin/nanoid.cjs" + "nanoid": "bin/nanoid.js" }, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": "^22 || ^24 || >=26" } }, "node_modules/napi-postinstall": { @@ -8910,6 +8910,25 @@ } } }, + "node_modules/postcss/node_modules/nanoid": { + "version": "3.3.19", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.19.tgz", + "integrity": "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/postgres": { "version": "3.4.9", "license": "Unlicense", diff --git a/package.json b/package.json index 66883cfa..0c7ea2b6 100644 --- a/package.json +++ b/package.json @@ -88,6 +88,7 @@ "lenis": "^1.3.26", "lucide-react": "^1.8.0", "motion": "^12.42.2", + "nanoid": "^6.0.1", "postgres": "^3.4.9", "react": "^19.2.8", "react-dom": "^19.2.8", From 42d02f03a66c13e13262f5e46087047ee688ba9d Mon Sep 17 00:00:00 2001 From: Devarshi Shimpi Date: Tue, 22 Sep 2026 02:51:41 +0530 Subject: [PATCH 02/16] feat: add Docker support and self-hosting documentation --- README.md | 6 ++- apps/node/Dockerfile | 43 +++++++++++++++++++ apps/node/src/index.ts | 28 +++++++------ docker-compose.yml | 53 +++++++++++++++++++++++ docs/deployment/docker.md | 88 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 204 insertions(+), 14 deletions(-) create mode 100644 apps/node/Dockerfile create mode 100644 docker-compose.yml create mode 100644 docs/deployment/docker.md diff --git a/README.md b/README.md index 201c692a..a0cd1a53 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,9 @@ | Docs | - Installation + Installation (Cloudflare) + | + Installation (Docker / Self-Hosted) | Issues | @@ -35,7 +37,7 @@ Codra listens to GitHub pull request events, runs AI-powered review jobs, posts ## Why Codra -- **Own the whole review loop**: Run the GitHub App, Cloudflare Worker, queue, database, model credentials, and dashboard under your own control. +- **Own the whole review loop**: Run the GitHub App, web server, queue, database, model credentials, and dashboard under your own control (deploy on Cloudflare Workers or your own VPS via Docker). - **Review with repository context**: Codra checks pull request diffs for correctness, security, performance, maintainability, and repo-specific patterns. - **Configure each repository**: Tune triggers, skipped paths, draft handling, mention reviews, labels, custom rules, and review budgets from the dashboard. - **Route models deliberately**: Use global defaults, per-repo model chains, fallbacks, and size-based overrides for larger pull requests. diff --git a/apps/node/Dockerfile b/apps/node/Dockerfile new file mode 100644 index 00000000..8f234223 --- /dev/null +++ b/apps/node/Dockerfile @@ -0,0 +1,43 @@ +FROM node:20-alpine AS builder +WORKDIR /app + +# Install build dependencies if needed +RUN apk add --no-cache python3 make g++ + +# Copy workspace and lock file +COPY package.json package-lock.json tsconfig.base.json ./ +COPY apps/node/package.json ./apps/node/ +COPY packages/api/package.json ./packages/api/ +COPY packages/core/package.json ./packages/core/ +COPY packages/db/package.json ./packages/db/ +COPY packages/models/package.json ./packages/models/ +COPY packages/provider-github/package.json ./packages/provider-github/ +COPY packages/schema/package.json ./packages/schema/ + +# Install all dependencies (with devDependencies for build) +RUN npm ci + +# Copy source code +COPY apps/node ./apps/node +COPY packages ./packages + +# Build the node server and its dependencies +RUN npm run build --workspace=@codraoss/node-server + +# Build runner stage +FROM node:20-alpine AS runner +WORKDIR /app + +ENV NODE_ENV=production + +# Copy built artifacts and necessary files +COPY --from=builder /app/node_modules ./node_modules +COPY --from=builder /app/apps/node/dist ./apps/node/dist +COPY --from=builder /app/apps/node/package.json ./apps/node/package.json + +# Copy packages that the node app might need at runtime +COPY --from=builder /app/packages ./packages + +EXPOSE 3000 + +CMD ["node", "apps/node/dist/index.js"] diff --git a/apps/node/src/index.ts b/apps/node/src/index.ts index 2590d44c..fe54fd6b 100644 --- a/apps/node/src/index.ts +++ b/apps/node/src/index.ts @@ -73,18 +73,22 @@ if (process.env.START_WORKER !== 'false') { logger.info('BullMQ Background Worker started.'); } -const server = serve({ - fetch: async (request) => { - const apiEnv = { - ...envWithAssets, - deps: createNodeApiDeps(env), - }; - try { return await runWithDb(env, () => app.fetch(request, apiEnv as any)); } catch (e) { console.error('SERVE ERROR:', e); throw e; } - }, - port, -}, (info) => { - logger.info(`Codra Node server running on http://localhost:${info.port}`); -}); +if (process.env.START_API !== 'false') { + const server = serve({ + fetch: async (request) => { + const apiEnv = { + ...envWithAssets, + deps: createNodeApiDeps(env), + }; + try { return await runWithDb(env, () => app.fetch(request, apiEnv as any)); } catch (e) { console.error('SERVE ERROR:', e); throw e; } + }, + port, + }, (info) => { + logger.info(`Codra Node server running on http://localhost:${info.port}`); + }); +} else { + logger.info('API Server disabled via START_API=false'); +} const shutdown = async () => { logger.info('Shutting down Codra Node server...'); diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..32ce5b0b --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,53 @@ +version: '3.8' + +services: + postgres: + image: postgres:15-alpine + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: password + POSTGRES_DB: codra + ports: + - "5432:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 5s + timeout: 5s + retries: 5 + + redis: + image: redis:7-alpine + ports: + - "6379:6379" + volumes: + - redis_data:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 5s + timeout: 5s + retries: 5 + + codra-app: + build: + context: . + dockerfile: apps/node/Dockerfile + ports: + - "3000:3000" + environment: + - DATABASE_URL=postgres://postgres:password@postgres:5432/codra + - REDIS_URL=redis://redis:6379 + - START_WORKER=true + - PORT=3000 + env_file: + - .env + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + +volumes: + postgres_data: + redis_data: diff --git a/docs/deployment/docker.md b/docs/deployment/docker.md new file mode 100644 index 00000000..edc0c922 --- /dev/null +++ b/docs/deployment/docker.md @@ -0,0 +1,88 @@ +# Self-Hosting with Docker + +While Codra is natively optimized for Cloudflare Workers, you can easily self-host the entire architecture on a dedicated server (like a VPS or EC2 instance) using Docker. This setup replaces Cloudflare infrastructure with standard open-source equivalents: + +* **Node.js** (Web Server and Job Runner) +* **PostgreSQL** (Database) +* **Redis** (Key-Value Store and Queues via BullMQ) + +## Prerequisites + +Before deploying, ensure your server has the following installed: +- [Docker](https://docs.docker.com/get-docker/) +- [Docker Compose](https://docs.docker.com/compose/install/) + +You will also need: +- A GitHub App configured for your organization (See [GitHub App Setup](../setup/github-app.md)). You will need the **App ID**, **Client ID**, **Client Secret**, **Webhook Secret**, and **Private Key**. +- An API Key for your chosen LLM provider (e.g., Anthropic, OpenAI, or Google). + +## Environment Setup + +Create a `.env` file in the root of the cloned repository. This file handles all your secrets. + +```env +# Server Configuration +APP_URL=https://codra.yourdomain.com +ENVIRONMENT=production + +# Database & Redis (these map to the docker-compose.yml service names) +DATABASE_URL=postgres://postgres:password@postgres:5432/codra +REDIS_URL=redis://redis:6379 + +# GitHub App Secrets +GITHUB_APP_ID=your_app_id +GITHUB_CLIENT_ID=your_client_id +GITHUB_CLIENT_SECRET=your_client_secret +GITHUB_APP_WEBHOOK_SECRET=your_webhook_secret +APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----" + +# Application Settings +AUTH_CALLBACK_URL=https://codra.yourdomain.com/api/auth/callback +DASHBOARD_ALLOWED_USERS=your_github_username +BOT_USERNAME=your-github-app-slug[bot] + +# Encryption Key (Must be 32 random characters for AES-256) +LLM_CONFIG_ENCRYPTION_KEY=generate_a_random_32_char_string +``` + +## Running the Stack + +Once your `.env` is prepared, deploying is a single command. From the root of the repository, run: + +```bash +docker compose up -d --build +``` + +This command will: +1. Compile the Codra Node app and its workspace dependencies. +2. Boot Postgres and Redis containers. +3. Start the Codra Node application container (which runs both the HTTP server and the background worker by default). + +To view logs: +```bash +docker compose logs -f codra-app +``` + +The application should now be accessible at `http://localhost:3000` (or whatever `PORT` you configured). + +## Advanced Configuration: Scaling (Unified Process Model) + +By default, the `codra-app` service boots *both* the web server (Hono API) and the background worker (BullMQ) in a single Node process. + +For high-traffic environments, you may want to scale the API web nodes independently of the heavy AI review worker nodes. + +You can split these by overriding the `START_WORKER` and `START_API` environment variables across different containers. + +**API Node:** +```env +START_WORKER=false +# START_API=true (Default) +``` + +**Worker Node:** +```env +START_WORKER=true +START_API=false # Disables the HTTP server +``` + +This allows you to spin up multiple worker containers listening to Redis while load-balancing incoming HTTP requests to your API nodes. From b3c0f70872f8afb662a59f0ae0b3823b29eb1825 Mon Sep 17 00:00:00 2001 From: Devarshi Shimpi Date: Tue, 22 Sep 2026 20:12:40 +0530 Subject: [PATCH 03/16] feat: integrate BullMQ across node app with improved docker configuration --- apps/node/Dockerfile | 27 +++++++++++-- apps/node/src/adapters/node-orchestrator.ts | 42 ++++++++++----------- apps/node/src/adapters/services.ts | 2 +- apps/node/src/api-deps.ts | 16 ++++++-- apps/node/src/env.ts | 4 +- apps/node/src/index.ts | 29 ++++---------- apps/node/src/runtime.ts | 2 +- apps/node/src/worker.ts | 7 ++-- docker-compose.yml | 6 +++ 9 files changed, 79 insertions(+), 56 deletions(-) diff --git a/apps/node/Dockerfile b/apps/node/Dockerfile index 8f234223..537bfc19 100644 --- a/apps/node/Dockerfile +++ b/apps/node/Dockerfile @@ -32,12 +32,33 @@ ENV NODE_ENV=production # Copy built artifacts and necessary files COPY --from=builder /app/node_modules ./node_modules -COPY --from=builder /app/apps/node/dist ./apps/node/dist + COPY --from=builder /app/apps/node/package.json ./apps/node/package.json +COPY --from=builder /app/apps/node/dist ./apps/node/dist + +# Selectively copy only workspace packages' dist folders and package.json files +COPY --from=builder /app/packages/api/package.json ./packages/api/ +COPY --from=builder /app/packages/api/dist ./packages/api/dist + +COPY --from=builder /app/packages/core/package.json ./packages/core/ +COPY --from=builder /app/packages/core/dist ./packages/core/dist -# Copy packages that the node app might need at runtime -COPY --from=builder /app/packages ./packages +COPY --from=builder /app/packages/db/package.json ./packages/db/ +COPY --from=builder /app/packages/db/dist ./packages/db/dist + +COPY --from=builder /app/packages/models/package.json ./packages/models/ +COPY --from=builder /app/packages/models/dist ./packages/models/dist + +COPY --from=builder /app/packages/provider-github/package.json ./packages/provider-github/ +COPY --from=builder /app/packages/provider-github/dist ./packages/provider-github/dist + +COPY --from=builder /app/packages/schema/package.json ./packages/schema/ +COPY --from=builder /app/packages/schema/dist ./packages/schema/dist + +USER node EXPOSE 3000 +HEALTHCHECK --interval=30s --timeout=3s CMD wget -qO- http://localhost:3000/health || exit 1 + CMD ["node", "apps/node/dist/index.js"] diff --git a/apps/node/src/adapters/node-orchestrator.ts b/apps/node/src/adapters/node-orchestrator.ts index 32fe8f4c..28c1080a 100644 --- a/apps/node/src/adapters/node-orchestrator.ts +++ b/apps/node/src/adapters/node-orchestrator.ts @@ -1,37 +1,35 @@ -import type { JobOrchestrator, ReviewRuntime } from '@codraoss/core'; +import type { JobOrchestrator, QueueProducer, ReviewRuntime } from '@codraoss/core'; import type { ReviewJobMessage } from '@codraoss/schema'; import { runReview } from '@codraoss/core'; import { runWithDb } from '@codraoss/db/client'; import type { DbEnv } from '@codraoss/db/env'; -import { setTimeout } from 'node:timers/promises'; export class NodeOrchestrator implements JobOrchestrator { - constructor(private readonly env: ReviewRuntime & DbEnv) {} + constructor( + private readonly env: ReviewRuntime & DbEnv, + private readonly queue: QueueProducer + ) {} async startReviewJob(id: string, params: ReviewJobMessage): Promise { return runWithDb(this.env, async () => { - let currentParams = { ...params }; - let phase = currentParams.phase ?? 'prepare'; + const currentParams = { ...params }; + currentParams.phase = currentParams.phase ?? 'prepare'; - while (phase) { - currentParams.phase = phase; - const result = await runReview(this.env, currentParams); + const result = await runReview(this.env, currentParams); - if (result.action === 'next_phase') { - phase = result.phase; - if (result.jobId) { - currentParams.jobId = result.jobId; - } - if (result.delaySeconds > 0) { - await setTimeout(result.delaySeconds * 1000); - } - } else if (result.action === 'retry') { - if (result.delaySeconds > 0) { - await setTimeout(result.delaySeconds * 1000); - } - } else if (result.action === 'ack') { - break; + if (result.action === 'next_phase') { + const nextParams: ReviewJobMessage = { + ...currentParams, + phase: result.phase, + }; + if (result.jobId) { + nextParams.jobId = result.jobId; } + await this.queue.send(nextParams, { delaySeconds: result.delaySeconds }); + } else if (result.action === 'retry') { + await this.queue.send(currentParams, { delaySeconds: result.delaySeconds }); + } else if (result.action === 'ack') { + // Job is done, nothing to enqueue } }); } diff --git a/apps/node/src/adapters/services.ts b/apps/node/src/adapters/services.ts index 6be696d0..5b36b2ba 100644 --- a/apps/node/src/adapters/services.ts +++ b/apps/node/src/adapters/services.ts @@ -16,7 +16,7 @@ export function makeModelFactory(env: NodeAppBindings) { secretStore: { getSecret: async (key) => env[key as keyof NodeAppBindings] as string || process.env[key] || null, }, - getConfig: (modelId) => getResolvedModelConfig({ HYPERDRIVE: env.HYPERDRIVE, APP_KV: env.APP_KV }, modelId), + getConfig: (modelId) => getResolvedModelConfig({ HYPERDRIVE: env.DATABASE_CONFIG, APP_KV: env.APP_KV }, modelId), aiBinding: undefined, tracker, jobId, diff --git a/apps/node/src/api-deps.ts b/apps/node/src/api-deps.ts index 930c5af2..537e4568 100644 --- a/apps/node/src/api-deps.ts +++ b/apps/node/src/api-deps.ts @@ -2,19 +2,29 @@ import { createSharedApiDeps } from '@codraoss/api'; import type { NodeAppBindings } from './env'; import { logger } from '@codraoss/api/logger'; import { createReviewRuntime } from './runtime'; +import { Queue } from 'bullmq'; +import Redis from 'ioredis'; export function createNodeApiDeps(env: NodeAppBindings) { return createSharedApiDeps({ sessionStore: env.SESSION_STORE, kv: env.APP_KV, - db: { HYPERDRIVE: env.HYPERDRIVE, APP_KV: env.APP_KV, workerMode: false }, + db: { HYPERDRIVE: env.DATABASE_CONFIG, APP_KV: env.APP_KV, workerMode: false }, identityProvider: env.IDENTITY_PROVIDER, enqueueReviewJob: async (input) => { await env.REVIEW_QUEUE.send(input); }, - terminateJobWorkflow: async (_job) => { - logger.warn('[STUB] terminateJobWorkflow called'); + terminateJobWorkflow: async (job) => { + const workerQueue = new Queue('codra-reviews', { connection: new Redis(process.env.REDIS_URL || 'redis://localhost:6379') }); + const bullMqJob = await workerQueue.getJob(job.id); + if (bullMqJob) { + await bullMqJob.remove(); + logger.info(`[API Deps] Terminated job workflow for job ${job.id}`); + } else { + logger.warn(`[API Deps] Attempted to terminate non-existent or completed job ${job.id}`); + } + await workerQueue.close(); }, scheduleBestEffortJobMaintenance: () => { // In node, this is a long running process, we can just spawn a promise. diff --git a/apps/node/src/env.ts b/apps/node/src/env.ts index 4605dac7..302cf134 100644 --- a/apps/node/src/env.ts +++ b/apps/node/src/env.ts @@ -9,7 +9,7 @@ export interface NodeAppBindings { REVIEW_ORCHESTRATOR: JobOrchestrator; // ── Database ── - HYPERDRIVE: { connectionString: string }; + DATABASE_CONFIG: { connectionString: string }; // ── Environment strings (loaded from process.env via dotenv) ── APP_PRIVATE_KEY: string; @@ -41,7 +41,7 @@ export function createNodeEnv(stubs: { return { ...stubs, REVIEW_ORCHESTRATOR: stubs.REVIEW_ORCHESTRATOR!, - HYPERDRIVE: { connectionString: requireEnv('DATABASE_URL') }, + DATABASE_CONFIG: { connectionString: requireEnv('DATABASE_URL') }, APP_PRIVATE_KEY: requireEnv('APP_PRIVATE_KEY'), GITHUB_APP_ID: requireEnv('GITHUB_APP_ID'), GITHUB_APP_SLUG: process.env.GITHUB_APP_SLUG, diff --git a/apps/node/src/index.ts b/apps/node/src/index.ts index fe54fd6b..7f0b3e60 100644 --- a/apps/node/src/index.ts +++ b/apps/node/src/index.ts @@ -33,30 +33,13 @@ const stubs = { }; const env = createNodeEnv(stubs); -env.REVIEW_ORCHESTRATOR = new NodeOrchestrator(createReviewRuntime(env)); +env.REVIEW_ORCHESTRATOR = new NodeOrchestrator(createReviewRuntime(env), stubs.REVIEW_QUEUE); import fs from 'node:fs'; import { serveStatic } from '@hono/node-server/serve-static'; const dashboardDist = path.resolve(process.cwd(), process.cwd().endsWith('node') ? '../../dist/client' : 'dist/client'); -const envWithAssets = { - ...env, - ASSETS: { - fetch: async (_req: Request) => { - try { - console.log('fetching index.html from', path.join(dashboardDist, 'index.html')); - const html = fs.readFileSync(path.join(dashboardDist, 'index.html'), 'utf-8'); - console.log('html length:', html.length); - return new Response(html, { headers: { 'content-type': 'text/html' } }); - } catch (e) { - console.error('ASSETS error:', e); - return new Response('Dashboard build not found. Run npm run build -w @codraoss/dashboard', { status: 404 }); - } - } - } -}; - const app = createApiRouter(); app.onError((err, c) => { console.error('HONO ERROR:', err); @@ -65,22 +48,26 @@ app.onError((err, c) => { app.use('/assets/*', serveStatic({ root: process.cwd().endsWith('node') ? '../../dist/client' : 'dist/client' })); app.use('/*.svg', serveStatic({ root: process.cwd().endsWith('node') ? '../../dist/client' : 'dist/client' })); app.use('/*.ico', serveStatic({ root: process.cwd().endsWith('node') ? '../../dist/client' : 'dist/client' })); +app.get('*', serveStatic({ root: process.cwd().endsWith('node') ? '../../dist/client' : 'dist/client', path: 'index.html' })); + const port = parseInt(process.env.PORT || '3000', 10); let worker: ReturnType | undefined; if (process.env.START_WORKER !== 'false') { - worker = startWorker(env, redisUrl); + worker = startWorker(env, redisUrl, stubs.REVIEW_QUEUE); logger.info('BullMQ Background Worker started.'); } if (process.env.START_API !== 'false') { + app.get('/health', (c) => c.text('OK')); + const server = serve({ fetch: async (request) => { const apiEnv = { - ...envWithAssets, + ...env, deps: createNodeApiDeps(env), }; - try { return await runWithDb(env, () => app.fetch(request, apiEnv as any)); } catch (e) { console.error('SERVE ERROR:', e); throw e; } + try { return await runWithDb({ ...env, HYPERDRIVE: env.DATABASE_CONFIG }, () => app.fetch(request, apiEnv as any)); } catch (e) { console.error('SERVE ERROR:', e); throw e; } }, port, }, (info) => { diff --git a/apps/node/src/runtime.ts b/apps/node/src/runtime.ts index e6e2b8c7..935e74b5 100644 --- a/apps/node/src/runtime.ts +++ b/apps/node/src/runtime.ts @@ -21,7 +21,7 @@ import { } from './adapters/services'; export function createReviewRuntime(env: NodeAppBindings): ReviewRuntime & DbEnv { - const dbEnv = { HYPERDRIVE: env.HYPERDRIVE, APP_KV: env.APP_KV, workerMode: false }; + const dbEnv = { HYPERDRIVE: env.DATABASE_CONFIG, APP_KV: env.APP_KV, workerMode: false }; return { ...dbEnv, diff --git a/apps/node/src/worker.ts b/apps/node/src/worker.ts index 3da94dac..3e3bebf4 100644 --- a/apps/node/src/worker.ts +++ b/apps/node/src/worker.ts @@ -1,12 +1,13 @@ import { Worker, type Job } from 'bullmq'; -import { reviewJobMessageSchema } from '@codraoss/schema'; +import { reviewJobMessageSchema, type ReviewJobMessage } from '@codraoss/schema'; import { NodeOrchestrator } from './adapters/node-orchestrator'; import { logger } from '@codraoss/api/logger'; import type { NodeAppBindings } from './env'; import Redis from 'ioredis'; import { createReviewRuntime } from './runtime'; +import type { QueueProducer } from '@codraoss/core/ports'; -export function startWorker(env: NodeAppBindings, redisUrl: string): Worker { +export function startWorker(env: NodeAppBindings, redisUrl: string, queue: QueueProducer): Worker { const connection = new Redis(redisUrl, { maxRetriesPerRequest: null }); const worker = new Worker( @@ -19,7 +20,7 @@ export function startWorker(env: NodeAppBindings, redisUrl: string): Worker { } const reviewRuntime = createReviewRuntime(env); - const orchestrator = new NodeOrchestrator(reviewRuntime); + const orchestrator = new NodeOrchestrator(reviewRuntime, queue); await orchestrator.startReviewJob(job.id ?? 'unknown', parseResult.data); }, { connection } diff --git a/docker-compose.yml b/docker-compose.yml index 32ce5b0b..430cb958 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -47,6 +47,12 @@ services: condition: service_healthy redis: condition: service_healthy + stop_grace_period: 2m + healthcheck: + test: ["CMD-SHELL", "wget -qO- http://localhost:3000/health || exit 1"] + interval: 30s + timeout: 3s + retries: 3 volumes: postgres_data: From 22533151abcb98a2b1277be229f58f0d7c9f16c8 Mon Sep 17 00:00:00 2001 From: Devarshi Shimpi Date: Tue, 22 Sep 2026 22:53:07 +0530 Subject: [PATCH 04/16] refactor: extract node adapters to @codraoss/node-adapters package --- .changeset/README.md | 33 -- .changeset/config.json | 21 - CHANGELOG.md | 81 --- README.md | 3 +- apps/node/package.json | 3 +- apps/node/src/adapters/redis-queue.ts | 14 - apps/node/src/api-deps.ts | 11 +- apps/node/src/index.ts | 82 ++- apps/node/src/runtime.ts | 2 +- apps/worker/package.json | 2 +- package-lock.json | 530 +++--------------- package.json | 5 +- packages/api/CHANGELOG.md | 23 - packages/api/package.json | 51 +- packages/core/CHANGELOG.md | 9 - packages/core/package.json | 4 +- packages/core/src/ports/queue.ts | 1 + packages/db/CHANGELOG.md | 11 - packages/db/package.json | 6 +- packages/db/src/client.ts | 8 +- packages/models/CHANGELOG.md | 16 - packages/models/package.json | 6 +- packages/node-adapters/package.json | 58 ++ packages/node-adapters/src/index.ts | 6 + .../node-adapters/src}/node-orchestrator.ts | 0 .../node-adapters/src}/platform.ts | 7 +- .../node-adapters/src}/redis-kv.ts | 0 packages/node-adapters/src/redis-queue.ts | 22 + .../node-adapters/src}/redis-session-store.ts | 7 +- .../node-adapters}/src/worker.ts | 24 +- packages/node-adapters/tsconfig.json | 10 + packages/node-adapters/tsup.config.json | 10 + packages/provider-github/CHANGELOG.md | 10 - packages/provider-github/package.json | 6 +- packages/schema/CHANGELOG.md | 7 - packages/schema/package.json | 2 +- packages/ui/CHANGELOG.md | 9 - packages/ui/package.json | 4 +- scripts/swap-publish-exports.mjs | 1 + 39 files changed, 320 insertions(+), 785 deletions(-) delete mode 100644 .changeset/README.md delete mode 100644 .changeset/config.json delete mode 100644 CHANGELOG.md delete mode 100644 apps/node/src/adapters/redis-queue.ts delete mode 100644 packages/api/CHANGELOG.md delete mode 100644 packages/core/CHANGELOG.md delete mode 100644 packages/db/CHANGELOG.md delete mode 100644 packages/models/CHANGELOG.md create mode 100644 packages/node-adapters/package.json create mode 100644 packages/node-adapters/src/index.ts rename {apps/node/src/adapters => packages/node-adapters/src}/node-orchestrator.ts (100%) rename {apps/node/src/adapters => packages/node-adapters/src}/platform.ts (51%) rename {apps/node/src/adapters => packages/node-adapters/src}/redis-kv.ts (100%) create mode 100644 packages/node-adapters/src/redis-queue.ts rename {apps/node/src/adapters => packages/node-adapters/src}/redis-session-store.ts (87%) rename {apps/node => packages/node-adapters}/src/worker.ts (57%) create mode 100644 packages/node-adapters/tsconfig.json create mode 100644 packages/node-adapters/tsup.config.json delete mode 100644 packages/provider-github/CHANGELOG.md delete mode 100644 packages/schema/CHANGELOG.md delete mode 100644 packages/ui/CHANGELOG.md diff --git a/.changeset/README.md b/.changeset/README.md deleted file mode 100644 index 697e6f70..00000000 --- a/.changeset/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# Changesets - -This folder is managed by [Changesets](https://github.com/changesets/changesets), used here only for -**local, manual** versioning of the public `@codraoss/*` packages. There is no CI-based publishing. - -The seven publishable packages (`schema`, `core`, `db`, `models`, `provider-github`, `api`, `ui`) -are a **fixed group** — they version and release in lockstep (currently `0.9.4`). The worker app -(`@codraoss/worker`) is private and never published. - -## Recording a change - -```bash -npx changeset # pick the bump, write a summary; commit the generated file -``` - -## Cutting a release (run locally, then publish by hand) - -```bash -npm run version:packages # applies pending changesets: bumps all @codraoss/* in lockstep + changelog -npm run release # builds dist and runs `changeset publish` for you -``` - -`npm run release` builds every package to `dist/` and publishes. Publishing is also possible per -package with plain npm — the `prepack` hook rewrites `exports` to the compiled `dist/` paths in the -tarball automatically: - -```bash -npm run build:packages -npm publish -w @codraoss/schema # ...repeat bottom-up: schema → core → db/models/provider-github → api → ui -``` - -You must be logged in to npm (`npm whoami`) and own the `@codraoss` scope. First publish of each -package needs public access, which `publishConfig.access` already sets. diff --git a/.changeset/config.json b/.changeset/config.json deleted file mode 100644 index eec496e2..00000000 --- a/.changeset/config.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json", - "changelog": "@changesets/cli/changelog", - "commit": false, - "fixed": [ - [ - "@codraoss/schema", - "@codraoss/core", - "@codraoss/db", - "@codraoss/models", - "@codraoss/provider-github", - "@codraoss/api", - "@codraoss/ui" - ] - ], - "linked": [], - "access": "public", - "baseBranch": "main", - "updateInternalDependencies": "patch", - "ignore": ["@codraoss/worker"] -} diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 18d490c8..00000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,81 +0,0 @@ -# Changelog - -## v0.9.5 - -### What's New - -- **Monorepo & Public npm Packages:** Codra is now an npm workspace monorepo, with the review engine, model adapters, GitHub provider, API router, and design system published under `@codraoss/*`. -- **Evidence-Grounded Findings:** Findings must quote the diff they claim to describe; candidates that can't ground themselves are dropped before posting. -- **Precision Gates & Gatekeeper Verifier:** A claim-type denylist, a dedicated verifier pass, and disposition tracking for degraded reviews. -- **Batched Reviews & New Providers:** Files are packed into bins with structured Gemini output; Vertex AI, NVIDIA, and xAI joined the model catalog. -- **Language Personas & Secondary Reviewer:** Configurable per-language personas and an optional second reviewer pass. -- **Clean-Pass Summaries:** A review that finds nothing now posts a real summary and reacts with a thumbs-up instead of an empty comment. -- **Job Detail & Stats Overhaul:** Rebuilt views with new chart primitives and bucketed trends. - -### Bug Fixes - -- Fixed `GET /api/jobs/:id` returning 503s while polling — Cloudflare's edge rewrites strong ETags to weak ones, so the 304 path never matched. -- Dropped an invalid `propertyOrdering` keyword from Gemini response grammars that was silently disabling constrained decoding on every Google review. -- Fixed issue-accuracy and "files reviewed" reporting, telemetry/repo settings UI, and CI dependency install failures. - -**Full Changelog**: https://github.com/devarshishimpi/codra/compare/v0.9.4...v0.9.5 - -## v0.9.4 - -### What's New - -- **Job Cancellation & Deletion:** Review jobs can now be cancelled and deleted directly, with improved job status handling. -- **Telemetry Opt-Out:** Anonymous telemetry now ships with a clear opt-out and support for overriding the telemetry secret. -- **Concurrency Controls:** Model calls now respect configurable concurrency limits, with enhanced timeout handling to keep reviews from stalling. -- **Custom File Matchers:** Diff parsing and the review flow were refactored to support custom file matchers. -- **Review Performance Settings:** New settings and UI for tuning review performance. -- **Motion UI:** Smooth scrolling (Lenis) and new motion-based sidebar navigation, tabs, and select components. -- **New Installs Experience:** Improved UI and skeleton loaders for new installs. - -### Improvements - -- Added in-memory token caching for GitHub requests to reduce redundant API calls. -- Job continuation tracking added so long-running reviews resume more reliably under concurrency limits. -- Improved handling of subrequest budget limits during review job processing. -- Enhanced review job concurrency handling and caching mechanisms. -- Improved model service caching, timeout handling, and review settings API, with added tests for concurrency limits. -- Enhanced CI configuration, select component accessibility, and optimized stats queries. -- Refactored stats page and model configurations. -- Removed dead letter queue (DLQ) functionality and related dead code. -- Updated font imports and improved the jobs search bar UI, with added stale repo cleanup. -- Bumped vulnerable packages. - -### Bug Fixes - -- Fixed a failing Google API test case. -- Fixed `onMouseEnter` typecheck issue. -- Fixed sidebar active button colors. - -**Full Changelog**: https://github.com/devarshishimpi/codra/commits/v0.9.4 - -## v0.9.2 - -### What's New - -- **UI Redesign:** Full visual overhaul of the dashboard, landing page, and auth flows with an updated color system, improved dark mode, and a cleaner layout overall. -- **Code Splitting:** All pages now use `React.lazy` for async loading, reducing initial bundle size and improving load times. -- **Custom LLM Providers:** Manage custom OpenAI-compatible API providers directly from the dashboard. Rate limits are optional. No more hardcoded keys in wrangler config. -- **Cloudflare Setup Script:** A new Node.js script automates the full Cloudflare deployment setup, making self-hosting significantly easier. -- **Increased Review Capacity:** Max files processed per review raised from 15 to 100. - -### Improvements - -- Review jobs are now resumable and lease-aware, stalled reviews can recover automatically. -- Added retry logic for transient model provider failures. -- Optimized job polling with ETag caching and adaptive delays. -- Improved settings UI, error reporting, and API robustness across the board. -- Added GitHub Actions CI with a disposable test environment for the full test suite. - -### Bug Fixes - -- Fixed `APP_PRIVATE_KEY` parsing for single-line strings with literal `\n` sequences. -- Fixed database migration failures on existing deployments. -- Fixed duplicate `/api/auth/updates-email` calls on page load. -- Fixed file review status bar rendering on the dashboard. - -**Full Changelog**: https://github.com/devarshishimpi/codra/commits/v0.9.2 diff --git a/README.md b/README.md index a0cd1a53..d1a24991 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ License: AGPL-3.0 Sponsor Cloudflare Workers + Node.js Built with React TypeScript

@@ -33,7 +34,7 @@ Codra listens to GitHub pull request events, runs AI-powered review jobs, posts inline findings back to the PR, and gives you a dashboard to inspect jobs, repositories, model routing, review history, and failed queue runs. -> **Beta** - Codra is under active development. Expect rough edges, missing features, and breaking changes between releases. Feedback and bug reports are welcome via [GitHub Issues](https://github.com/devarshishimpi/codra/issues). +> **Beta** - Codra is under active development. Cloudflare Worker is stable for self-hosted use; Node standalone is newer and still being hardened. Expect rough edges. Bugs and feedback welcome via [GitHub Issues](https://github.com/devarshishimpi/codra/issues). ## Why Codra diff --git a/apps/node/package.json b/apps/node/package.json index d509b275..895968ed 100644 --- a/apps/node/package.json +++ b/apps/node/package.json @@ -1,6 +1,6 @@ { "name": "@codraoss/node-server", - "version": "0.9.6", + "version": "0.9.12", "private": true, "type": "module", "scripts": { @@ -14,6 +14,7 @@ "@codraoss/core": "*", "@codraoss/db": "*", "@codraoss/models": "*", + "@codraoss/node-adapters": "*", "@codraoss/provider-github": "*", "@codraoss/schema": "*", "@hono/node-server": "^1.11.1", diff --git a/apps/node/src/adapters/redis-queue.ts b/apps/node/src/adapters/redis-queue.ts deleted file mode 100644 index 643d94e1..00000000 --- a/apps/node/src/adapters/redis-queue.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { QueueProducer } from '@codraoss/core/ports'; -import type { Queue } from 'bullmq'; - -export class RedisQueueAdapter implements QueueProducer { - constructor(private readonly queue: Queue) {} - - async send(message: T, options?: { delaySeconds?: number }): Promise { - const jobOptions = options?.delaySeconds - ? { delay: options.delaySeconds * 1000 } - : undefined; - - await this.queue.add('review-job', message, jobOptions); - } -} diff --git a/apps/node/src/api-deps.ts b/apps/node/src/api-deps.ts index 537e4568..ac0f4f44 100644 --- a/apps/node/src/api-deps.ts +++ b/apps/node/src/api-deps.ts @@ -2,8 +2,6 @@ import { createSharedApiDeps } from '@codraoss/api'; import type { NodeAppBindings } from './env'; import { logger } from '@codraoss/api/logger'; import { createReviewRuntime } from './runtime'; -import { Queue } from 'bullmq'; -import Redis from 'ioredis'; export function createNodeApiDeps(env: NodeAppBindings) { return createSharedApiDeps({ @@ -16,15 +14,12 @@ export function createNodeApiDeps(env: NodeAppBindings) { await env.REVIEW_QUEUE.send(input); }, terminateJobWorkflow: async (job) => { - const workerQueue = new Queue('codra-reviews', { connection: new Redis(process.env.REDIS_URL || 'redis://localhost:6379') }); - const bullMqJob = await workerQueue.getJob(job.id); - if (bullMqJob) { - await bullMqJob.remove(); + if (env.REVIEW_QUEUE.deleteJob) { + await env.REVIEW_QUEUE.deleteJob(job.id); logger.info(`[API Deps] Terminated job workflow for job ${job.id}`); } else { - logger.warn(`[API Deps] Attempted to terminate non-existent or completed job ${job.id}`); + logger.warn(`[API Deps] QueueAdapter does not support deleteJob. Cannot terminate ${job.id}`); } - await workerQueue.close(); }, scheduleBestEffortJobMaintenance: () => { // In node, this is a long running process, we can just spawn a promise. diff --git a/apps/node/src/index.ts b/apps/node/src/index.ts index 7f0b3e60..ca7f9795 100644 --- a/apps/node/src/index.ts +++ b/apps/node/src/index.ts @@ -2,23 +2,31 @@ import * as path from 'node:path'; dotenv.config({ path: path.resolve(process.cwd(), '../../.dev.vars') }); // Fallback if running from apps/node dotenv.config({ path: path.resolve(process.cwd(), '.dev.vars') }); // Root level + import { serve } from '@hono/node-server'; import { createApiRouter } from '@codraoss/api'; import { runWithDb } from '@codraoss/db/client'; + +import { NodeOrchestrator, RedisKVAdapter, RedisQueueAdapter, RedisSessionStore, startWorker } from '@codraoss/node-adapters'; import { createNodeApiDeps } from './api-deps'; -import { createNodeEnv } from './env'; +import { createNodeEnv, NodeAppBindings } from './env'; import { logger } from '@codraoss/api/logger'; import Redis from 'ioredis'; -import { RedisKVAdapter } from './adapters/redis-kv'; import { Queue } from 'bullmq'; -import { RedisQueueAdapter } from './adapters/redis-queue'; -import { NodeOrchestrator } from './adapters/node-orchestrator'; -import { startWorker } from './worker'; + +import { serveStatic } from '@hono/node-server/serve-static'; +import { fileURLToPath } from 'url'; +import { dirname } from 'path'; import { createReviewRuntime } from './runtime'; -import { RedisSessionStore } from './adapters/redis-session-store'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); const redisUrl = process.env.REDIS_URL || 'redis://localhost:6379'; -const redisClient = new Redis(redisUrl, { maxRetriesPerRequest: null }); // maxRetriesPerRequest: null is required for bullmq +const redisClient = new Redis(redisUrl, { + maxRetriesPerRequest: null, + tls: redisUrl.startsWith('rediss://') ? { rejectUnauthorized: false } : undefined, // For managed Redis +}); redisClient.on('error', (err) => { logger.error('[Redis Error]', err); }); @@ -27,67 +35,93 @@ const reviewQueue = new Queue('codra-reviews', { connection: redisClient }); const stubs = { SESSION_STORE: new RedisSessionStore(redisClient), - APP_KV: new RedisKVAdapter(redisClient), + KV_STORE: new RedisKVAdapter(redisClient), REVIEW_QUEUE: new RedisQueueAdapter(reviewQueue), REVIEW_ORCHESTRATOR: undefined, // Will be set to NodeOrchestrator below }; -const env = createNodeEnv(stubs); +const env: NodeAppBindings = createNodeEnv(stubs); env.REVIEW_ORCHESTRATOR = new NodeOrchestrator(createReviewRuntime(env), stubs.REVIEW_QUEUE); -import fs from 'node:fs'; -import { serveStatic } from '@hono/node-server/serve-static'; - -const dashboardDist = path.resolve(process.cwd(), process.cwd().endsWith('node') ? '../../dist/client' : 'dist/client'); - const app = createApiRouter(); + app.onError((err, c) => { console.error('HONO ERROR:', err); return c.text('Custom Error: ' + err.message, 500); }); -app.use('/assets/*', serveStatic({ root: process.cwd().endsWith('node') ? '../../dist/client' : 'dist/client' })); -app.use('/*.svg', serveStatic({ root: process.cwd().endsWith('node') ? '../../dist/client' : 'dist/client' })); -app.use('/*.ico', serveStatic({ root: process.cwd().endsWith('node') ? '../../dist/client' : 'dist/client' })); -app.get('*', serveStatic({ root: process.cwd().endsWith('node') ? '../../dist/client' : 'dist/client', path: 'index.html' })); + +// Static file serving +const distClientPath = path.resolve(__dirname, process.cwd().endsWith('node/dist') ? '../../dist/client' : '../dist/client'); +app.use('/assets/*', serveStatic({ root: distClientPath })); +app.use('/*.svg', serveStatic({ root: distClientPath })); +app.use('/*.ico', serveStatic({ root: distClientPath })); +app.get('*', serveStatic({ root: distClientPath, path: 'index.html' })); + +app.get('/health', (c) => c.text('OK')); // Healthcheck endpoint const port = parseInt(process.env.PORT || '3000', 10); let worker: ReturnType | undefined; if (process.env.START_WORKER !== 'false') { - worker = startWorker(env, redisUrl, stubs.REVIEW_QUEUE); + worker = startWorker(redisClient, stubs.REVIEW_QUEUE, () => createReviewRuntime(env), logger); logger.info('BullMQ Background Worker started.'); } if (process.env.START_API !== 'false') { - app.get('/health', (c) => c.text('OK')); - const server = serve({ fetch: async (request) => { const apiEnv = { ...env, deps: createNodeApiDeps(env), }; + // Ensure runWithDb receives correct DbEnv type try { return await runWithDb({ ...env, HYPERDRIVE: env.DATABASE_CONFIG }, () => app.fetch(request, apiEnv as any)); } catch (e) { console.error('SERVE ERROR:', e); throw e; } }, port, + hostname: '0.0.0.0', }, (info) => { logger.info(`Codra Node server running on http://localhost:${info.port}`); }); -} else { - logger.info('API Server disabled via START_API=false'); + + // Add server to shutdown sequence + if (server) { + const originalClose = server.close; + server.close = () => new Promise(resolve => { + originalClose(() => { + logger.info('HTTP server closed.'); + resolve(); + }); + }); + } } const shutdown = async () => { logger.info('Shutting down Codra Node server...'); + + // Close BullMQ worker first to prevent new jobs if (worker) { logger.info('Waiting for active BullMQ jobs to finish...'); await worker.close(); logger.info('BullMQ Worker closed gracefully.'); } + + // Close Redis connection + if (redisClient) { + logger.info('Closing Redis client...'); + await redisClient.quit(); + logger.info('Redis client closed.'); + } + + // Close Postgres connection pool + // The postgres.js client automatically manages its pool. + // runWithDb client is scoped, but a top-level client for specific operations might exist in the future + // For now, no explicit close needed for `postgres.js` unless it's a global client. + // If `runWithDb` is the only entry point, it manages its own connections. + + logger.info('All services stopped. Exiting.'); process.exit(0); }; process.on('SIGTERM', shutdown); process.on('SIGINT', shutdown); -// Trigger restart for env vars diff --git a/apps/node/src/runtime.ts b/apps/node/src/runtime.ts index 935e74b5..42c829d2 100644 --- a/apps/node/src/runtime.ts +++ b/apps/node/src/runtime.ts @@ -10,7 +10,7 @@ import { makeJobStore, makeFileReviewStore, makeLearningStore, makeModelConfigRe import { loadRepoConfig } from '@codraoss/api/platform'; // Node-specific adapters -import { cryptoIds, systemClock } from './adapters/platform'; +import { cryptoIds, systemClock } from '@codraoss/node-adapters'; import { makeTelemetrySink } from './adapters/telemetry'; import { makeFormatterFactory, diff --git a/apps/worker/package.json b/apps/worker/package.json index 17115003..19f3f136 100644 --- a/apps/worker/package.json +++ b/apps/worker/package.json @@ -1,6 +1,6 @@ { "name": "@codraoss/worker", - "version": "0.9.6", + "version": "0.9.12", "private": true, "type": "module", "dependencies": { diff --git a/package-lock.json b/package-lock.json index b88d9a71..174725ae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -45,7 +45,6 @@ }, "devDependencies": { "@arethetypeswrong/cli": "^0.18.5", - "@changesets/cli": "^3.0.0", "@eslint/js": "^10.0.1", "@tailwindcss/vite": "^4.2.2", "@testing-library/dom": "^10.4.1", @@ -75,12 +74,13 @@ }, "apps/node": { "name": "@codraoss/node-server", - "version": "0.9.6", + "version": "0.9.12", "dependencies": { "@codraoss/api": "*", "@codraoss/core": "*", "@codraoss/db": "*", "@codraoss/models": "*", + "@codraoss/node-adapters": "*", "@codraoss/provider-github": "*", "@codraoss/schema": "*", "@hono/node-server": "^1.11.1", @@ -113,7 +113,7 @@ }, "apps/worker": { "name": "@codraoss/worker", - "version": "0.9.6", + "version": "0.9.12", "dependencies": { "@codraoss/api": "*", "@codraoss/core": "*", @@ -638,289 +638,6 @@ "specificity": "bin/cli.js" } }, - "node_modules/@changesets/apply-release-plan": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@changesets/apply-release-plan/-/apply-release-plan-8.0.0.tgz", - "integrity": "sha512-kUd2pbf1w5/AYmBMb0Tt+rkIPCjFJdT0SZMrkOjJT/WV/QbtmvkyB5jkV0oaNPheavprZk+SfUiozUti7TIL2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@changesets/config": "^4.0.0", - "@changesets/format": "^0.1.1", - "@changesets/git": "^4.0.0", - "@changesets/should-skip-package": "^1.0.0", - "@changesets/types": "^7.0.0", - "import-meta-resolve": "^4.2.0", - "jsonc-parser": "^3.3.1", - "semver": "^7.8.1" - }, - "engines": { - "node": "^22.11 || ^24 || >=26" - } - }, - "node_modules/@changesets/assemble-release-plan": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@changesets/assemble-release-plan/-/assemble-release-plan-7.0.0.tgz", - "integrity": "sha512-oEW8BxdA604kGGtDSCiHr5w9Tv4UWe9I2k61IBNZzCOE1kbYaJj4v+lFQNgcEZFkUc2pV/+hASErGDvpJOZCTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@changesets/errors": "^1.0.0", - "@changesets/get-dependents-graph": "^3.0.0", - "@changesets/should-skip-package": "^1.0.0", - "@changesets/types": "^7.0.0", - "semver": "^7.8.1" - }, - "engines": { - "node": "^22.11 || ^24 || >=26" - } - }, - "node_modules/@changesets/changelog-git": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@changesets/changelog-git/-/changelog-git-1.0.0.tgz", - "integrity": "sha512-3Dst2Ime2Op5nd4XmWJLPIgp11ZFqJqSkVug9izK6TDcIV4YlhPS4ECbEVR+eGI0bk0r1ItogD4j2Oli87bJrA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@changesets/types": "^7.0.0" - }, - "engines": { - "node": "^22.11 || ^24 || >=26" - } - }, - "node_modules/@changesets/cli": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@changesets/cli/-/cli-3.0.0.tgz", - "integrity": "sha512-V7Gm+GP5OT3mJinMI2YcJD/JyO/a4WChnaMCCzTRhWHgu1zhtsyY7zCjP6n5W6zsgSjJKs+yPmvtREvE0F2g8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@changesets/apply-release-plan": "^8.0.0", - "@changesets/assemble-release-plan": "^7.0.0", - "@changesets/changelog-git": "^1.0.0", - "@changesets/config": "^4.0.0", - "@changesets/errors": "^1.0.0", - "@changesets/get-dependents-graph": "^3.0.0", - "@changesets/git": "^4.0.0", - "@changesets/pre": "^3.0.0", - "@changesets/read": "^1.0.0", - "@changesets/should-skip-package": "^1.0.0", - "@changesets/types": "^7.0.0", - "@changesets/write": "^1.0.0", - "@clack/prompts": "^1.7.0", - "@manypkg/get-packages": "^3.1.0", - "@pnpm/deps.graph-sequencer": "^1100.0.1", - "cac": "^7.0.0", - "import-meta-resolve": "^4.2.0", - "launch-editor": "^2.14.1", - "package-manager-detector": "^1.6.0", - "semver": "^7.8.1", - "tinyexec": "^1.3.0" - }, - "bin": { - "changeset": "bin.js" - }, - "engines": { - "node": "^22.11 || ^24 || >=26", - "npm": ">=10.9.0", - "pnpm": ">=10.0.0", - "yarn": ">=4.5.2" - } - }, - "node_modules/@changesets/cli/node_modules/cac": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cac/-/cac-7.0.0.tgz", - "integrity": "sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=20.19.0" - } - }, - "node_modules/@changesets/config": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@changesets/config/-/config-4.0.0.tgz", - "integrity": "sha512-mw95/YrkOuhZZxfnVAA4bSXOFUi+KlhzOBTM8C4x777NhUU6HWIl9Z+K+nME+E4PVsv5NQVQwTfiHihAS1A/ow==", - "dev": true, - "license": "MIT", - "dependencies": { - "@changesets/get-dependents-graph": "^3.0.0", - "@changesets/should-skip-package": "^1.0.0", - "@changesets/types": "^7.0.0", - "@manypkg/get-packages": "^3.1.0", - "picomatch": "^4.0.4" - }, - "engines": { - "node": "^22.11 || ^24 || >=26" - } - }, - "node_modules/@changesets/errors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@changesets/errors/-/errors-1.0.0.tgz", - "integrity": "sha512-ElN/mEzn6zmETgjwf5MclCMa9ef59sAR0lfO8VSYIsiRvbC2FbLB/92EoYw10Sl0kGixxHFiJZUSv7dA+YpR8g==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.11 || ^24 || >=26" - } - }, - "node_modules/@changesets/format": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@changesets/format/-/format-0.1.2.tgz", - "integrity": "sha512-Caez5XtNXCFS/G5bwyav3wuXL0tMxVd2ZGbaumWbzN08tyzO21asCw7JZhNtVsAZDCvDRUzZN+Iit9SyRITSYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "package-manager-detector": "^1.8.0", - "tinyexec": "^1.3.0" - }, - "engines": { - "node": "^22.11 || ^24 || >=26" - } - }, - "node_modules/@changesets/get-dependents-graph": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@changesets/get-dependents-graph/-/get-dependents-graph-3.0.0.tgz", - "integrity": "sha512-ji/t5wFA1zREKXRUePE6Qi+Qu2UgxCeSSGQrphezwvQZrp49B7sJ+8+wvM0tA7zPeSxYKCojDy3WWgrl+s+awg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@changesets/types": "^7.0.0", - "semver": "^7.8.1" - }, - "engines": { - "node": "^22.11 || ^24 || >=26" - } - }, - "node_modules/@changesets/git": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@changesets/git/-/git-4.0.0.tgz", - "integrity": "sha512-uIEswpPUgzBBqrC0qg13byNaPorzhf05LE2T+gRizEKCXvMsJC6NPJp5iNDSV/gYj4Viqh09UiOF5E7XWeH5lQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@changesets/errors": "^1.0.0", - "@changesets/types": "^7.0.0", - "@manypkg/get-packages": "^3.1.0", - "picomatch": "^4.0.4", - "tinyexec": "^1.3.0" - }, - "engines": { - "node": "^22.11 || ^24 || >=26" - } - }, - "node_modules/@changesets/parse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@changesets/parse/-/parse-1.0.0.tgz", - "integrity": "sha512-P0iaMb9p9CRYZiTgAllEIF9AUMQHIy1G72tKlcIqJp61icZSsKQNiOPdxAMZG8m/DvZwt/oz5xEbTpike//dWg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@changesets/types": "^7.0.0", - "yaml": "^2.9.0" - }, - "engines": { - "node": "^22.11 || ^24 || >=26" - } - }, - "node_modules/@changesets/pre": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@changesets/pre/-/pre-3.0.0.tgz", - "integrity": "sha512-Zm/6YliV/a2oeWTqHJf6KxLrQwgcK1i/BRDl2m0EKZvbnxV5fG9QRhwJJGshjsZTUTS6dkfURQ2K6aAvgNw/3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@changesets/errors": "^1.0.0", - "@changesets/types": "^7.0.0", - "@manypkg/get-packages": "^3.1.0" - }, - "engines": { - "node": "^22.11 || ^24 || >=26" - } - }, - "node_modules/@changesets/read": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@changesets/read/-/read-1.0.0.tgz", - "integrity": "sha512-8TdE2PwG6yArPt5Ozej83Z6iHz1G8BDBKvIEKZ455MccR4K3GNbLR2XqjBxa+5yLFnEd3xAOPXYboBg1pt8stQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@changesets/git": "^4.0.0", - "@changesets/parse": "^1.0.0", - "@changesets/types": "^7.0.0" - }, - "engines": { - "node": "^22.11 || ^24 || >=26" - } - }, - "node_modules/@changesets/should-skip-package": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@changesets/should-skip-package/-/should-skip-package-1.0.0.tgz", - "integrity": "sha512-pwqoJmbONn1XgXmZXPEExgAaT+HdZLjALFTDgIm+PnS5KeO2nLtzA2/Q+4aMFY14kFMuXKG30MObhvDzWgzDgg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@changesets/types": "^7.0.0" - }, - "engines": { - "node": "^22.11 || ^24 || >=26" - } - }, - "node_modules/@changesets/types": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@changesets/types/-/types-7.0.0.tgz", - "integrity": "sha512-c5GoiQyt3pxiXjrWSNoP8/GRf4kG+VnKzovx1OQM8dYYALlSwgedmkPmJ+ZqGxqwg9D3Bkj85Uo4KLd5BN3A0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^22.11 || ^24 || >=26" - } - }, - "node_modules/@changesets/write": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@changesets/write/-/write-1.0.0.tgz", - "integrity": "sha512-xCK3/4C7Z7muQB/wguE6HcbjtY9iOtaK9orZKE+7xi573cMGD8rLZz7qlo6IvK7s+SIUIKajzj1l+F1QuP97tw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@changesets/format": "^0.1.1", - "@changesets/types": "^7.0.0", - "human-id": "^4.2.0" - }, - "engines": { - "node": "^22.11 || ^24 || >=26" - } - }, - "node_modules/@clack/core": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@clack/core/-/core-1.4.3.tgz", - "integrity": "sha512-/kr3UWNtdJfxZtPgDqUOmG2pvwlmcLGheex5yiZKdwbzZJxhV+HMNR9QNmyY5cGwTNV6LrR7Jtp+KjhUAP1qBQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-wrap-ansi": "^0.2.0", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 20.12.0" - } - }, - "node_modules/@clack/prompts": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-1.7.0.tgz", - "integrity": "sha512-y7/yvZ2TPAnR9+jnc00klvNNLkJiXFFrQA/hlLCcxA9a2A4zQIOimyFQ9XfwYKiGD1fb5GY8vbKIIgO8d5Tb2A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@clack/core": "1.4.3", - "fast-string-width": "^3.0.2", - "fast-wrap-ansi": "^0.2.0", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 20.12.0" - } - }, "node_modules/@cloudflare/kv-asset-handler": { "version": "0.5.0", "dev": true, @@ -1044,6 +761,10 @@ "resolved": "packages/models", "link": true }, + "node_modules/@codraoss/node-adapters": { + "resolved": "packages/node-adapters", + "link": true + }, "node_modules/@codraoss/node-server": { "resolved": "apps/node", "link": true @@ -2595,48 +2316,6 @@ "@braidai/lang": "^1.0.0" } }, - "node_modules/@manypkg/find-root": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@manypkg/find-root/-/find-root-3.1.0.tgz", - "integrity": "sha512-BcSqCyKhBVZ5YkSzOiheMCV41kqAFptW6xGqYSTjkVTl9XQpr+pqHhwgGCOHQtjDCv7Is6EFyA14Sm5GVbVABA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@manypkg/tools": "^2.1.0" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@manypkg/get-packages": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@manypkg/get-packages/-/get-packages-3.1.0.tgz", - "integrity": "sha512-0TbBVyvPrP7xGYBI/cP8UP+yl/z+HtbTttAD7FMAJgn/kXOTwh5/60TsqP9ZYY710forNfyV0N8P/IE/ujGZJg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@manypkg/find-root": "^3.1.0", - "@manypkg/tools": "^2.1.0" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@manypkg/tools": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@manypkg/tools/-/tools-2.1.2.tgz", - "integrity": "sha512-6QEf6yqFbETdwGITKq57aYoPfX/3K8XFNwsAlx0C1M7o8cb79sv1M3w+tWuWvIcSbNqrLF7OD7YpZMVVz335hQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "jju": "^1.4.0", - "tinyglobby": "^0.2.13", - "yaml": "^2.9.0" - }, - "engines": { - "node": ">=20.0.0" - } - }, "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.4.tgz", @@ -2765,19 +2444,6 @@ "url": "https://github.com/sponsors/Boshen" } }, - "node_modules/@pnpm/deps.graph-sequencer": { - "version": "1100.0.1", - "resolved": "https://registry.npmjs.org/@pnpm/deps.graph-sequencer/-/deps.graph-sequencer-1100.0.1.tgz", - "integrity": "sha512-pOr5+q1fLYKwFN3LAJuGZEnfXDcQ73zqgDHMtGy+K+uIoUqyY+6MeDCWFwfu+4EFuq76I5EPFofoNAI+Bmmq4A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=22.13" - }, - "funding": { - "url": "https://opencollective.com/pnpm" - } - }, "node_modules/@poppinss/colors": { "version": "4.1.6", "resolved": "https://registry.npmjs.org/@poppinss/colors/-/colors-4.1.6.tgz", @@ -6324,33 +5990,6 @@ "dev": true, "license": "MIT" }, - "node_modules/fast-string-truncated-width": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", - "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-string-width": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz", - "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-string-truncated-width": "^3.0.2" - } - }, - "node_modules/fast-wrap-ansi": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.2.tgz", - "integrity": "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-string-width": "^3.0.2" - } - }, "node_modules/fdir": { "version": "6.5.0", "dev": true, @@ -6761,16 +6400,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/human-id": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/human-id/-/human-id-4.2.0.tgz", - "integrity": "sha512-K3GbkIWqyvvlpfhBPlbEvD97TtqBpAYA4kt+cn2lD2x2HuohzZCibcA2nOlnJT6exqvJLggoB5nv2dNf192nEA==", - "dev": true, - "license": "MIT", - "bin": { - "human-id": "dist/cli.js" - } - }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -6789,17 +6418,6 @@ "url": "https://opencollective.com/immer" } }, - "node_modules/import-meta-resolve": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", - "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -6971,13 +6589,6 @@ "jiti": "lib/jiti-cli.mjs" } }, - "node_modules/jju": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", - "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", - "dev": true, - "license": "MIT" - }, "node_modules/joycon": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", @@ -7079,13 +6690,6 @@ "node": ">=6" } }, - "node_modules/jsonc-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", - "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", - "dev": true, - "license": "MIT" - }, "node_modules/jsonrepair": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/jsonrepair/-/jsonrepair-3.15.0.tgz", @@ -7113,17 +6717,6 @@ "node": ">=6" } }, - "node_modules/launch-editor": { - "version": "2.14.1", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.14.1.tgz", - "integrity": "sha512-QWBrQsMpH7gPr965dsKD/3cKWiNoTjpATQf++Xq63N6sKRGMwlVXz41O1IZTMfZQgBctD/K5Zt06+/I6pP6+HA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picocolors": "^1.1.1", - "shell-quote": "^1.8.4" - } - }, "node_modules/lenis": { "version": "1.3.26", "resolved": "https://registry.npmjs.org/lenis/-/lenis-1.3.26.tgz", @@ -11259,22 +10852,6 @@ "dev": true, "license": "ISC" }, - "node_modules/yaml": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", - "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", - "dev": true, - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - }, - "funding": { - "url": "https://github.com/sponsors/eemeli" - } - }, "node_modules/yargs": { "version": "17.7.2", "dev": true, @@ -11403,14 +10980,14 @@ }, "packages/api": { "name": "@codraoss/api", - "version": "0.9.8", + "version": "0.9.12", "license": "AGPL-3.0-only", "dependencies": { - "@codraoss/core": "^0.9.5", - "@codraoss/db": "^0.9.5", - "@codraoss/models": "^0.9.6", - "@codraoss/provider-github": "^0.9.5", - "@codraoss/schema": "^0.9.5", + "@codraoss/core": "*", + "@codraoss/db": "*", + "@codraoss/models": "*", + "@codraoss/provider-github": "*", + "@codraoss/schema": "*", "hono": "^4.12.25", "zod": "^4.3.6" }, @@ -11438,10 +11015,10 @@ }, "packages/core": { "name": "@codraoss/core", - "version": "0.9.6", + "version": "0.9.12", "license": "AGPL-3.0-only", "dependencies": { - "@codraoss/schema": "^0.9.5", + "@codraoss/schema": "*", "jsonrepair": "^3.15.0", "picomatch": "^4.0.5", "zod": "^4.3.6" @@ -11453,11 +11030,11 @@ }, "packages/db": { "name": "@codraoss/db", - "version": "0.9.5", + "version": "0.9.12", "license": "AGPL-3.0-only", "dependencies": { - "@codraoss/core": "^0.9.5", - "@codraoss/schema": "^0.9.5", + "@codraoss/core": "*", + "@codraoss/schema": "*", "postgres": "^3.4.9" }, "devDependencies": { @@ -11484,23 +11061,76 @@ }, "packages/models": { "name": "@codraoss/models", - "version": "0.9.6", + "version": "0.9.12", + "license": "AGPL-3.0-only", + "dependencies": { + "@codraoss/core": "*", + "@codraoss/schema": "*" + }, + "devDependencies": { + "tsup": "^8.0.0" + } + }, + "packages/node-adapters": { + "name": "@codraoss/node-adapters", + "version": "0.9.12", "license": "AGPL-3.0-only", "dependencies": { - "@codraoss/core": "^0.9.5", - "@codraoss/schema": "^0.9.5" + "@codraoss/api": "*", + "@codraoss/core": "*", + "@codraoss/db": "*", + "@codraoss/schema": "*", + "bullmq": "^6.3.8", + "ioredis": "^6.0.0", + "nanoid": "^5.0.7" }, "devDependencies": { + "@types/node": "^22.0.0", "tsup": "^8.0.0" } }, + "packages/node-adapters/node_modules/@types/node": { + "version": "22.20.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.4.tgz", + "integrity": "sha512-zJRE40jpHtKqE/C4fgHrAKQLJuSpzEnP9ff9Y7YtoR3Wd2pwqzlekDeEuUQXjRd+QCYnVnNwuJYmhdk9XV8gvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "packages/node-adapters/node_modules/nanoid": { + "version": "5.1.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.16.tgz", + "integrity": "sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, + "packages/node-adapters/node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, "packages/provider-github": { "name": "@codraoss/provider-github", - "version": "0.9.5", + "version": "0.9.12", "license": "AGPL-3.0-only", "dependencies": { - "@codraoss/core": "^0.9.5", - "@codraoss/schema": "^0.9.5" + "@codraoss/core": "*", + "@codraoss/schema": "*" }, "devDependencies": { "tsup": "^8.0.0" @@ -11508,7 +11138,7 @@ }, "packages/schema": { "name": "@codraoss/schema", - "version": "0.9.5", + "version": "0.9.12", "license": "AGPL-3.0-only", "dependencies": { "zod": "^4.3.6" @@ -11519,11 +11149,11 @@ }, "packages/ui": { "name": "@codraoss/ui", - "version": "0.9.10", + "version": "0.9.12", "license": "AGPL-3.0-only", "dependencies": { "@base-ui/react": "^1.6.0", - "@codraoss/schema": "^0.9.5", + "@codraoss/schema": "*", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "sugar-high": "^2.0.0", diff --git a/package.json b/package.json index 0c7ea2b6..d89faa79 100644 --- a/package.json +++ b/package.json @@ -38,14 +38,11 @@ "test:watch": "vitest", "typecheck": "tsc --noEmit", "typecheck:all": "npm run typecheck --workspaces --if-present", - "changeset": "changeset", - "version:packages": "changeset version", - "release": "npm run build:packages && changeset publish", + "release": "npm run build:packages", "check:exports": "node scripts/check-package-exports.mjs" }, "devDependencies": { "@arethetypeswrong/cli": "^0.18.5", - "@changesets/cli": "^3.0.0", "@eslint/js": "^10.0.1", "@tailwindcss/vite": "^4.2.2", "@testing-library/dom": "^10.4.1", diff --git a/packages/api/CHANGELOG.md b/packages/api/CHANGELOG.md deleted file mode 100644 index db620f29..00000000 --- a/packages/api/CHANGELOG.md +++ /dev/null @@ -1,23 +0,0 @@ -# @codraoss/api - -## 0.9.6 - -### Patch Changes - -- Compare `If-None-Match` on `GET /api/jobs/:id` as a weak validator. Cloudflare's edge rewrites strong ETags to `W/"..."` when it compresses the response, so a strict equality check never matched and the 304 path never fired in production; every poll re-serialized the full job detail and could exhaust the CPU limit on a large running job. -- Updated dependencies - - @codraoss/models@0.9.6 - -## 0.9.5 - -### Patch Changes - -- Add optional extension points. `createApiRouter(options?)` accepts `beforeAuth`, `afterAuth`, `pages`, `publicPages` and `routes`; `routes` is invoked last so anything mounted under `/api/*` still inherits the session and CSRF middleware. `ApiRouterDeps` gains optional `authz` and `checkQuota` ports, and every mutating endpoint is now checked against them. Both default to allow-all, so calling `createApiRouter()` with no arguments is unchanged. Quota denial on the webhook path answers 202-ignored rather than 429, because GitHub redelivers failed deliveries. -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @codraoss/core@0.9.5 - - @codraoss/db@0.9.5 - - @codraoss/schema@0.9.5 - - @codraoss/models@0.9.5 - - @codraoss/provider-github@0.9.5 diff --git a/packages/api/package.json b/packages/api/package.json index 1b4db67a..474f59c2 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -1,6 +1,6 @@ { "name": "@codraoss/api", - "version": "0.9.8", + "version": "0.9.12", "description": "Codra's HTTP surface as a mountable Hono router, wired through ports.", "author": "Devarshi Shimpi", "license": "AGPL-3.0-only", @@ -16,9 +16,18 @@ "type": "module", "sideEffects": false, "exports": { - ".": "./src/index.ts", - "./logger": "./src/logger.ts", - "./platform": "./src/platform/index.ts" + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + }, + "./logger": { + "types": "./dist/logger.d.ts", + "import": "./dist/logger.js" + }, + "./platform": { + "types": "./dist/platform/index.d.ts", + "import": "./dist/platform/index.js" + } }, "files": [ "dist", @@ -26,24 +35,7 @@ "README.md" ], "publishConfig": { - "access": "public", - "main": "./dist/index.js", - "module": "./dist/index.js", - "types": "./dist/index.d.ts", - "exports": { - ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.js" - }, - "./logger": { - "types": "./dist/logger.d.ts", - "import": "./dist/logger.js" - }, - "./platform": { - "types": "./dist/platform/index.d.ts", - "import": "./dist/platform/index.js" - } - } + "access": "public" }, "scripts": { "build": "tsup", @@ -54,14 +46,17 @@ "dependencies": { "hono": "^4.12.25", "zod": "^4.3.6", - "@codraoss/schema": "^0.9.5", - "@codraoss/core": "^0.9.5", - "@codraoss/db": "^0.9.5", - "@codraoss/provider-github": "^0.9.5", - "@codraoss/models": "^0.9.6" + "@codraoss/schema": "*", + "@codraoss/core": "*", + "@codraoss/db": "*", + "@codraoss/provider-github": "*", + "@codraoss/models": "*" }, "devDependencies": { "@types/node": "^22.0.0", "tsup": "^8.0.0" - } + }, + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./dist/index.d.ts" } diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md deleted file mode 100644 index cfe1e298..00000000 --- a/packages/core/CHANGELOG.md +++ /dev/null @@ -1,9 +0,0 @@ -# @codraoss/core - -## 0.9.5 - -### Patch Changes - -- Raise `MAX_TOTAL_DIFF_CHARS` to 4,000,000 and centralise it in the package constants. Files dropped by the file-count and diff-size limits now mark a review as partial and are named in the job status, rather than appearing only in the pull request comment. -- Updated dependencies - - @codraoss/schema@0.9.5 diff --git a/packages/core/package.json b/packages/core/package.json index 2829b7d1..69d26fe7 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@codraoss/core", - "version": "0.9.6", + "version": "0.9.12", "description": "The Codra review engine: pure, transport- and platform-agnostic code review logic behind ports.", "author": "Devarshi Shimpi", "license": "AGPL-3.0-only", @@ -123,7 +123,7 @@ "postpack": "node ../../scripts/swap-publish-exports.mjs restore" }, "dependencies": { - "@codraoss/schema": "^0.9.5", + "@codraoss/schema": "*", "jsonrepair": "^3.15.0", "picomatch": "^4.0.5", "zod": "^4.3.6" diff --git a/packages/core/src/ports/queue.ts b/packages/core/src/ports/queue.ts index 745a3375..9a554cda 100644 --- a/packages/core/src/ports/queue.ts +++ b/packages/core/src/ports/queue.ts @@ -1,3 +1,4 @@ export interface QueueProducer { send(message: T, options?: { delaySeconds?: number }): Promise; + deleteJob?(jobId: string): Promise; } diff --git a/packages/db/CHANGELOG.md b/packages/db/CHANGELOG.md deleted file mode 100644 index 049396b1..00000000 --- a/packages/db/CHANGELOG.md +++ /dev/null @@ -1,11 +0,0 @@ -# @codraoss/db - -## 0.9.5 - -### Patch Changes - -- Publish `migrations/` and `scripts/`, which were excluded from the tarball, so an installed copy can create its schema. The runner now accepts `--extra-dir` or `CODRA_EXTRA_MIGRATIONS_DIR`: those migrations run after the built-in set inside the same transaction and advisory lock, and are tracked under an `extra:` prefix so filenames cannot collide with future core migrations. Env-file lookup checks the working directory first so it works when run from `node_modules`. -- Updated dependencies -- Updated dependencies - - @codraoss/core@0.9.5 - - @codraoss/schema@0.9.5 diff --git a/packages/db/package.json b/packages/db/package.json index 19ad1ca1..7ffc05e4 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -1,6 +1,6 @@ { "name": "@codraoss/db", - "version": "0.9.5", + "version": "0.9.12", "description": "Codra's Postgres persistence layer behind repository interfaces.", "author": "Devarshi Shimpi", "license": "AGPL-3.0-only", @@ -67,8 +67,8 @@ "postpack": "node ../../scripts/swap-publish-exports.mjs restore" }, "dependencies": { - "@codraoss/schema": "^0.9.5", - "@codraoss/core": "^0.9.5", + "@codraoss/schema": "*", + "@codraoss/core": "*", "postgres": "^3.4.9" }, "devDependencies": { diff --git a/packages/db/src/client.ts b/packages/db/src/client.ts index 020f0235..a1af66bf 100644 --- a/packages/db/src/client.ts +++ b/packages/db/src/client.ts @@ -11,10 +11,11 @@ const dbStorage = new AsyncLocalStorage(); function createDbClient(env: DbEnv): DbClient { const sql = postgres(env.HYPERDRIVE.connectionString, { - max: 5, + max: process.env.DB_POOL_SIZE ? parseInt(process.env.DB_POOL_SIZE, 10) : 5, fetch_types: false, prepare: false, onnotice: () => {}, + ssl: env.HYPERDRIVE.connectionString.includes('sslmode=require') ? { rejectUnauthorized: false } : false, }); return { @@ -91,13 +92,14 @@ async function withStaleConnectionRecovery(env: DbEnv, op: (db: DbClient) => try { return await op(getDb(env)); } catch (error) { - if (env.workerMode === false || inScope || !isStaleConnectionError(error)) throw error; + if (inScope || !isStaleConnectionError(error)) throw error; const connectionString = env.HYPERDRIVE.connectionString; fallbackClients.delete(connectionString); const fresh = createDbClient(env); fallbackClients.set(connectionString, fresh); - return op(fresh); + + return await op(fresh); } } diff --git a/packages/models/CHANGELOG.md b/packages/models/CHANGELOG.md deleted file mode 100644 index 64c84674..00000000 --- a/packages/models/CHANGELOG.md +++ /dev/null @@ -1,16 +0,0 @@ -# @codraoss/models - -## 0.9.6 - -### Patch Changes - -- Stop sending `propertyOrdering` in Gemini response grammars. The keyword belongs to the legacy OpenAPI-style `responseSchema`; inside `responseJsonSchema` the API rejects it with a bare 400 `invalid argument`, so every constrained Google review fell back to unconstrained decoding and frequently returned prose that failed the reviewable-output gate. Property declaration order already carries the ordering the grammar relied on. - -## 0.9.5 - -### Patch Changes - -- Updated dependencies -- Updated dependencies - - @codraoss/core@0.9.5 - - @codraoss/schema@0.9.5 diff --git a/packages/models/package.json b/packages/models/package.json index 5d18172b..0a494564 100644 --- a/packages/models/package.json +++ b/packages/models/package.json @@ -1,6 +1,6 @@ { "name": "@codraoss/models", - "version": "0.9.6", + "version": "0.9.12", "description": "LLM provider adapters and model catalog for the Codra review engine.", "author": "Devarshi Shimpi", "license": "AGPL-3.0-only", @@ -78,8 +78,8 @@ "postpack": "node ../../scripts/swap-publish-exports.mjs restore" }, "dependencies": { - "@codraoss/schema": "^0.9.5", - "@codraoss/core": "^0.9.5" + "@codraoss/schema": "*", + "@codraoss/core": "*" }, "devDependencies": { "tsup": "^8.0.0" diff --git a/packages/node-adapters/package.json b/packages/node-adapters/package.json new file mode 100644 index 00000000..9041ab33 --- /dev/null +++ b/packages/node-adapters/package.json @@ -0,0 +1,58 @@ +{ + "name": "@codraoss/node-adapters", + "version": "0.9.12", + "description": "Node.js platform adapters for Codra, including BullMQ, Redis, and Stateless Orchestration.", + "type": "module", + "main": "./src/index.ts", + "types": "./dist/index.d.ts", + "exports": { + ".": "./src/index.ts" + }, + "publishConfig": { + "access": "public", + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "import": "./dist/index.js", + "types": "./dist/index.d.ts" + } + } + }, + "files": [ + "dist", + "LICENSE", + "README.md" + ], + "author": "Devarshi Shimpi", + "license": "AGPL-3.0-only", + "repository": { + "type": "git", + "url": "git+https://github.com/devarshishimpi/codra.git", + "directory": "packages/node-adapters" + }, + "bugs": { + "url": "https://github.com/devarshishimpi/codra/issues" + }, + "homepage": "https://codra.run", + "scripts": { + "build": "tsup", + "prepack": "node ../../scripts/swap-publish-exports.mjs promote", + "postpack": "node ../../scripts/swap-publish-exports.mjs restore", + "typecheck": "tsc -p tsconfig.json" + }, + "dependencies": { + "@codraoss/api": "*", + "@codraoss/core": "*", + "@codraoss/db": "*", + "@codraoss/schema": "*", + "bullmq": "^6.3.8", + "ioredis": "^6.0.0", + "nanoid": "^5.0.7" + }, + "devDependencies": { + "@types/node": "^22.0.0", + "tsup": "^8.0.0" + } +} diff --git a/packages/node-adapters/src/index.ts b/packages/node-adapters/src/index.ts new file mode 100644 index 00000000..3bfd71af --- /dev/null +++ b/packages/node-adapters/src/index.ts @@ -0,0 +1,6 @@ +export * from './node-orchestrator.js'; +export * from './platform.js'; +export * from './redis-kv.js'; +export * from './redis-queue.js'; +export * from './redis-session-store.js'; +export * from './worker.js'; diff --git a/apps/node/src/adapters/node-orchestrator.ts b/packages/node-adapters/src/node-orchestrator.ts similarity index 100% rename from apps/node/src/adapters/node-orchestrator.ts rename to packages/node-adapters/src/node-orchestrator.ts diff --git a/apps/node/src/adapters/platform.ts b/packages/node-adapters/src/platform.ts similarity index 51% rename from apps/node/src/adapters/platform.ts rename to packages/node-adapters/src/platform.ts index f3b8855b..da811954 100644 --- a/apps/node/src/adapters/platform.ts +++ b/packages/node-adapters/src/platform.ts @@ -1,10 +1,9 @@ import type { Clock, IdGenerator, KvStore } from '@codraoss/core/ports'; -import type { NodeAppBindings } from '../env'; -export function makeKvStore(env: NodeAppBindings): KvStore { +export function makeKvStore(appKv: KvStore): KvStore { return { - get: (key) => env.APP_KV.get(key), - put: (key, value, options) => env.APP_KV.put(key, value, options), + get: (key) => appKv.get(key), + put: (key, value, options) => appKv.put(key, value, options), }; } diff --git a/apps/node/src/adapters/redis-kv.ts b/packages/node-adapters/src/redis-kv.ts similarity index 100% rename from apps/node/src/adapters/redis-kv.ts rename to packages/node-adapters/src/redis-kv.ts diff --git a/packages/node-adapters/src/redis-queue.ts b/packages/node-adapters/src/redis-queue.ts new file mode 100644 index 00000000..704ac38b --- /dev/null +++ b/packages/node-adapters/src/redis-queue.ts @@ -0,0 +1,22 @@ +import type { QueueProducer } from '@codraoss/core/ports'; +import type { Queue } from 'bullmq'; + +export class RedisQueueAdapter implements QueueProducer { + constructor(private readonly queue: Queue) {} + + async send(message: T, options?: { delaySeconds?: number; jobId?: string }): Promise { + const jobOptions: any = options?.jobId ? { jobId: options.jobId } : {}; + if (options?.delaySeconds) { + jobOptions.delay = options.delaySeconds * 1000; + } + + await this.queue.add('review-job', message, jobOptions); + } + + async deleteJob(jobId: string): Promise { + const bullMqJob = await this.queue.getJob(jobId); + if (bullMqJob) { + await bullMqJob.remove(); + } + } +} diff --git a/apps/node/src/adapters/redis-session-store.ts b/packages/node-adapters/src/redis-session-store.ts similarity index 87% rename from apps/node/src/adapters/redis-session-store.ts rename to packages/node-adapters/src/redis-session-store.ts index d4c75e71..e8ad4fd9 100644 --- a/apps/node/src/adapters/redis-session-store.ts +++ b/packages/node-adapters/src/redis-session-store.ts @@ -22,7 +22,12 @@ export class RedisSessionStore implements SessionStore { if (!sessionString) { return null; } - return JSON.parse(sessionString) as DashboardSessionUser; + try { + return JSON.parse(sessionString) as DashboardSessionUser; + } catch { + // Return null if parsing fails (corrupted JSON) + return null; + } } async destroySession(token: string): Promise { diff --git a/apps/node/src/worker.ts b/packages/node-adapters/src/worker.ts similarity index 57% rename from apps/node/src/worker.ts rename to packages/node-adapters/src/worker.ts index 3e3bebf4..dba5151c 100644 --- a/apps/node/src/worker.ts +++ b/packages/node-adapters/src/worker.ts @@ -1,15 +1,16 @@ import { Worker, type Job } from 'bullmq'; import { reviewJobMessageSchema, type ReviewJobMessage } from '@codraoss/schema'; -import { NodeOrchestrator } from './adapters/node-orchestrator'; -import { logger } from '@codraoss/api/logger'; -import type { NodeAppBindings } from './env'; -import Redis from 'ioredis'; -import { createReviewRuntime } from './runtime'; +import { NodeOrchestrator } from './node-orchestrator'; +import type { ReviewRuntime } from '@codraoss/core/ports'; import type { QueueProducer } from '@codraoss/core/ports'; +import type Redis from 'ioredis'; -export function startWorker(env: NodeAppBindings, redisUrl: string, queue: QueueProducer): Worker { - const connection = new Redis(redisUrl, { maxRetriesPerRequest: null }); - +export function startWorker( + redisConnection: Redis, + queue: QueueProducer, + createRuntime: () => ReviewRuntime, + logger: { info: (msg: string) => void, error: (msg: string, err?: any) => void } +): Worker { const worker = new Worker( 'codra-reviews', async (job: Job) => { @@ -19,11 +20,12 @@ export function startWorker(env: NodeAppBindings, redisUrl: string, queue: Queue throw new Error('Invalid job payload'); } - const reviewRuntime = createReviewRuntime(env); - const orchestrator = new NodeOrchestrator(reviewRuntime, queue); + const reviewRuntime = createRuntime(); + // Need to cast to any since DbEnv is dynamically merged downstream + const orchestrator = new NodeOrchestrator(reviewRuntime as any, queue); await orchestrator.startReviewJob(job.id ?? 'unknown', parseResult.data); }, - { connection } + { connection: redisConnection } ); worker.on('completed', (job) => { diff --git a/packages/node-adapters/tsconfig.json b/packages/node-adapters/tsconfig.json new file mode 100644 index 00000000..391e21ce --- /dev/null +++ b/packages/node-adapters/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "types": ["node"] + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +} diff --git a/packages/node-adapters/tsup.config.json b/packages/node-adapters/tsup.config.json new file mode 100644 index 00000000..28a3fea0 --- /dev/null +++ b/packages/node-adapters/tsup.config.json @@ -0,0 +1,10 @@ +{ + "entry": ["src/index.ts"], + "format": ["esm"], + "outDir": "dist", + "clean": true, + "sourcemap": true, + "splitting": false, + "target": "node20", + "noExternal": [] +} diff --git a/packages/provider-github/CHANGELOG.md b/packages/provider-github/CHANGELOG.md deleted file mode 100644 index f3189ffd..00000000 --- a/packages/provider-github/CHANGELOG.md +++ /dev/null @@ -1,10 +0,0 @@ -# @codraoss/provider-github - -## 0.9.5 - -### Patch Changes - -- Updated dependencies -- Updated dependencies - - @codraoss/core@0.9.5 - - @codraoss/schema@0.9.5 diff --git a/packages/provider-github/package.json b/packages/provider-github/package.json index 1d4fec1e..2002e889 100644 --- a/packages/provider-github/package.json +++ b/packages/provider-github/package.json @@ -1,6 +1,6 @@ { "name": "@codraoss/provider-github", - "version": "0.9.5", + "version": "0.9.12", "description": "GitHub git-provider adapter for Codra: App auth, webhooks, OAuth, and review posting.", "author": "Devarshi Shimpi", "license": "AGPL-3.0-only", @@ -53,8 +53,8 @@ "postpack": "node ../../scripts/swap-publish-exports.mjs restore" }, "dependencies": { - "@codraoss/core": "^0.9.5", - "@codraoss/schema": "^0.9.5" + "@codraoss/core": "*", + "@codraoss/schema": "*" }, "devDependencies": { "tsup": "^8.0.0" diff --git a/packages/schema/CHANGELOG.md b/packages/schema/CHANGELOG.md deleted file mode 100644 index 4a9d2817..00000000 --- a/packages/schema/CHANGELOG.md +++ /dev/null @@ -1,7 +0,0 @@ -# @codraoss/schema - -## 0.9.5 - -### Patch Changes - -- Export `apiActions`, the vocabulary of permission identifiers the API checks, and `ApiAction` as an open union so consumers can add their own names. `AuthSessionResponse` gains an optional `permissions` field; omitting it means every action is allowed. diff --git a/packages/schema/package.json b/packages/schema/package.json index fd0ce87b..f1ddceae 100644 --- a/packages/schema/package.json +++ b/packages/schema/package.json @@ -1,6 +1,6 @@ { "name": "@codraoss/schema", - "version": "0.9.5", + "version": "0.9.12", "description": "Shared types and Zod contracts for Codra, the open-source code review engine.", "author": "Devarshi Shimpi", "license": "AGPL-3.0-only", diff --git a/packages/ui/CHANGELOG.md b/packages/ui/CHANGELOG.md deleted file mode 100644 index f7b7254e..00000000 --- a/packages/ui/CHANGELOG.md +++ /dev/null @@ -1,9 +0,0 @@ -# @codraoss/ui - -## 0.9.5 - -### Patch Changes - -- Ship the design tokens as `@codraoss/ui/styles`. The components reference `--ui-*`, `--btn-primary-*`, `surface`, `skeleton`, `ui-panel`, `ui-well` and `ui-font-*`, none of which were published before, so the package rendered unstyled outside this repository. Import it after `@import "tailwindcss"` and include the package in your Tailwind source globs. -- Updated dependencies - - @codraoss/schema@0.9.5 diff --git a/packages/ui/package.json b/packages/ui/package.json index b03debb3..45f03693 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@codraoss/ui", - "version": "0.9.10", + "version": "0.9.12", "description": "Codra's reusable React design-system primitives and hooks.", "author": "Devarshi Shimpi", "license": "AGPL-3.0-only", @@ -105,7 +105,7 @@ "sonner": ">=2.0.0" }, "dependencies": { - "@codraoss/schema": "^0.9.5", + "@codraoss/schema": "*", "@base-ui/react": "^1.6.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", diff --git a/scripts/swap-publish-exports.mjs b/scripts/swap-publish-exports.mjs index 958363eb..886f69cc 100644 --- a/scripts/swap-publish-exports.mjs +++ b/scripts/swap-publish-exports.mjs @@ -12,6 +12,7 @@ if (mode === 'promote') { for (const field of PROMOTABLE) { if (fields[field] !== undefined) { pkg[field] = fields[field]; + delete pkg.publishConfig[field]; // Remove to prevent npm warnings } } From 046a1780fe7664dbf0c195c524d9b77045066310 Mon Sep 17 00:00:00 2001 From: Devarshi Shimpi Date: Tue, 22 Sep 2026 23:19:50 +0530 Subject: [PATCH 05/16] chore: upgrade dependencies across monorepo --- apps/node/package.json | 8 +- apps/node/src/index.ts | 15 +- apps/worker/package.json | 2 +- package-lock.json | 1676 ++++++++++++++++--------- package.json | 31 +- packages/api/package.json | 6 +- packages/core/package.json | 4 +- packages/db/package.json | 4 +- packages/models/package.json | 2 +- packages/node-adapters/package.json | 6 +- packages/node-adapters/src/worker.ts | 3 +- packages/provider-github/package.json | 2 +- packages/schema/package.json | 2 +- packages/ui/package.json | 4 +- 14 files changed, 1136 insertions(+), 629 deletions(-) diff --git a/apps/node/package.json b/apps/node/package.json index 895968ed..6a062909 100644 --- a/apps/node/package.json +++ b/apps/node/package.json @@ -17,14 +17,14 @@ "@codraoss/node-adapters": "*", "@codraoss/provider-github": "*", "@codraoss/schema": "*", - "@hono/node-server": "^1.11.1", + "@hono/node-server": "^1.11.5", "bullmq": "^6.3.8", - "dotenv": "^16.4.5", - "hono": "^4.6.0", + "dotenv": "^16.4.7", + "hono": "^4.6.20", "ioredis": "^6.0.0" }, "devDependencies": { - "@types/node": "^20.12.12", + "@types/node": "^20.12.14", "tsup": "^8.0.2" } } diff --git a/apps/node/src/index.ts b/apps/node/src/index.ts index ca7f9795..5649148d 100644 --- a/apps/node/src/index.ts +++ b/apps/node/src/index.ts @@ -9,7 +9,7 @@ import { runWithDb } from '@codraoss/db/client'; import { NodeOrchestrator, RedisKVAdapter, RedisQueueAdapter, RedisSessionStore, startWorker } from '@codraoss/node-adapters'; import { createNodeApiDeps } from './api-deps'; -import { createNodeEnv, NodeAppBindings } from './env'; +import { createNodeEnv, type NodeAppBindings } from './env'; import { logger } from '@codraoss/api/logger'; import Redis from 'ioredis'; import { Queue } from 'bullmq'; @@ -35,9 +35,9 @@ const reviewQueue = new Queue('codra-reviews', { connection: redisClient }); const stubs = { SESSION_STORE: new RedisSessionStore(redisClient), - KV_STORE: new RedisKVAdapter(redisClient), + APP_KV: new RedisKVAdapter(redisClient), REVIEW_QUEUE: new RedisQueueAdapter(reviewQueue), - REVIEW_ORCHESTRATOR: undefined, // Will be set to NodeOrchestrator below + REVIEW_ORCHESTRATOR: undefined as NodeOrchestrator | undefined, }; const env: NodeAppBindings = createNodeEnv(stubs); @@ -86,12 +86,9 @@ if (process.env.START_API !== 'false') { // Add server to shutdown sequence if (server) { const originalClose = server.close; - server.close = () => new Promise(resolve => { - originalClose(() => { - logger.info('HTTP server closed.'); - resolve(); - }); - }); + server.close = () => originalClose(() => { + logger.info('HTTP server closed.'); + }) as any; } } diff --git a/apps/worker/package.json b/apps/worker/package.json index 19f3f136..5f5629db 100644 --- a/apps/worker/package.json +++ b/apps/worker/package.json @@ -10,7 +10,7 @@ "@codraoss/models": "*", "@codraoss/provider-github": "*", "@codraoss/schema": "*", - "hono": "^4.12.25" + "hono": "^4.12.34" }, "devDependencies": { "@cloudflare/workers-types": "^4.20250109.0", diff --git a/package-lock.json b/package-lock.json index 174725ae..1ad9c43d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,7 @@ "@codraoss/ui": "*", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", - "hono": "^4.12.25", + "hono": "^4.12.34", "jsonrepair": "^3.15.0", "lenis": "^1.3.26", "lucide-react": "^1.8.0", @@ -33,25 +33,25 @@ "react": "^19.2.8", "react-dom": "^19.2.8", "react-markdown": "^10.1.0", - "react-router-dom": "^7.18.2", + "react-router-dom": "^7.18.4", "recharts": "^3.8.1", "rehype-raw": "^7.0.0", "rehype-sanitize": "^6.0.0", "remark-gfm": "^4.0.1", - "sonner": "^2.0.7", - "sugar-high": "^2.0.0", + "sonner": "^2.0.8", + "sugar-high": "^2.0.3", "tailwind-merge": "^3.5.0", "zod": "^4.3.6" }, "devDependencies": { "@arethetypeswrong/cli": "^0.18.5", "@eslint/js": "^10.0.1", - "@tailwindcss/vite": "^4.2.2", - "@testing-library/dom": "^10.4.1", - "@testing-library/react": "^16.3.2", - "@types/node": "^25.6.0", + "@tailwindcss/vite": "^4.2.4", + "@testing-library/dom": "^10.4.2", + "@testing-library/react": "^16.3.3", + "@types/node": "^25.6.2", "@types/react": "^19.2.18", - "@types/react-dom": "^19.2.4", + "@types/react-dom": "^19.2.7", "@vitejs/plugin-react": "^6.0.5", "chalk": "^5.6.2", "concurrently": "^9.2.4", @@ -62,13 +62,13 @@ "jsdom": "^29.0.2", "ora": "^9.4.1", "prompts": "^2.4.2", - "publint": "^0.3.23", - "tailwindcss": "^4.2.2", + "publint": "^0.3.24", + "tailwindcss": "^4.2.4", "tsup": "^8.5.1", - "typescript": "^6.0.2", + "typescript": "^6.0.3", "typescript-eslint": "^8.66.0", - "vite": "^8.0.8", - "vitest": "^4.1.10", + "vite": "^8.0.16", + "vitest": "^4.1.11", "wrangler": "^4.114.0" } }, @@ -83,14 +83,14 @@ "@codraoss/node-adapters": "*", "@codraoss/provider-github": "*", "@codraoss/schema": "*", - "@hono/node-server": "^1.11.1", + "@hono/node-server": "^1.11.5", "bullmq": "^6.3.8", - "dotenv": "^16.4.5", - "hono": "^4.6.0", + "dotenv": "^16.4.7", + "hono": "^4.6.20", "ioredis": "^6.0.0" }, "devDependencies": { - "@types/node": "^20.12.12", + "@types/node": "^20.12.14", "tsup": "^8.0.2" } }, @@ -121,7 +121,7 @@ "@codraoss/models": "*", "@codraoss/provider-github": "*", "@codraoss/schema": "*", - "hono": "^4.12.25" + "hono": "^4.12.34" }, "devDependencies": { "@cloudflare/workers-types": "^4.20250109.0", @@ -246,6 +246,8 @@ }, "node_modules/@asamuzakjp/css-color": { "version": "5.1.11", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.11.tgz", + "integrity": "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==", "dev": true, "license": "MIT", "dependencies": { @@ -261,6 +263,8 @@ }, "node_modules/@asamuzakjp/dom-selector": { "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.1.1.tgz", + "integrity": "sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==", "dev": true, "license": "MIT", "dependencies": { @@ -276,6 +280,8 @@ }, "node_modules/@asamuzakjp/generational-cache": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/generational-cache/-/generational-cache-1.0.1.tgz", + "integrity": "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==", "dev": true, "license": "MIT", "engines": { @@ -284,6 +290,8 @@ }, "node_modules/@asamuzakjp/nwsapi": { "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", "dev": true, "license": "MIT" }, @@ -561,15 +569,15 @@ } }, "node_modules/@base-ui/react": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@base-ui/react/-/react-1.6.0.tgz", - "integrity": "sha512-/jzjTWJYXhRFO45Bev9lc3cHbmjzCMpUqbMZ2AgKy/z25mY9B6shGSNcXcjQar9n5doM0KYW1W8fcFv2jZBuMw==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@base-ui/react/-/react-1.8.0.tgz", + "integrity": "sha512-P0/1sxo6SBVZOklKMIedvTWqw2s2IQzi9x5bIVsXu980cuSOD4NeuRSs+/L7LZQfDkZP/uRZyGPyfFl/B1oH+Q==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.29.2", - "@base-ui/utils": "0.3.1", - "@floating-ui/react-dom": "^2.1.8", - "@floating-ui/utils": "^0.2.11", + "@babel/runtime": "^7.29.7", + "@base-ui/utils": "0.4.0", + "@floating-ui/react-dom": "^2.1.9", + "@floating-ui/utils": "^0.2.12", "use-sync-external-store": "^1.6.0" }, "engines": { @@ -599,13 +607,13 @@ } }, "node_modules/@base-ui/utils": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@base-ui/utils/-/utils-0.3.1.tgz", - "integrity": "sha512-gFFiltORVmW/N6IILTGxizP3PBpVpysqML1ALY5Vk0mH+7faVkCknOU31goYHN5Aoek2dkjxva1XOD2Ce9WuIg==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@base-ui/utils/-/utils-0.4.0.tgz", + "integrity": "sha512-bO9fz25kKtPf+aZVyfQrC0PDmJdmVni31W2hCS5/Owb+inwdIL3XU26pCPRPlt4LSxZrBgLwubXQXQlKaFEZzw==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.29.2", - "@floating-ui/utils": "^0.2.11", + "@babel/runtime": "^7.29.7", + "@floating-ui/utils": "^0.2.12", "reselect": "^5.2.0", "use-sync-external-store": "^1.6.0" }, @@ -638,6 +646,30 @@ "specificity": "bin/cli.js" } }, + "node_modules/@cacheable/memory": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cacheable/memory/-/memory-2.2.0.tgz", + "integrity": "sha512-CTLKqLItRCEixEAewD3/j9DB3/o96gpTPD4eJ1v+DGOlxZRZncRQkGYqqnAGCscYd6RNeXfGeiuCphsPtqyIfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cacheable/utils": "^2.5.0", + "@keyv/bigmap": "^1.3.1", + "hookified": "^1.15.1", + "keyv": "^5.6.0" + } + }, + "node_modules/@cacheable/utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@cacheable/utils/-/utils-2.5.0.tgz", + "integrity": "sha512-buipgOVDkkPXNR5+xBpDw7Zk2n1EvU7qBJCNUcL7rhQ//kfpOXPAvQ511Os0vpLYJ1pZnvudNytkQt2hst3wqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hashery": "^1.5.1", + "keyv": "^5.6.0" + } + }, "node_modules/@cloudflare/kv-asset-handler": { "version": "0.5.0", "dev": true, @@ -647,7 +679,9 @@ } }, "node_modules/@cloudflare/unenv-preset": { - "version": "2.16.1", + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.16.2.tgz", + "integrity": "sha512-JBP1+Z7ZSNG/d4mRP+y8VC5dka3tZVMLEZRvS+rzQ4DGV1EoxRFQckcJTTkXbHSQiTj0DtNI01Zwb/V2fX0mvQ==", "dev": true, "license": "MIT OR Apache-2.0", "peerDependencies": { @@ -661,9 +695,9 @@ } }, "node_modules/@cloudflare/workerd-darwin-64": { - "version": "1.20260918.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260918.1.tgz", - "integrity": "sha512-H5Em6Wd0jjxaloYh2rp+WLBl2eWbkk7nSP1svGt6K1RSv/rNVqmKdpjZPJTBSavOmeYGa88qvtOWwS+33OHTqQ==", + "version": "1.20260921.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260921.1.tgz", + "integrity": "sha512-3iB2WnYOlZ29T+1zhCwbHFExCBp6E9bgmDUMryATYwrIGEQ1YbvR78m4ydm56XKN/d/yF3803ivMGfZMYDtiMg==", "cpu": [ "x64" ], @@ -678,9 +712,9 @@ } }, "node_modules/@cloudflare/workerd-darwin-arm64": { - "version": "1.20260918.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260918.1.tgz", - "integrity": "sha512-CR9JRZEQo93fNgBVF4Df2H2/VYO4n7rxSseSwCVv3bJQEb0huADUSCj2FK4ipxar8ToOEB4wawyw0vJo5U/rQQ==", + "version": "1.20260921.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260921.1.tgz", + "integrity": "sha512-FpqVR7IQXVBmGtajyonEmhmb5UAsmV7dTaIkpemmHZXHEw7uYpkhkzKPjc4BOPhNQy8iwt2p+RZBPMY3Y7/bvQ==", "cpu": [ "arm64" ], @@ -695,9 +729,9 @@ } }, "node_modules/@cloudflare/workerd-linux-64": { - "version": "1.20260918.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260918.1.tgz", - "integrity": "sha512-UQ2nnY3qpXLzQ80frmWO+8HvtqyWaQILe8QYZwpemdjT+sqwCz4Dz+0/WVkFco0v/04kIIqikVeLTY/7gEhmkw==", + "version": "1.20260921.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260921.1.tgz", + "integrity": "sha512-riAJIohaVp5A8Sqy4yKlzHOaLPOICMf5oey+jC2rm45RVT+wK8+7UU0d31Dy/02Nc8YUkobAFwNVjX06P8WQ5g==", "cpu": [ "x64" ], @@ -712,9 +746,9 @@ } }, "node_modules/@cloudflare/workerd-linux-arm64": { - "version": "1.20260918.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260918.1.tgz", - "integrity": "sha512-4rib51MaLNWweUIUxM/Xj558M5QmyZoBSf0ffv+lYah5VTrvwnez3XGxX72pElnBKHROvAPOi5msQ5Ts8JSI0A==", + "version": "1.20260921.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260921.1.tgz", + "integrity": "sha512-tnJu08tT7s0XWDqp3O0H/vCp0voy9OqVAzspb89biMo1dh8IiEpnyXnoPmdJ7H4qBnXCmXgy0kuEphuvpDPj9w==", "cpu": [ "arm64" ], @@ -729,9 +763,9 @@ } }, "node_modules/@cloudflare/workerd-windows-64": { - "version": "1.20260918.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260918.1.tgz", - "integrity": "sha512-sATrMx5ShYYgmgUGrcTmvsFSJBFuN95NEkX3xwb1qk8w1A6h5N11sea7yN2IeibwPyPmXKjWNjXOno0hZAM71Q==", + "version": "1.20260921.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260921.1.tgz", + "integrity": "sha512-VgNcRPstoZMb1G94JTrx+jU24GtkkazNfox0gnF/2fkuXpcfW/M0e0xvdMovYfwt8ZxG5AB2ZNvanD6ufBwiuQ==", "cpu": [ "x64" ], @@ -821,7 +855,9 @@ } }, "node_modules/@csstools/color-helpers": { - "version": "6.1.0", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.1.1.tgz", + "integrity": "sha512-gLNsunvwf3mCi5u5o46/Z/JcJMnhbHSaZ69rkgPzNM3J4s8hWwpPUQB6/tt0EDFyCiWzxANlx+2LJwpYj4zS1w==", "dev": true, "funding": [ { @@ -839,7 +875,9 @@ } }, "node_modules/@csstools/css-calc": { - "version": "3.2.1", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.4.0.tgz", + "integrity": "sha512-XQKj5B7QiZcHiegCOCAzcAOJdhGgWOHbbu62h5e5mkHnn8lWcfiJhllkqWmxu5zWR9jucPHuo1iTB56P033hcg==", "dev": true, "funding": [ { @@ -861,7 +899,9 @@ } }, "node_modules/@csstools/css-color-parser": { - "version": "4.1.9", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.2.3.tgz", + "integrity": "sha512-y4LpL+lmpuyKDiEFq2PnZUVFdAjsoB/qQJod79yLNokXyW7jewi+/WJ69EfItj8A2unWtxXnGjw6LYXgXu5ZjA==", "dev": true, "funding": [ { @@ -875,8 +915,8 @@ ], "license": "MIT", "dependencies": { - "@csstools/color-helpers": "^6.1.0", - "@csstools/css-calc": "^3.2.1" + "@csstools/color-helpers": "^6.1.1", + "@csstools/css-calc": "^3.4.0" }, "engines": { "node": ">=20.19.0" @@ -888,6 +928,8 @@ }, "node_modules/@csstools/css-parser-algorithms": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", + "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", "dev": true, "funding": [ { @@ -908,7 +950,9 @@ } }, "node_modules/@csstools/css-syntax-patches-for-csstree": { - "version": "1.1.6", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.14.tgz", + "integrity": "sha512-HpbVXyrofRXpHpgkNIjU/3EWR4WJvOkO3emNK/L6X/mTJU7bGUI3AkkpoTNXznQLp0KRjLHELTGeKI5dIkI9JQ==", "dev": true, "funding": [ { @@ -931,7 +975,9 @@ } }, "node_modules/@csstools/css-tokenizer": { - "version": "4.0.0", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.1.tgz", + "integrity": "sha512-bPlN9S9O1A0euCpEWE4qnvB5YDuyYVsUTrxSgmAM1Is0j4tICHoVyOVAXfWMP/kS9ZrjvyIXWV2PmomiAXXqOw==", "dev": true, "funding": [ { @@ -961,9 +1007,9 @@ } }, "node_modules/@emnapi/runtime": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", - "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", "dev": true, "license": "MIT", "optional": true, @@ -1537,9 +1583,9 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz", - "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==", + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.3.tgz", + "integrity": "sha512-IkO+/KEUvwbVpiURZg+P7zF74z5Jxe0UgJxVni+RtoHQ6IZieXaO02kmadomap/q+l6bc/jdPGGqTjhuZnuz1Q==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1567,25 +1613,31 @@ } }, "node_modules/@floating-ui/core": { - "version": "1.7.5", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.8.0.tgz", + "integrity": "sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==", "license": "MIT", "dependencies": { - "@floating-ui/utils": "^0.2.11" + "@floating-ui/utils": "^0.2.12" } }, "node_modules/@floating-ui/dom": { - "version": "1.7.6", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.8.0.tgz", + "integrity": "sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==", "license": "MIT", "dependencies": { - "@floating-ui/core": "^1.7.5", - "@floating-ui/utils": "^0.2.11" + "@floating-ui/core": "^1.8.0", + "@floating-ui/utils": "^0.2.12" } }, "node_modules/@floating-ui/react-dom": { - "version": "2.1.8", + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.9.tgz", + "integrity": "sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==", "license": "MIT", "dependencies": { - "@floating-ui/dom": "^1.7.6" + "@floating-ui/dom": "^1.8.0" }, "peerDependencies": { "react": ">=16.8.0", @@ -1593,7 +1645,9 @@ } }, "node_modules/@floating-ui/utils": { - "version": "0.2.11", + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.12.tgz", + "integrity": "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==", "license": "MIT" }, "node_modules/@hono/node-server": { @@ -2169,17 +2223,6 @@ "url": "https://opencollective.com/libvips" } }, - "node_modules/@img/sharp-wasm32/node_modules/@emnapi/runtime": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", - "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@img/sharp-webcontainers-wasm32": { "version": "0.35.4", "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.4.tgz", @@ -2293,7 +2336,9 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", "dev": true, "license": "MIT" }, @@ -2306,6 +2351,30 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@keyv/bigmap": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@keyv/bigmap/-/bigmap-1.3.1.tgz", + "integrity": "sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "hashery": "^1.4.0", + "hookified": "^1.15.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "keyv": "^5.6.0" + } + }, + "node_modules/@keyv/serialize": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.1.tgz", + "integrity": "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==", + "dev": true, + "license": "MIT" + }, "node_modules/@loaderkit/resolve": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@loaderkit/resolve/-/resolve-1.0.6.tgz", @@ -2437,11 +2506,13 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.137.0", + "version": "0.150.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.150.0.tgz", + "integrity": "sha512-rDS5/31E9HfPl/CIzGrn0DOlvBbXFseQ5URJ9sYMfstbKLD/c6Gm9vmRzRGDdAXyOIL4zmO37lc9RIwYqVruZw==", "dev": true, "license": "MIT", "funding": { - "url": "https://github.com/sponsors/Boshen" + "url": "https://github.com/sponsors/oxc-project" } }, "node_modules/@poppinss/colors": { @@ -2476,19 +2547,6 @@ "supports-color": "^10.0.0" } }, - "node_modules/@poppinss/dumper/node_modules/supports-color": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", - "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, "node_modules/@poppinss/exception": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/@poppinss/exception/-/exception-1.2.3.tgz", @@ -2497,13 +2555,13 @@ "license": "MIT" }, "node_modules/@publint/pack": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@publint/pack/-/pack-0.1.6.tgz", - "integrity": "sha512-3uVNyGcVplhPZSLVyeIpL7+cIRn1YCSNHLG/rUIlBQMVH8YuN9++YF+5+UDIIO9RW98dujiUoTltO7RDB5bFJA==", + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/@publint/pack/-/pack-0.1.7.tgz", + "integrity": "sha512-4EDEmvxWtgsCnnVeBvtFIFZtUhPPt1+bA9JrSwU4Sa//6oKtzCSlGGXYJr44OD9aGISymbieJ4mCKHUygUDU+g==", "dev": true, "license": "MIT", "dependencies": { - "tinyexec": "^1.2.4" + "tinyexec": "^1.3.0" }, "engines": { "node": ">=18" @@ -2536,18 +2594,27 @@ } } }, - "node_modules/@reduxjs/toolkit/node_modules/immer": { - "version": "11.1.8", + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.9.tgz", + "integrity": "sha512-tNISae1QEf/vkb3xkRcjV5SEdzPE97We5IVaa2Z8jSszQPZ8U60B/YCYpw4QI7VidYsBtKavczXf+DyDs9WGxw==", + "cpu": [ + "arm" + ], + "dev": true, "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/immer" + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.3.tgz", - "integrity": "sha512-DT6Z3PhvioeHMvxo+xHc3KtqggrI7CCTXCmC2h/5zUlp5jVitv7XEy+9q5/7v8IolhlioawpMo8Kg0EEBy7J0g==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.9.tgz", + "integrity": "sha512-YC8YsI30o606GTZi0VyzYlsDKFP8W61i/QzayHDkLbNEz/IShqAmTa+hsJRj13xTHA0H+6fk4b2UmGn+Q/cMlg==", "cpu": [ "arm64" ], @@ -2562,9 +2629,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.3.tgz", - "integrity": "sha512-0NwgwsjM7LrsuVnXMK3koTpagBNOhloc/BNjKqZjv4V5zI5r13qx69uVhRx+o5Z0yy4Hzq+lpy7TAgUG/ocvrw==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.9.tgz", + "integrity": "sha512-IwhlH3qK5urrY8hZiEgGkHKEFN901p/p2bjxCxJlr4GyNnF7wYpUvK+Y43uaRYuC4hpfjzbR3SJC3arX1jGvmw==", "cpu": [ "arm64" ], @@ -2579,9 +2646,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.3.tgz", - "integrity": "sha512-YtiBp4disu6V560loT6PjMdiRaWmVvDNrUunAalbiFx2ggeJwxdAsgZMcoGP17uyAsTwAj5V1niksxlHnVQ1Sw==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.9.tgz", + "integrity": "sha512-XxpJfVzFh+jilRxIXUqcfYAYcunIc/XEzIizsOL1fcJee5Sf7H3mH8WlLmfHfluz5amqR88QQo9izKtmMlavAw==", "cpu": [ "x64" ], @@ -2596,9 +2663,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.3.tgz", - "integrity": "sha512-yD3EkEdXk2LypPxnf/kSZHirarsI8gcPzc62SukhR9VJTyvV+F9Q/GxWNuCojc7sXyuVC4DxRGhdDK4X8VSsbw==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.9.tgz", + "integrity": "sha512-kSfvhmgeWyfkbT3p/1s5vSgboogoah2zkm9fX2zjg2hHxSV7T4KhMWRUUaRk4OXNqoD3QAUeRqLcs1aZOK4U1g==", "cpu": [ "x64" ], @@ -2613,9 +2680,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.3.tgz", - "integrity": "sha512-c+8vieQbsD7HNAHKIA34w0GJ9FedFFuJGD+7E6vz7Q3uqAIugL5p45fhlsj4UaAsHpcmlqugBWMhA0/j7o0sIg==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.9.tgz", + "integrity": "sha512-1RVzG17pxqbTfYLC352JlLt6kKLG+6Hr30n8DlIJqsnV5luUDd2Qdx9Ayw1Cabfyb1K9k0jXEZ7evxkRoT+uiw==", "cpu": [ "arm" ], @@ -2630,9 +2697,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.3.tgz", - "integrity": "sha512-50jD0uUwLvur7Zz9LHz17kaAdTPjn5wN93hEgjvmYFRZwiR7ZJYovTd5ipyWJDAnXKvZ+wgc+/Ika6dwSF5OcA==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.9.tgz", + "integrity": "sha512-BXqPvZ2drqVD+/Z8UpKwcs4Mp7grM+eGFku4CAEKrEtcbAsUpzREphK1sogCRZGreVPiMkiiBtw0n3TPteuqvw==", "cpu": [ "arm64" ], @@ -2650,9 +2717,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.3.tgz", - "integrity": "sha512-BO9+oPL8K9poZJBfYPsXNtYjPE5uM3qeehT3aFcW4LITOl+iSqhp0abzjR2nWBUNjIZeKXjAEWBZ64WjNoHd6w==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.9.tgz", + "integrity": "sha512-11vWvo8YDwLzukt27J3aYDWU+gg2P7J+ZOmiJ0hkF5BXZDW7pVya7r40MXDy6ya0i9KamoENSVKIugvJNgFXIA==", "cpu": [ "arm64" ], @@ -2670,9 +2737,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.3.tgz", - "integrity": "sha512-f3VpLB1vQ0Eo6ecr/6cekLnvYMFF4YBFoVGkfkvPLq1bAkbAwHYQPZKoAmG6OJyTcxxoC+AvezGx/S1obNC0Mw==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.9.tgz", + "integrity": "sha512-a1tijMkdwsIARtc0F39ApURROkf3NwqinI6TOiSSWCTR7dT96dffNvMUtDHnq64wKNTIZOIlzKrFvvFUznJiyw==", "cpu": [ "ppc64" ], @@ -2690,9 +2757,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.3.tgz", - "integrity": "sha512-AmurZ26Pqx/RI9N1gzEOCklkKXl927yjfXWUUS0O7Puh8ARM/Ob8qfrD3qnWksScdw6cSrW5PSHE9DyLu7+PtA==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.9.tgz", + "integrity": "sha512-x6SQNdAvv4c3hWqTMaWuawzMX9myaCs/yEmlGsxJzkdClnHW7FbrjQuSiRDhuSYzEYoEMhsaJy9qHG/XNemJPQ==", "cpu": [ "s390x" ], @@ -2710,9 +2777,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.3.tgz", - "integrity": "sha512-JJpqs8bRGITDOdbkNKnlojzBabbOHrqjSvDr0IVsZObE1lBcPjxItUEY9eWIDbxaJ3cGrXPWGfGkIxFijg/URg==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.9.tgz", + "integrity": "sha512-9s0AZ8BFK5/n7B/TBoa2yJE3gI3KURrbXcPBlsAsvjU4VeJKgE90y1YtNxyEUIcHPQkg6/yfF3qihUrcM/Kf0Q==", "cpu": [ "x64" ], @@ -2730,9 +2797,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.3.tgz", - "integrity": "sha512-rSJcdjPxzA/by/6/rYs+v+bXU7UjvnbUWz8MJb6kh6+knqB1dCrtHg0uu7C/4haqJvqdkYHQ5IGn+tCH9GLW/g==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.9.tgz", + "integrity": "sha512-P7VWAmV+WdJluH7ovnRGoiv2i8To7GAZ+kGzfGup635cyL7SyYl3lSUaA3Gp5THf0n/Co5EyEqb2zbqq+nMOHQ==", "cpu": [ "x64" ], @@ -2750,9 +2817,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.3.tgz", - "integrity": "sha512-hQ3/PYkDJICgevvyNcVrihVeqq7k1Pp3VZ9lY+dauAYUJKO+auqApvANhvR1An9BhmqYKvW2Mu1F9u4DXSMLxQ==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.9.tgz", + "integrity": "sha512-1qixtsE4BK8h+yS3BfmZ09UhA7O/N4IACva6YBr7EBvCJraByTuRcgOTaiA62Tm0vey3UcKXLOaoGHtYmNGEVg==", "cpu": [ "arm64" ], @@ -2766,29 +2833,10 @@ "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.3.tgz", - "integrity": "sha512-Elcv/BtML9lXrV6JuKITc/grN2kYV9gjsQpW8Jfw4ioK0TOkjBjye0nnyqQNy9STNaI20lXNaQBRrD5gSgR0Yg==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "1.11.1", - "@emnapi/runtime": "1.11.1", - "@napi-rs/wasm-runtime": "^1.1.6" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.3.tgz", - "integrity": "sha512-2DrEfhluH9yhiaFApmsjsjwrSYbNcY1oFTzYSP1a535jDbV98zCFanA/96TBUd0iDFcxGmw9QRExwGCXz3U+/g==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.9.tgz", + "integrity": "sha512-ok8IQjcEPs1AKZfuEUznVBrJw+gK4soq+bx8b1X2XoMqVClarc1q5JDmVtWXY1xfr6ZuHTAsPXHTgTrqKTZeww==", "cpu": [ "arm64" ], @@ -2803,7 +2851,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.1.3", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.9.tgz", + "integrity": "sha512-Ip2mXoU0hM0boq3Rf+ekuT653OROSo6aSYcPT1VHE4q52KvyxgFkQgrgb/IEsxOuvQ2fZZbs8khJAyCEPM24/g==", "cpu": [ "x64" ], @@ -3240,45 +3290,49 @@ "license": "MIT" }, "node_modules/@tailwindcss/node": { - "version": "4.3.2", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.3.tgz", + "integrity": "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==", "dev": true, "license": "MIT", "dependencies": { "@jridgewell/remapping": "^2.3.5", - "enhanced-resolve": "5.21.6", + "enhanced-resolve": "^5.24.1", "jiti": "^2.7.0", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", - "tailwindcss": "4.3.2" + "tailwindcss": "4.3.3" } }, "node_modules/@tailwindcss/oxide": { - "version": "4.3.2", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.3.tgz", + "integrity": "sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==", "dev": true, "license": "MIT", "engines": { "node": ">= 20" }, "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.3.2", - "@tailwindcss/oxide-darwin-arm64": "4.3.2", - "@tailwindcss/oxide-darwin-x64": "4.3.2", - "@tailwindcss/oxide-freebsd-x64": "4.3.2", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.2", - "@tailwindcss/oxide-linux-arm64-gnu": "4.3.2", - "@tailwindcss/oxide-linux-arm64-musl": "4.3.2", - "@tailwindcss/oxide-linux-x64-gnu": "4.3.2", - "@tailwindcss/oxide-linux-x64-musl": "4.3.2", - "@tailwindcss/oxide-wasm32-wasi": "4.3.2", - "@tailwindcss/oxide-win32-arm64-msvc": "4.3.2", - "@tailwindcss/oxide-win32-x64-msvc": "4.3.2" + "@tailwindcss/oxide-android-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-x64": "4.3.3", + "@tailwindcss/oxide-freebsd-x64": "4.3.3", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.3", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-x64-musl": "4.3.3", + "@tailwindcss/oxide-wasm32-wasi": "4.3.3", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.3", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.3" } }, "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.2.tgz", - "integrity": "sha512-WHxqIuHpvZ5VtdX6GTl1Ik/Vp2YuN42Et+0CdeaVd/frQ9jAvGmvR8vLT+jk3e8/Q3x8kECB9+R17pgpp2BulA==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.3.tgz", + "integrity": "sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==", "cpu": [ "arm64" ], @@ -3293,9 +3347,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.2.tgz", - "integrity": "sha512-GZypeUY/IDJW3877KeM+O67vbXr3MBnbtEL4aYhNErv/JWZhye2vGSWWG9tB6iiqR2MqRNkY8IOUy4NdSZV26w==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.3.tgz", + "integrity": "sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==", "cpu": [ "arm64" ], @@ -3310,9 +3364,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.2.tgz", - "integrity": "sha512-UIIzmefR6KO1sDU7MzRqAxC8iBpft/VhkGjTjnhoS6k7Z3rQ9wEgA1ODSiyH/tcSYssulNm4Ci3hOeK1jH7ccQ==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.3.tgz", + "integrity": "sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==", "cpu": [ "x64" ], @@ -3327,9 +3381,9 @@ } }, "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.2.tgz", - "integrity": "sha512-GN+uAmcI6DNspnCDwtOAZrTz6oukJnp337qZvxqCGLd3BHBzJpO0ZbTLRvJNdztOeAmTzewewGIMPb0tk2R4WA==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.3.tgz", + "integrity": "sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==", "cpu": [ "x64" ], @@ -3344,9 +3398,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.2.tgz", - "integrity": "sha512-4ABn7qSbdHRwTiDiuWNegCyb5+2FJ4vKIKc3DmKrvAFw7MU1Lm11dIkTPwUaFdTzc7IsOpDbqBrlh0x6y36U/w==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.3.tgz", + "integrity": "sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==", "cpu": [ "arm" ], @@ -3361,9 +3415,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.2.tgz", - "integrity": "sha512-wDgEIGwoM8w8pufh9LVt1PahDgNdKXrLC2qfAnV3vAmococ9RWbxeAw4pxPttd/TsJfwjyLf90Dg1y9y8I6Emw==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.3.tgz", + "integrity": "sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==", "cpu": [ "arm64" ], @@ -3381,9 +3435,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.2.tgz", - "integrity": "sha512-J5Nuk0uZQIiMTJj3LEx4sAA9tMFUoXQZFv1J6An+QGYe53HKRJuFDi0rpq/tuouCZeAbOBY3kQ6g8qeD4TUjtA==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.3.tgz", + "integrity": "sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==", "cpu": [ "arm64" ], @@ -3401,9 +3455,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.2.tgz", - "integrity": "sha512-kqCZpSKOBEJO4mz7OqWoofBZeXTAwaVGPj0ErAj7CojmhKpWVWVOnrt9dE8odoIraZq4oj3ausM37kXi+Tow8w==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.3.tgz", + "integrity": "sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==", "cpu": [ "x64" ], @@ -3421,9 +3475,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.2.tgz", - "integrity": "sha512-cixpqbh2toJDmkuCRI68nXA8ZxNmdK9Y+9v5h3MC3ZQKy/0BO8AWzlkWyRM7JAFSGBlfig4YVTPsK6MVgqz1uw==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.3.tgz", + "integrity": "sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==", "cpu": [ "x64" ], @@ -3441,9 +3495,9 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.2.tgz", - "integrity": "sha512-4ec2Z/LOmRsAgU23CS4xeJfcJlmRg94A/XrbGRCF1gyU/zdDfRLYDVsS+ynSZCmGNxQ1jQriQOKMQeQxBA3Isw==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.3.tgz", + "integrity": "sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==", "bundleDependencies": [ "@napi-rs/wasm-runtime", "@emnapi/core", @@ -3471,9 +3525,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.2.tgz", - "integrity": "sha512-Zyr/M0+XcYZu3bZrUytc7TXvrk0ftWfl8gN2MwekNDzhqhKRUucMPSeOzM0o0wH5AWOU49BsKRrfKxI2atCPMQ==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.3.tgz", + "integrity": "sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==", "cpu": [ "arm64" ], @@ -3488,7 +3542,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.3.2", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.3.tgz", + "integrity": "sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==", "cpu": [ "x64" ], @@ -3503,20 +3559,24 @@ } }, "node_modules/@tailwindcss/vite": { - "version": "4.3.2", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.3.tgz", + "integrity": "sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw==", "dev": true, "license": "MIT", "dependencies": { - "@tailwindcss/node": "4.3.2", - "@tailwindcss/oxide": "4.3.2", - "tailwindcss": "4.3.2" + "@tailwindcss/node": "4.3.3", + "@tailwindcss/oxide": "4.3.3", + "tailwindcss": "4.3.3" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7 || ^8" } }, "node_modules/@testing-library/dom": { - "version": "10.4.1", + "version": "10.4.2", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.2.tgz", + "integrity": "sha512-yzr2S9HyAIdhz2/6qHgbs665Q7PKVcDF05vsOlHPxG1mo36gKVesdYVeDLnXgfjJ03CrKRk08knc6+E/9m8v2Q==", "dev": true, "license": "MIT", "dependencies": { @@ -3534,7 +3594,9 @@ } }, "node_modules/@testing-library/react": { - "version": "16.3.2", + "version": "16.3.3", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.3.tgz", + "integrity": "sha512-Uo193NgQbPMz6lrrhtRQQFcMC6Re/ELLFbbuVL30WDlZxlpZf9/lMHTAVxPRLw1q1iu9OJmR1c2BLiENRstdBg==", "dev": true, "license": "MIT", "dependencies": { @@ -3689,7 +3751,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "25.9.4", + "version": "25.9.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.8.tgz", + "integrity": "sha512-VfMrScDmMhUJQmd5hArdQnFvK0OIeD36uN2Va1FcpYaLB/BgkgM8Ulc50XtYISPMz7APJ30+N0T5EM0jlcdfRw==", "dev": true, "license": "MIT", "dependencies": { @@ -3702,22 +3766,22 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "19.2.18", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz", - "integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==", + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.3.0.tgz", + "integrity": "sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==", "license": "MIT", "dependencies": { "csstype": "^3.2.2" } }, "node_modules/@types/react-dom": { - "version": "19.2.4", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.4.tgz", - "integrity": "sha512-Bsc+QHgp+P/F02XDzNCY9jnZNCUuLki36KT7VKrTXXLdHf+vHMNZnW1rVu5DNW/rCK+fya3DATySbLM4yhtKUw==", + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.3.0.tgz", + "integrity": "sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q==", "dev": true, "license": "MIT", "peerDependencies": { - "@types/react": "^19.2.0" + "@types/react": "^19.3.0" } }, "node_modules/@types/unist": { @@ -3728,139 +3792,15 @@ "version": "0.0.6", "license": "MIT" }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.66.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.66.0.tgz", - "integrity": "sha512-p088eaGrzYz1s+7cov0aMOCkNGTJlVxF4jgubf28c8L0Cv9Rloj8YBHnv4hXLq6IIEE1AsjNWavO+k+8kP2Y0A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.66.0", - "@typescript-eslint/type-utils": "8.66.0", - "@typescript-eslint/utils": "8.66.0", - "@typescript-eslint/visitor-keys": "8.66.0", - "ignore": "^7.0.5", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.5.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.66.0", - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz", - "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.66.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.66.0.tgz", - "integrity": "sha512-X6ypGChaWYk6PBtUg2BwuTZEFFcHJAtGTVJ9/lCTOufhZ4i9fNolQNnktq+kkMCwMj7V8Svsq7+TxSDslmhE0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.66.0", - "@typescript-eslint/types": "8.66.0", - "@typescript-eslint/typescript-estree": "8.66.0", - "@typescript-eslint/visitor-keys": "8.66.0", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.66.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.66.0.tgz", - "integrity": "sha512-7MthGPTt4BP69lSryqpqq8HQqxuzynssckL/jyDyk3+TNMQ3y2jFWkptCrktWvBrP+EH787Nl5N5Qpw7WZg+5g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.66.0", - "@typescript-eslint/types": "^8.66.0", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.66.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.66.0.tgz", - "integrity": "sha512-8TGcH25j9zqJ/IULB/ppyhRvxA8QYfFEZ7nfbg6/BN9spDgb8fPWQXlE5l8TWBL50EtUx007uZ1o9VOwrq2/9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.66.0", - "@typescript-eslint/visitor-keys": "8.66.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.66.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.66.0.tgz", - "integrity": "sha512-9D5gLYZG4rOjcoag8MQ/fWI8WqA9wcPDyOGyWtWFhvM1lHRbliqUSPIY5J3zqCU1tvSwzXxnnjhQhz5Ne7mJ4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.66.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.66.0.tgz", - "integrity": "sha512-LG2dWfjZQQp0ADtAu/EWJVayefGL2UEZ3CDeI44D9v3rXB/WYUqE/jpO28KrEKul5AySrmI+Zh1v6v+xW2U9+g==", + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.70.1.tgz", + "integrity": "sha512-Pa0EeSeAusQc1WbjQMac+YfenewYTBu0KjgYvkUKwhXaHUKbFog23Dm/rp0DX/6tyYOQ3Xl1a+3EcFNZynGHCw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.66.0", - "@typescript-eslint/typescript-estree": "8.66.0", - "@typescript-eslint/utils": "8.66.0", - "debug": "^4.4.3", - "ts-api-utils": "^2.5.0" + "@typescript-eslint/types": "8.70.1", + "@typescript-eslint/visitor-keys": "8.70.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3868,86 +3808,30 @@ "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.66.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.66.0.tgz", - "integrity": "sha512-H6gcYaSDOyvL3AD/jHUtUFo2jqGgn/F6nuyuZSu0QTesxL+cP4dQoIMrODRofuJC09g64+WgZ6tE19Y1N2YIFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.66.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.66.0.tgz", - "integrity": "sha512-8/x4INiiQb10jGgXYD7116/zQ+OL84ZIFn0za68wwFHCanT/VLbBEroWht8RV8fn0/ZCAoazHLQgwUC0UQcDfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.66.0", - "@typescript-eslint/tsconfig-utils": "8.66.0", - "@typescript-eslint/types": "8.66.0", - "@typescript-eslint/visitor-keys": "8.66.0", - "debug": "^4.4.3", - "minimatch": "^10.2.2", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.5.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "8.66.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.66.0.tgz", - "integrity": "sha512-jasearZPolBw5NJNYGMwxzHMF83niVWmMU1VdHzG1CyfI2VS7f7nZltnKtHcg20hW+7Uo5GfK4MeDPoU3qI8EA==", + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.70.1.tgz", + "integrity": "sha512-Dm1ypdhhrGCTyyehxElhgJ6kgk8MVCv5qXdoOVqPr1uqk42jX8KjrZqhROvdShczA8qrDoYiOWn1ykWlx2k81Q==", "dev": true, "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.66.0", - "@typescript-eslint/types": "8.66.0", - "@typescript-eslint/typescript-estree": "8.66.0" - }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.66.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.66.0.tgz", - "integrity": "sha512-dkKR8q+lKciskj1Y3vthHktl+3cMLWGyVUP23bRiPZ5O9BRT++4EqDDV+TVeIKBL1VXVEqrJlz8MYbcnvJcAlg==", + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.70.1.tgz", + "integrity": "sha512-Vwj9lUIW5Xq3wQ9w6gv3R86g1hMK8f2zNOdGTAgeXUMMXFK78G9ruCjjqutHMNJc0+CH7LYRnHeUB9IT8wFmcw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.66.0", + "@typescript-eslint/types": "8.70.1", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -4340,9 +4224,9 @@ ] }, "node_modules/@vitejs/plugin-react": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.5.tgz", - "integrity": "sha512-BOVzne/NL162sMdResB25mUv+vWMF5NoAjNf09TeGlE7ZpszZWSD3winycicLJw72yeVsoCn/2kOhEuCvEShMA==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.1.1.tgz", + "integrity": "sha512-yxLaQV9gkhS8ezJqCM6+ndU7mDY6gqAg75NQ+0IjwEI8IYOmQCgkRwHKVSfWXW076DsqMo0Dk+0FK1U+M5RgFw==", "dev": true, "license": "MIT", "dependencies": { @@ -4354,6 +4238,7 @@ "peerDependencies": { "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", "babel-plugin-react-compiler": "^1.0.0", + "oxc-transform-react": "^0.145.0", "vite": "^8.0.0" }, "peerDependenciesMeta": { @@ -4362,6 +4247,9 @@ }, "babel-plugin-react-compiler": { "optional": true + }, + "oxc-transform-react": { + "optional": true } } }, @@ -4610,7 +4498,9 @@ } }, "node_modules/bidi-js": { - "version": "1.0.3", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.1.0.tgz", + "integrity": "sha512-fX1Onk0tdVPC7obPWB5EbJ1z7NVhLq4m2xZLq2YXBkxzMXIGRpNMU88n0EPgWseKl12J7zXs7qrDxPK4sRs2fg==", "dev": true, "license": "MIT", "dependencies": { @@ -4731,6 +4621,20 @@ "node": ">=8" } }, + "node_modules/cacheable": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-2.5.0.tgz", + "integrity": "sha512-60cyAOytib/OzBw1JNSoSV/boK1AtHryDIjvVBk7XbN4ugfkM3+Sry7fEjNgPMGgOjuaZPAp8ruZ0Cxafwyq9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cacheable/memory": "^2.2.0", + "@cacheable/utils": "^2.5.0", + "hookified": "^1.15.0", + "keyv": "^5.6.0", + "qified": "^0.10.1" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001806", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz", @@ -4772,6 +4676,8 @@ }, "node_modules/chalk": { "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", "dev": true, "license": "MIT", "engines": { @@ -5071,6 +4977,8 @@ }, "node_modules/cliui": { "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "license": "ISC", "dependencies": { @@ -5084,6 +4992,8 @@ }, "node_modules/cliui/node_modules/string-width": { "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "license": "MIT", "dependencies": { @@ -5097,6 +5007,8 @@ }, "node_modules/cliui/node_modules/strip-ansi": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "license": "MIT", "dependencies": { @@ -5193,6 +5105,8 @@ }, "node_modules/concurrently/node_modules/ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "license": "MIT", "dependencies": { @@ -5207,6 +5121,8 @@ }, "node_modules/concurrently/node_modules/chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", "dependencies": { @@ -5222,6 +5138,8 @@ }, "node_modules/concurrently/node_modules/chalk/node_modules/supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", "dependencies": { @@ -5231,6 +5149,22 @@ "node": ">=8" } }, + "node_modules/concurrently/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/confbox": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", @@ -5534,7 +5468,9 @@ "license": "MIT" }, "node_modules/enhanced-resolve": { - "version": "5.21.6", + "version": "5.25.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.25.1.tgz", + "integrity": "sha512-nGXts5znJzmWPu+mIE9izCOzdg63oJca2mDzGWWTth7sr4aCToKcoyFVBQwN75Ij5Pf6p510EwkTqViTRzDV+w==", "dev": true, "license": "MIT", "dependencies": { @@ -5580,7 +5516,9 @@ } }, "node_modules/es-module-lexer": { - "version": "2.2.0", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz", + "integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==", "dev": true, "license": "MIT" }, @@ -5651,9 +5589,9 @@ } }, "node_modules/eslint": { - "version": "10.8.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.8.1.tgz", - "integrity": "sha512-wqA7W2jbsC/BnV9Iv1UZpKVFkO1AdNoSmYW8NWG4HNOBbkAMvIqDZ27pI2f07dqn583NcIC44ckjAcOXDL1QbQ==", + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.11.0.tgz", + "integrity": "sha512-P7a6UEEqb9G95MYAtqkmsTbVXIYyzIfl6NGOIJk162PaahFxFyeGcrlXYFSiagECg4sEm8IseJdZBKR3rx6MsQ==", "dev": true, "license": "MIT", "workspaces": [ @@ -5665,7 +5603,7 @@ "@eslint/config-array": "^0.23.5", "@eslint/config-helpers": "^0.7.0", "@eslint/core": "^1.2.1", - "@eslint/plugin-kit": "^0.7.2", + "@eslint/plugin-kit": "^0.7.3", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -5680,7 +5618,7 @@ "esquery": "^1.7.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", + "file-entry-cache": "11.1.5 || >11.1.6 <12", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", @@ -6014,16 +5952,13 @@ "license": "MIT" }, "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-11.1.5.tgz", + "integrity": "sha512-+PFTHITI08JIGhnNpGNI8T8inUpgZfk3GNEqfT9R2zZV2iFXg3CvqzSl/uEhs7TSGujYRELEANyDvS8Fj7+S7Q==", "dev": true, "license": "MIT", "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" + "flat-cache": "^6.1.23" } }, "node_modules/find-up": { @@ -6056,17 +5991,15 @@ } }, "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "version": "6.1.23", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.23.tgz", + "integrity": "sha512-f++BY9pTk+983xK1FLzlLpmM0i0z+jHmx3QESGkURMXujQZz1k5wzwX6hjnQ8goaD0B+sYnDK1yZ6MTyZfUaqA==", "dev": true, "license": "MIT", "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" + "cacheable": "^2.5.0", + "flatted": "^3.4.2", + "hookified": "^1.15.0" } }, "node_modules/flatted": { @@ -6077,10 +6010,12 @@ "license": "ISC" }, "node_modules/framer-motion": { - "version": "12.42.2", + "version": "12.43.0", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.43.0.tgz", + "integrity": "sha512-1eaL3RvR/kAlbG7UYcpMptEyzPoENO0c6w7ZnB3/hh2vSAz/6uGAFn6fdoqTBguNstf3MsFhJHsD/0DHiclG+g==", "license": "MIT", "dependencies": { - "motion-dom": "^12.42.2", + "motion-dom": "^12.43.0", "motion-utils": "^12.39.0", "tslib": "^2.4.0" }, @@ -6173,6 +6108,8 @@ }, "node_modules/graceful-fs": { "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true, "license": "ISC" }, @@ -6184,6 +6121,19 @@ "node": ">=8" } }, + "node_modules/hashery": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/hashery/-/hashery-1.5.1.tgz", + "integrity": "sha512-iZyKG96/JwPz1N55vj2Ie2vXbhu440zfUfJvSwEqEbeLluk7NnapfGqa7LH0mOsnDxTF85Mx8/dyR6HfqcbmbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "hookified": "^1.15.0" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/hast-util-from-parse5": { "version": "8.0.3", "license": "MIT", @@ -6373,8 +6323,17 @@ "node": ">=16.9.0" } }, + "node_modules/hookified": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.15.1.tgz", + "integrity": "sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==", + "dev": true, + "license": "MIT" + }, "node_modules/html-encoding-sniffer": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", + "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", "dev": true, "license": "MIT", "dependencies": { @@ -6411,7 +6370,9 @@ } }, "node_modules/immer": { - "version": "10.2.0", + "version": "11.1.18", + "resolved": "https://registry.npmjs.org/immer/-/immer-11.1.18.tgz", + "integrity": "sha512-EQyQtLiYW029lyoczMl/Hh4Xu7cDecSc58JRYpHyL4tIAu3eqd1yJzQX04d2BZHDkzFFvm6qJEJWOtfDSWAXbQ==", "license": "MIT", "funding": { "type": "opencollective", @@ -6560,6 +6521,8 @@ }, "node_modules/is-potential-custom-element-name": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", "dev": true, "license": "MIT" }, @@ -6606,6 +6569,8 @@ }, "node_modules/jsdom": { "version": "29.1.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.1.1.tgz", + "integrity": "sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==", "dev": true, "license": "MIT", "dependencies": { @@ -6656,13 +6621,6 @@ "node": ">=6" } }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -6700,13 +6658,13 @@ } }, "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", + "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", "dev": true, "license": "MIT", "dependencies": { - "json-buffer": "3.0.1" + "@keyv/serialize": "^1.1.1" } }, "node_modules/kleur": { @@ -6764,6 +6722,8 @@ }, "node_modules/lightningcss": { "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", "dev": true, "license": "MPL-2.0", "dependencies": { @@ -7014,6 +6974,8 @@ }, "node_modules/lightningcss-win32-x64-msvc": { "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", "cpu": [ "x64" ], @@ -7101,7 +7063,9 @@ } }, "node_modules/lru-cache": { - "version": "11.5.1", + "version": "11.5.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.3.tgz", + "integrity": "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==", "dev": true, "license": "BlueOak-1.0.0", "engines": { @@ -7109,7 +7073,9 @@ } }, "node_modules/lucide-react": { - "version": "1.22.0", + "version": "1.47.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.47.0.tgz", + "integrity": "sha512-o8C23aXpNQypRY73W7fW02EyvWJinEMXgKeGjFoKym0zj3Q73hD97A1IQps1g8C14HTGsOpZL0Am+xjfgFZAbg==", "license": "ISC", "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" @@ -7960,16 +7926,16 @@ } }, "node_modules/miniflare": { - "version": "5.20260918.0-alpha", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-5.20260918.0-alpha.tgz", - "integrity": "sha512-vyIes7yW/OTzHtz4GhcBCTohZfqk2eQNiIkngZ07VRA5Qu24aNgW/TrFwlPkMLMjWDQ8R4JJBBHR/KX+liSDtg==", + "version": "5.20260921.0-alpha", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-5.20260921.0-alpha.tgz", + "integrity": "sha512-vHH/unOYvV2jA1Q9SdkmzrQhhMoksdwg5jegu6ZeKaaRzgxZhVbt1NdTpQjHF2VTgiBjgP8SiUlUMfruB3N3SQ==", "dev": true, "license": "MIT", "dependencies": { "@cspotcode/source-map-support": "0.8.1", "sharp": "0.35.4", "undici": "7.29.0", - "workerd": "1.20260918.1", + "workerd": "1.20260921.1", "ws": "8.21.0", "youch": "4.1.0-beta.10" }, @@ -8007,10 +7973,12 @@ } }, "node_modules/motion": { - "version": "12.42.2", + "version": "12.43.0", + "resolved": "https://registry.npmjs.org/motion/-/motion-12.43.0.tgz", + "integrity": "sha512-BQgQbSa9Hn3/mtbib0MK53y6JSANa+YKUKlaYnWzAVDH424RYQ5LVpV3pNiWH00BA2z4ojsSdMzqT7g2FQwjuQ==", "license": "MIT", "dependencies": { - "framer-motion": "^12.42.2", + "framer-motion": "^12.43.0", "tslib": "^2.4.0" }, "peerDependencies": { @@ -8031,7 +7999,9 @@ } }, "node_modules/motion-dom": { - "version": "12.42.2", + "version": "12.43.0", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.43.0.tgz", + "integrity": "sha512-azKON4d9S65PEoFUiQTMTgPheEmzf2QngdRc50AKfJp9Q9mmcBVw22c8eMq9k8kxOFHdL7+WZY7N/5F/lwiDag==", "license": "MIT", "dependencies": { "motion-utils": "^12.39.0" @@ -8039,6 +8009,8 @@ }, "node_modules/motion-utils": { "version": "12.39.0", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.39.0.tgz", + "integrity": "sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ==", "license": "MIT" }, "node_modules/mri": { @@ -8210,7 +8182,9 @@ } }, "node_modules/obug": { - "version": "2.1.3", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.2.1.tgz", + "integrity": "sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==", "dev": true, "funding": [ "https://github.com/sponsors/sxzz", @@ -8398,9 +8372,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", - "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "license": "MIT", "engines": { "node": ">=12" @@ -8432,9 +8406,9 @@ } }, "node_modules/postcss": { - "version": "8.5.26", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", - "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", + "version": "8.5.28", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", + "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", "dev": true, "funding": [ { @@ -8452,7 +8426,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.17", + "nanoid": "^3.3.18", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -8582,14 +8556,14 @@ } }, "node_modules/publint": { - "version": "0.3.23", - "resolved": "https://registry.npmjs.org/publint/-/publint-0.3.23.tgz", - "integrity": "sha512-5MQipUPcB7MWw84zLUkHrg/H/UBtk3LL+A0GngTTBSsiNJLQurMUaSIRG3edlOrRz4UFe0AOKK9TZdIWviV+jQ==", + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/publint/-/publint-0.3.24.tgz", + "integrity": "sha512-9zS56KrKBoqi5Qt8h92uMP8TTM9AYZSgnmCo4u2priMqkOZvQnTsziZ2p5LJ2ywbYkAjoCDp2jda9u4cgFefIw==", "dev": true, "license": "MIT", "dependencies": { - "@publint/pack": "^0.1.6", - "package-manager-detector": "^1.7.0", + "@publint/pack": "^0.1.7", + "package-manager-detector": "^1.8.0", "picocolors": "^1.1.1", "sade": "^1.8.1" }, @@ -8611,25 +8585,45 @@ "node": ">=6" } }, + "node_modules/qified": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/qified/-/qified-0.10.1.tgz", + "integrity": "sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hookified": "^2.1.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/qified/node_modules/hookified": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-2.2.0.tgz", + "integrity": "sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA==", + "dev": true, + "license": "MIT" + }, "node_modules/react": { - "version": "19.2.8", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz", - "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==", + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.3.0.tgz", + "integrity": "sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "19.2.8", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz", - "integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==", + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.3.0.tgz", + "integrity": "sha512-JDk8dgif51OjFoDE70+OT9ICyYr+69HlmihNwp1+Nsfbna3t5sIiCa9ZJktDmQ4/1b/rn26hIAR2uYXDMr5r0Q==", "license": "MIT", "dependencies": { - "scheduler": "^0.27.0" + "scheduler": "^0.28.0" }, "peerDependencies": { - "react": "^19.2.8" + "react": "^19.3.0" } }, "node_modules/react-is": { @@ -8684,9 +8678,9 @@ } }, "node_modules/react-router": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.2.tgz", - "integrity": "sha512-aUVMjFm3GAPTTZL7oYr5E7ETiqfQCHRLH+B+5afnICvf0r7kkK4eR6SMuwbSTJw/7t+12khT/Kahij49fqOCIg==", + "version": "7.18.4", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.4.tgz", + "integrity": "sha512-PUPQcMhMGRAslLcvtlPz/kmzBEWPhLdgLFrL7pLNepBL6dX0lWj4WD2cUYVgYCuT3jxvghYFg81cDTj44DhetQ==", "license": "MIT", "dependencies": { "cookie": "^1.0.1", @@ -8706,12 +8700,12 @@ } }, "node_modules/react-router-dom": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.18.2.tgz", - "integrity": "sha512-AIKJ/jgGlFb3EbfCXk5Gzshiwt+l3mqbCrNjmEWMMjqQxNJ3svBa6bgzFyCC2Sw3RA0VWF1kg3uQf2OFhxb8hw==", + "version": "7.18.4", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.18.4.tgz", + "integrity": "sha512-yrfmJHIpDG7taCpqKjT1G5B6q3O2K+RN8/fgNf0lTjCwiPbQ0ei6vXX9ZjQR+7ld8Tr7Z5xmyMnZ8YJrphWQUw==", "license": "MIT", "dependencies": { - "react-router": "7.18.2" + "react-router": "7.18.4" }, "engines": { "node": ">=20.0.0" @@ -8736,7 +8730,9 @@ } }, "node_modules/recharts": { - "version": "3.9.0", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.10.1.tgz", + "integrity": "sha512-QXFrvt6IVcw7eeZCoyXTwkIJAX3Dv1nyVhMicXJ47GsGDDpcN8z6o644DibE9XjpBTThtsomLKnTV6lc+cVFUA==", "license": "MIT", "workspaces": [ "www" @@ -8747,7 +8743,7 @@ "decimal.js-light": "^2.5.1", "es-toolkit": "^1.39.3", "eventemitter3": "^5.0.1", - "immer": "^10.1.1", + "immer": "^11.1.8", "react-redux": "8.x.x || 9.x.x", "reselect": "5.2.0", "tiny-invariant": "^1.3.3", @@ -8876,6 +8872,8 @@ }, "node_modules/require-from-string": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true, "license": "MIT", "engines": { @@ -8922,11 +8920,13 @@ } }, "node_modules/rolldown": { - "version": "1.1.3", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.9.tgz", + "integrity": "sha512-hx/Pv0N1haXRb11qkfnK5MXB/iqr7i0yjWQqmO9uHqZpBgQSqzc8UsSnEpalsh+j1I8qQ2CkXAkJC8Br3dKSlg==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.137.0", + "@oxc-project/types": "=0.150.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { @@ -8936,21 +8936,21 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.1.3", - "@rolldown/binding-darwin-arm64": "1.1.3", - "@rolldown/binding-darwin-x64": "1.1.3", - "@rolldown/binding-freebsd-x64": "1.1.3", - "@rolldown/binding-linux-arm-gnueabihf": "1.1.3", - "@rolldown/binding-linux-arm64-gnu": "1.1.3", - "@rolldown/binding-linux-arm64-musl": "1.1.3", - "@rolldown/binding-linux-ppc64-gnu": "1.1.3", - "@rolldown/binding-linux-s390x-gnu": "1.1.3", - "@rolldown/binding-linux-x64-gnu": "1.1.3", - "@rolldown/binding-linux-x64-musl": "1.1.3", - "@rolldown/binding-openharmony-arm64": "1.1.3", - "@rolldown/binding-wasm32-wasi": "1.1.3", - "@rolldown/binding-win32-arm64-msvc": "1.1.3", - "@rolldown/binding-win32-x64-msvc": "1.1.3" + "@rolldown/binding-android-arm-eabi": "1.2.9", + "@rolldown/binding-android-arm64": "1.2.9", + "@rolldown/binding-darwin-arm64": "1.2.9", + "@rolldown/binding-darwin-x64": "1.2.9", + "@rolldown/binding-freebsd-x64": "1.2.9", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.9", + "@rolldown/binding-linux-arm64-gnu": "1.2.9", + "@rolldown/binding-linux-arm64-musl": "1.2.9", + "@rolldown/binding-linux-ppc64-gnu": "1.2.9", + "@rolldown/binding-linux-s390x-gnu": "1.2.9", + "@rolldown/binding-linux-x64-gnu": "1.2.9", + "@rolldown/binding-linux-x64-musl": "1.2.9", + "@rolldown/binding-openharmony-arm64": "1.2.9", + "@rolldown/binding-win32-arm64-msvc": "1.2.9", + "@rolldown/binding-win32-x64-msvc": "1.2.9" } }, "node_modules/rollup": { @@ -9032,7 +9032,9 @@ } }, "node_modules/scheduler": { - "version": "0.27.0", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.28.0.tgz", + "integrity": "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==", "license": "MIT" }, "node_modules/semver": { @@ -9174,11 +9176,19 @@ } }, "node_modules/sonner": { - "version": "2.0.7", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.8.tgz", + "integrity": "sha512-UM/ByIoFra8yzV75n1o0Puu0bw5U/9UNnDacrJNspekBewIfsQ3D6ez1nvlWpt7aTsO6rujQtifBpycwIivqlg==", "license": "MIT", "peerDependencies": { + "@types/react": "^18.0.0 || ^19.0.0", "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, "node_modules/source-map": { @@ -9229,7 +9239,9 @@ "license": "MIT" }, "node_modules/std-env": { - "version": "4.1.0", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz", + "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==", "dev": true, "license": "MIT" }, @@ -9344,20 +9356,27 @@ } }, "node_modules/sugar-high": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/sugar-high/-/sugar-high-2.0.0.tgz", - "integrity": "sha512-qZQaa+3vbmKSx00G465bY1LqVUU0RaUkY/7xDV63cQzezD6dF/DJCthztRQqYLGA7HXdObvCEtcCRpvoIrrbCg==", - "license": "MIT" + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/sugar-high/-/sugar-high-2.4.1.tgz", + "integrity": "sha512-w4uhunMgEP+9f/2W/ZLBjvrVBLY14oL4b6a2+Ca/JWECd0Rc45KqL89bDvUOVFGrr0D14q5hoEqBXkgxwxL6RQ==", + "license": "MIT", + "peerDependencies": { + "gpu-lexer": ">=0.0.2" + }, + "peerDependenciesMeta": { + "gpu-lexer": { + "optional": true + } + } }, "node_modules/supports-color": { - "version": "8.1.1", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", + "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", "dev": true, "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/chalk/supports-color?sponsor=1" @@ -9395,11 +9414,15 @@ }, "node_modules/symbol-tree": { "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true, "license": "MIT" }, "node_modules/tailwind-merge": { - "version": "3.6.0", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.7.0.tgz", + "integrity": "sha512-XPPUyAc+cvspz3lHTcR/QgPfW2A0lv/xQNIjX3HGhLR+Nq2lHaLq5MtTesHn8GUr3W3DguT2KT5x3NVgRtYwmA==", "license": "MIT", "funding": { "type": "github", @@ -9407,12 +9430,16 @@ } }, "node_modules/tailwindcss": { - "version": "4.3.2", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.3.tgz", + "integrity": "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==", "dev": true, "license": "MIT" }, "node_modules/tapable": { "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", "dev": true, "license": "MIT", "engines": { @@ -9452,6 +9479,8 @@ }, "node_modules/tinybench": { "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", "dev": true, "license": "MIT" }, @@ -9491,23 +9520,29 @@ } }, "node_modules/tldts": { - "version": "7.4.5", + "version": "7.4.14", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.14.tgz", + "integrity": "sha512-EahQoi+Q5oqmG3yxRHx+bwn36OaLbtw6jnTFcGjc8fcmktzTgk45xRnzAx8Ch87PFEiqRhoc3FWBmY+LBrvEGQ==", "dev": true, "license": "MIT", "dependencies": { - "tldts-core": "^7.4.5" + "tldts-core": "^7.4.14" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "7.4.5", + "version": "7.4.14", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.14.tgz", + "integrity": "sha512-KYkjfJHnIC5t+Gy7hPrMHgJmWc/N9FfmS+fggRPSlpFckidpB9HD6OzPsSTkfDH+MpZgcu2tJPGgLfHl979N1Q==", "dev": true, "license": "MIT" }, "node_modules/tough-cookie": { - "version": "6.0.1", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.2.tgz", + "integrity": "sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -10135,6 +10170,8 @@ }, "node_modules/typescript": { "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -10146,16 +10183,70 @@ } }, "node_modules/typescript-eslint": { - "version": "8.66.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.66.0.tgz", - "integrity": "sha512-QlEbBPz/RuJ1XUHj29nm3t0F/O/cSlEnntozqPOYHnnTGAXFamnMBu5i9Vn6vhUPHGAjR+Vl+5J8vPN/BMUrJw==", + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.70.1.tgz", + "integrity": "sha512-AcWG7KDjZ2THNXsgwttMaGmzVi0VFRlFYfqFHYQRbDpF3owuYbuiL8c7UUrd2k8s3PoSfIQrWfrGXfcElrWLYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.70.1", + "@typescript-eslint/parser": "8.70.1", + "@typescript-eslint/typescript-estree": "8.70.1", + "@typescript-eslint/utils": "8.70.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.70.1.tgz", + "integrity": "sha512-nDNrUQ/4ruSNYbu749TRY7cfrzPtoLHEXSNBI8aaNY32LlZCajixqRf3FqcKC4p5Cam4VOHYx/t+i5+nKXvrqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.70.1", + "@typescript-eslint/type-utils": "8.70.1", + "@typescript-eslint/utils": "8.70.1", + "@typescript-eslint/visitor-keys": "8.70.1", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.70.1", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.70.1.tgz", + "integrity": "sha512-7zKTnyvaVWqzLZHPFQtX1hVHqgkMC+WebPWakNCSyrQVbIP1AM0L0TlBZtACldIRb6PptI8Odk+jyZ5kP3B1VA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.66.0", - "@typescript-eslint/parser": "8.66.0", - "@typescript-eslint/typescript-estree": "8.66.0", - "@typescript-eslint/utils": "8.66.0" + "@typescript-eslint/types": "8.70.1", + "@typescript-eslint/typescript-estree": "8.70.1", + "@typescript-eslint/utils": "8.70.1", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -10169,6 +10260,132 @@ "typescript": ">=4.8.4 <6.1.0" } }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.70.1.tgz", + "integrity": "sha512-nO974WLllwhSFWQXnMLj6nDGa8f0khKEz1JzpPJ1u7Vm/4X1X6ZHajpoknU4bb41vJyMB0HHVyS2GqdhWfIXZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.70.1", + "@typescript-eslint/types": "8.70.1", + "@typescript-eslint/typescript-estree": "8.70.1", + "@typescript-eslint/visitor-keys": "8.70.1", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.70.1.tgz", + "integrity": "sha512-TU8PwyGN0PQJUcE96mw8eCQ44SmxGdQlJmlWakHaHQ15eIuuvye5yNtmh/i6oS88jzXVQB71xdNkbkB/fMwL0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.70.1", + "@typescript-eslint/tsconfig-utils": "8.70.1", + "@typescript-eslint/types": "8.70.1", + "@typescript-eslint/visitor-keys": "8.70.1", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/project-service": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.70.1.tgz", + "integrity": "sha512-62xOgboPfwc3/IgPSX/W6oQR3ZbF04194FPGUGH8HL8iLFHbt/456/8Ph1wLNUgVF+s94FlHoipBsz+v7+LMnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.70.1", + "@typescript-eslint/types": "^8.70.1", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.70.1.tgz", + "integrity": "sha512-jumze1fPI+sDOaM2TWGQdn39PDxTr7TZGeuyLkAbNyx2vtMT3uRnVKChN0hfht5V2TugphJzF6bYXvBcE09qqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": { + "version": "8.70.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.70.1.tgz", + "integrity": "sha512-Esgul8MsnKnRLdYU2Eb2cRV9bS5HJYtKj1ByJnOzzG2M58DGdSUQ1jUuILxipqcpB2h9WLrbD5GijIWUjX/Tqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.70.1", + "@typescript-eslint/types": "8.70.1", + "@typescript-eslint/typescript-estree": "8.70.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/typescript-eslint/node_modules/ignore": { + "version": "7.0.10", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.10.tgz", + "integrity": "sha512-HpbUakT7xp5miBUywCHf36ZEuAJNklBJDDsGpUIjMzOSmM8ELSfA9Sa/QDPeNeqeoN31u+UTCkL4klCOVvRm4Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/ufo": { "version": "1.6.4", "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", @@ -10188,11 +10405,15 @@ }, "node_modules/undici-types": { "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", "dev": true, "license": "MIT" }, "node_modules/unenv": { "version": "2.0.0-rc.24", + "resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.24.tgz", + "integrity": "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==", "dev": true, "license": "MIT", "dependencies": { @@ -10437,14 +10658,16 @@ } }, "node_modules/vite": { - "version": "8.1.0", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.0.tgz", + "integrity": "sha512-lhZBVvEHefgE+HQZC9O7EBJgCU/nVzFNl7vkS4RE0APtWLP02/8QVIkQtzBxPquh7lq5/78NHipTj7ODQ6XuyQ==", "dev": true, "license": "MIT", "dependencies": { - "lightningcss": "^1.32.0", - "picomatch": "^4.0.4", - "postcss": "^8.5.15", - "rolldown": "~1.1.2", + "lightningcss": "^1.33.0", + "picomatch": "^4.0.7", + "postcss": "^8.5.28", + "rolldown": "~1.2.6", "tinyglobby": "^0.2.17" }, "bin": { @@ -10461,7 +10684,7 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.3.0", + "@vitejs/devtools": "^0.7.1", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", @@ -10512,6 +10735,279 @@ } } }, + "node_modules/vite/node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/vite/node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/vitest": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.11.tgz", @@ -10604,6 +11100,8 @@ }, "node_modules/w3c-xmlserializer": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", "dev": true, "license": "MIT", "dependencies": { @@ -10692,9 +11190,9 @@ } }, "node_modules/workerd": { - "version": "1.20260918.1", - "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20260918.1.tgz", - "integrity": "sha512-NsjfQlBNQ0iEniv/STOy4zbp8s5k60PzL1Ter02Eg44arbDhHtf6UOs03E31XDRmmBFxSIkAZuCyld3RKH1wqA==", + "version": "1.20260921.1", + "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20260921.1.tgz", + "integrity": "sha512-4HyG7G1W4ksa6tUZ8bV2jxDRWuL5PXnHm9+Z1sjFPb9OZNoYtXz4y7QQRh4ibi0BF/lOmlAVjbhkUqsAVZuUKA==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -10705,28 +11203,28 @@ "node": ">=16" }, "optionalDependencies": { - "@cloudflare/workerd-darwin-64": "1.20260918.1", - "@cloudflare/workerd-darwin-arm64": "1.20260918.1", - "@cloudflare/workerd-linux-64": "1.20260918.1", - "@cloudflare/workerd-linux-arm64": "1.20260918.1", - "@cloudflare/workerd-windows-64": "1.20260918.1" + "@cloudflare/workerd-darwin-64": "1.20260921.1", + "@cloudflare/workerd-darwin-arm64": "1.20260921.1", + "@cloudflare/workerd-linux-64": "1.20260921.1", + "@cloudflare/workerd-linux-arm64": "1.20260921.1", + "@cloudflare/workerd-windows-64": "1.20260921.1" } }, "node_modules/wrangler": { - "version": "4.135.0", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.135.0.tgz", - "integrity": "sha512-WrNBQSfIG6YcILJcodYr5ty8vgkzGsV8YX+kfd+uZ5/Bd8cUW0GnUIRKAVfn5kYKqh1m/BPcji9h1d7mE8euFw==", + "version": "4.136.3", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.136.3.tgz", + "integrity": "sha512-L1bS8BI9xoEk3RRr5XoZn5q5k1jCRo8+37ZEEab/jsUFzE1Ea2sIBAbmP1CdwSeD6mZYmnUtR6xpi9esHIl6GA==", "dev": true, "license": "MIT OR Apache-2.0", "dependencies": { "@cloudflare/kv-asset-handler": "0.5.0", - "@cloudflare/unenv-preset": "2.16.1", + "@cloudflare/unenv-preset": "2.16.2", "blake3-wasm": "2.1.5", "esbuild": "0.28.1", - "miniflare": "5.20260918.0-alpha", + "miniflare": "5.20260921.0-alpha", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", - "workerd": "1.20260918.1" + "workerd": "1.20260921.1" }, "bin": { "cf-wrangler": "bin/cf-wrangler.js", @@ -10740,7 +11238,7 @@ "fsevents": "2.3.3" }, "peerDependencies": { - "@cloudflare/workers-types": "^5.20260918.1" + "@cloudflare/workers-types": "^5.20260921.1" }, "peerDependenciesMeta": { "@cloudflare/workers-types": { @@ -10826,6 +11324,8 @@ }, "node_modules/xml-name-validator": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", "dev": true, "license": "Apache-2.0", "engines": { @@ -10854,6 +11354,8 @@ }, "node_modules/yargs": { "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "license": "MIT", "dependencies": { @@ -10871,6 +11373,8 @@ }, "node_modules/yargs-parser": { "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, "license": "ISC", "engines": { @@ -10879,6 +11383,8 @@ }, "node_modules/yargs/node_modules/string-width": { "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "license": "MIT", "dependencies": { @@ -10892,6 +11398,8 @@ }, "node_modules/yargs/node_modules/strip-ansi": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "license": "MIT", "dependencies": { @@ -10951,7 +11459,9 @@ } }, "node_modules/zod": { - "version": "4.4.3", + "version": "4.6.5", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.5.tgz", + "integrity": "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -10988,18 +11498,18 @@ "@codraoss/models": "*", "@codraoss/provider-github": "*", "@codraoss/schema": "*", - "hono": "^4.12.25", + "hono": "^4.12.34", "zod": "^4.3.6" }, "devDependencies": { - "@types/node": "^22.0.0", - "tsup": "^8.0.0" + "@types/node": "^22.0.3", + "tsup": "^8.0.2" } }, "packages/api/node_modules/@types/node": { - "version": "22.20.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.1.tgz", - "integrity": "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==", + "version": "22.20.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.4.tgz", + "integrity": "sha512-zJRE40jpHtKqE/C4fgHrAKQLJuSpzEnP9ff9Y7YtoR3Wd2pwqzlekDeEuUQXjRd+QCYnVnNwuJYmhdk9XV8gvA==", "dev": true, "license": "MIT", "dependencies": { @@ -11020,12 +11530,12 @@ "dependencies": { "@codraoss/schema": "*", "jsonrepair": "^3.15.0", - "picomatch": "^4.0.5", + "picomatch": "^4.0.7", "zod": "^4.3.6" }, "devDependencies": { "@types/picomatch": "^4.0.3", - "tsup": "^8.0.0" + "tsup": "^8.0.2" } }, "packages/db": { @@ -11038,14 +11548,14 @@ "postgres": "^3.4.9" }, "devDependencies": { - "@types/node": "^22.0.0", - "tsup": "^8.0.0" + "@types/node": "^22.0.3", + "tsup": "^8.0.2" } }, "packages/db/node_modules/@types/node": { - "version": "22.20.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.1.tgz", - "integrity": "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==", + "version": "22.20.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.4.tgz", + "integrity": "sha512-zJRE40jpHtKqE/C4fgHrAKQLJuSpzEnP9ff9Y7YtoR3Wd2pwqzlekDeEuUQXjRd+QCYnVnNwuJYmhdk9XV8gvA==", "dev": true, "license": "MIT", "dependencies": { @@ -11068,7 +11578,7 @@ "@codraoss/schema": "*" }, "devDependencies": { - "tsup": "^8.0.0" + "tsup": "^8.0.2" } }, "packages/node-adapters": { @@ -11082,11 +11592,11 @@ "@codraoss/schema": "*", "bullmq": "^6.3.8", "ioredis": "^6.0.0", - "nanoid": "^5.0.7" + "nanoid": "^5.0.9" }, "devDependencies": { - "@types/node": "^22.0.0", - "tsup": "^8.0.0" + "@types/node": "^22.0.3", + "tsup": "^8.0.2" } }, "packages/node-adapters/node_modules/@types/node": { @@ -11133,7 +11643,7 @@ "@codraoss/schema": "*" }, "devDependencies": { - "tsup": "^8.0.0" + "tsup": "^8.0.2" } }, "packages/schema": { @@ -11144,7 +11654,7 @@ "zod": "^4.3.6" }, "devDependencies": { - "tsup": "^8.0.0" + "tsup": "^8.0.2" } }, "packages/ui": { @@ -11156,11 +11666,11 @@ "@codraoss/schema": "*", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", - "sugar-high": "^2.0.0", + "sugar-high": "^2.0.3", "tailwind-merge": "^3.5.0" }, "devDependencies": { - "tsup": "^8.0.0" + "tsup": "^8.0.2" }, "peerDependencies": { "lenis": ">=1.0.0", diff --git a/package.json b/package.json index d89faa79..9da8f25c 100644 --- a/package.json +++ b/package.json @@ -44,12 +44,12 @@ "devDependencies": { "@arethetypeswrong/cli": "^0.18.5", "@eslint/js": "^10.0.1", - "@tailwindcss/vite": "^4.2.2", - "@testing-library/dom": "^10.4.1", - "@testing-library/react": "^16.3.2", - "@types/node": "^25.6.0", + "@tailwindcss/vite": "^4.2.4", + "@testing-library/dom": "^10.4.2", + "@testing-library/react": "^16.3.3", + "@types/node": "^25.6.2", "@types/react": "^19.2.18", - "@types/react-dom": "^19.2.4", + "@types/react-dom": "^19.2.7", "@vitejs/plugin-react": "^6.0.5", "chalk": "^5.6.2", "concurrently": "^9.2.4", @@ -60,13 +60,13 @@ "jsdom": "^29.0.2", "ora": "^9.4.1", "prompts": "^2.4.2", - "publint": "^0.3.23", - "tailwindcss": "^4.2.2", + "publint": "^0.3.24", + "tailwindcss": "^4.2.4", "tsup": "^8.5.1", - "typescript": "^6.0.2", + "typescript": "^6.0.3", "typescript-eslint": "^8.66.0", - "vite": "^8.0.8", - "vitest": "^4.1.10", + "vite": "^8.0.16", + "vitest": "^4.1.11", "wrangler": "^4.114.0" }, "dependencies": { @@ -80,7 +80,7 @@ "@codraoss/ui": "*", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", - "hono": "^4.12.25", + "hono": "^4.12.34", "jsonrepair": "^3.15.0", "lenis": "^1.3.26", "lucide-react": "^1.8.0", @@ -90,19 +90,20 @@ "react": "^19.2.8", "react-dom": "^19.2.8", "react-markdown": "^10.1.0", - "react-router-dom": "^7.18.2", + "react-router-dom": "^7.18.4", "recharts": "^3.8.1", "rehype-raw": "^7.0.0", "rehype-sanitize": "^6.0.0", "remark-gfm": "^4.0.1", - "sonner": "^2.0.7", - "sugar-high": "^2.0.0", + "sonner": "^2.0.8", + "sugar-high": "^2.0.3", "tailwind-merge": "^3.5.0", "zod": "^4.3.6" }, "allowScripts": { "esbuild": true, "unrs-resolver@1.12.2": true, - "workerd@1.20260918.1": true + "workerd@1.20260918.1": true, + "msgpackr-extract@3.0.4": true } } diff --git a/packages/api/package.json b/packages/api/package.json index 474f59c2..718e6efb 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -44,7 +44,7 @@ "postpack": "node ../../scripts/swap-publish-exports.mjs restore" }, "dependencies": { - "hono": "^4.12.25", + "hono": "^4.12.34", "zod": "^4.3.6", "@codraoss/schema": "*", "@codraoss/core": "*", @@ -53,8 +53,8 @@ "@codraoss/models": "*" }, "devDependencies": { - "@types/node": "^22.0.0", - "tsup": "^8.0.0" + "@types/node": "^22.0.3", + "tsup": "^8.0.2" }, "main": "./dist/index.js", "module": "./dist/index.js", diff --git a/packages/core/package.json b/packages/core/package.json index 69d26fe7..8b6bb82a 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -125,11 +125,11 @@ "dependencies": { "@codraoss/schema": "*", "jsonrepair": "^3.15.0", - "picomatch": "^4.0.5", + "picomatch": "^4.0.7", "zod": "^4.3.6" }, "devDependencies": { "@types/picomatch": "^4.0.3", - "tsup": "^8.0.0" + "tsup": "^8.0.2" } } diff --git a/packages/db/package.json b/packages/db/package.json index 7ffc05e4..46bb56b0 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -72,7 +72,7 @@ "postgres": "^3.4.9" }, "devDependencies": { - "@types/node": "^22.0.0", - "tsup": "^8.0.0" + "@types/node": "^22.0.3", + "tsup": "^8.0.2" } } diff --git a/packages/models/package.json b/packages/models/package.json index 0a494564..ca597e43 100644 --- a/packages/models/package.json +++ b/packages/models/package.json @@ -82,6 +82,6 @@ "@codraoss/core": "*" }, "devDependencies": { - "tsup": "^8.0.0" + "tsup": "^8.0.2" } } diff --git a/packages/node-adapters/package.json b/packages/node-adapters/package.json index 9041ab33..55a02800 100644 --- a/packages/node-adapters/package.json +++ b/packages/node-adapters/package.json @@ -49,10 +49,10 @@ "@codraoss/schema": "*", "bullmq": "^6.3.8", "ioredis": "^6.0.0", - "nanoid": "^5.0.7" + "nanoid": "^5.0.9" }, "devDependencies": { - "@types/node": "^22.0.0", - "tsup": "^8.0.0" + "@types/node": "^22.0.3", + "tsup": "^8.0.2" } } diff --git a/packages/node-adapters/src/worker.ts b/packages/node-adapters/src/worker.ts index dba5151c..6c5789e4 100644 --- a/packages/node-adapters/src/worker.ts +++ b/packages/node-adapters/src/worker.ts @@ -1,8 +1,7 @@ import { Worker, type Job } from 'bullmq'; import { reviewJobMessageSchema, type ReviewJobMessage } from '@codraoss/schema'; import { NodeOrchestrator } from './node-orchestrator'; -import type { ReviewRuntime } from '@codraoss/core/ports'; -import type { QueueProducer } from '@codraoss/core/ports'; +import type { ReviewRuntime, QueueProducer } from '@codraoss/core/ports'; import type Redis from 'ioredis'; export function startWorker( diff --git a/packages/provider-github/package.json b/packages/provider-github/package.json index 2002e889..7413c9d2 100644 --- a/packages/provider-github/package.json +++ b/packages/provider-github/package.json @@ -57,6 +57,6 @@ "@codraoss/schema": "*" }, "devDependencies": { - "tsup": "^8.0.0" + "tsup": "^8.0.2" } } diff --git a/packages/schema/package.json b/packages/schema/package.json index f1ddceae..048e988c 100644 --- a/packages/schema/package.json +++ b/packages/schema/package.json @@ -80,6 +80,6 @@ "zod": "^4.3.6" }, "devDependencies": { - "tsup": "^8.0.0" + "tsup": "^8.0.2" } } diff --git a/packages/ui/package.json b/packages/ui/package.json index 45f03693..67870ca3 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -110,9 +110,9 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "tailwind-merge": "^3.5.0", - "sugar-high": "^2.0.0" + "sugar-high": "^2.0.3" }, "devDependencies": { - "tsup": "^8.0.0" + "tsup": "^8.0.2" } } From e417dd605eeaf7e840e2a2bbed92289013c502a3 Mon Sep 17 00:00:00 2001 From: Devarshi Shimpi Date: Wed, 23 Sep 2026 01:20:39 +0530 Subject: [PATCH 06/16] chore: strip verbose comments and use workspace dependencies --- apps/node/package.json | 2 +- apps/node/src/adapters/telemetry.ts | 3 +- apps/node/src/api-deps.ts | 10 +++-- apps/node/src/env.ts | 6 +-- apps/node/src/index.ts | 22 ++++------ apps/node/src/runtime.ts | 6 +-- apps/worker/package.json | 2 +- apps/worker/src/worker-env.d.ts | 25 ++++++++++- docker-compose.yml | 4 +- package-lock.json | 4 +- package.json | 2 +- packages/api/package.json | 43 +++++++++++-------- packages/core/package.json | 4 +- packages/db/package.json | 6 +-- packages/db/src/client.ts | 4 +- packages/models/package.json | 6 +-- .../models/src/internal/model-chain-runner.ts | 10 ++--- .../models/src/internal/model-rate-limits.ts | 4 +- .../models/src/internal/model-review-chain.ts | 25 ++++++----- .../models/src/internal/model-review-file.ts | 2 +- packages/models/src/internal/model-support.ts | 8 ++-- packages/models/src/providers/cloudflare.ts | 13 +++--- packages/models/src/providers/google.ts | 18 ++++---- packages/models/src/providers/openai.ts | 2 - packages/models/src/providers/vertex.ts | 14 +++--- packages/models/src/runner.ts | 29 +++++-------- packages/models/src/types.ts | 2 +- packages/models/src/url-guard.ts | 14 +++--- packages/node-adapters/package.json | 10 ++--- packages/node-adapters/src/platform.ts | 5 +-- packages/provider-github/package.json | 6 +-- packages/schema/package.json | 2 +- packages/schema/src/api.ts | 8 ---- packages/schema/src/hex.ts | 4 -- packages/schema/src/schema-claims.ts | 5 --- packages/schema/src/schema-enums.ts | 2 - packages/schema/src/schema-repo-config.ts | 7 --- packages/schema/src/schema.ts | 1 - packages/ui/package.json | 4 +- packages/ui/src/components/bar-sparkline.tsx | 10 +---- packages/ui/src/components/button.tsx | 5 --- .../ui/src/components/chart-primitives.tsx | 10 ----- packages/ui/src/components/load-error.tsx | 4 -- .../src/components/motion/smooth-scroll.tsx | 8 ---- .../src/components/motion/stepped-slider.tsx | 16 +------ packages/ui/src/components/motion/tabs.tsx | 8 +--- .../ui/src/components/onboarding-card.tsx | 30 ++++++------- packages/ui/src/components/select-panel.tsx | 6 +-- packages/ui/src/components/select-shared.ts | 5 +-- packages/ui/src/components/select.tsx | 9 ---- packages/ui/src/lib/constants.ts | 4 +- packages/ui/src/lib/prompt-diff.ts | 8 ++-- 52 files changed, 186 insertions(+), 281 deletions(-) diff --git a/apps/node/package.json b/apps/node/package.json index 6a062909..bca66afc 100644 --- a/apps/node/package.json +++ b/apps/node/package.json @@ -1,6 +1,6 @@ { "name": "@codraoss/node-server", - "version": "0.9.12", + "version": "0.9.13", "private": true, "type": "module", "scripts": { diff --git a/apps/node/src/adapters/telemetry.ts b/apps/node/src/adapters/telemetry.ts index 9c1e28c3..1f0b2046 100644 --- a/apps/node/src/adapters/telemetry.ts +++ b/apps/node/src/adapters/telemetry.ts @@ -4,8 +4,7 @@ import type { NodeAppBindings } from '../env'; export function makeTelemetrySink(_env: NodeAppBindings): TelemetrySink { return { send: async (_event) => { - // In a real setup this would batch and send to a telemetry endpoint - // For now, in Node open-source, telemetry can be safely stubbed or sent to logs + // Telemetry stubbed in Node open-source. } }; } diff --git a/apps/node/src/api-deps.ts b/apps/node/src/api-deps.ts index ac0f4f44..6007ca62 100644 --- a/apps/node/src/api-deps.ts +++ b/apps/node/src/api-deps.ts @@ -15,14 +15,18 @@ export function createNodeApiDeps(env: NodeAppBindings) { }, terminateJobWorkflow: async (job) => { if (env.REVIEW_QUEUE.deleteJob) { - await env.REVIEW_QUEUE.deleteJob(job.id); - logger.info(`[API Deps] Terminated job workflow for job ${job.id}`); + try { + await env.REVIEW_QUEUE.deleteJob(job.id); + logger.info(`[API Deps] Terminated job workflow for job ${job.id}`); + } catch (error) { + logger.error(`[API Deps] Failed to terminate job workflow for job ${job.id}: ${error}`); + } } else { logger.warn(`[API Deps] QueueAdapter does not support deleteJob. Cannot terminate ${job.id}`); } }, scheduleBestEffortJobMaintenance: () => { - // In node, this is a long running process, we can just spawn a promise. + }, createReviewRuntime: () => createReviewRuntime(env), getOrFetchRawDiffForCompletedJob: async () => '', diff --git a/apps/node/src/env.ts b/apps/node/src/env.ts index 302cf134..9b975777 100644 --- a/apps/node/src/env.ts +++ b/apps/node/src/env.ts @@ -8,10 +8,10 @@ export interface NodeAppBindings { REVIEW_QUEUE: QueueProducer; REVIEW_ORCHESTRATOR: JobOrchestrator; - // ── Database ── + DATABASE_CONFIG: { connectionString: string }; - // ── Environment strings (loaded from process.env via dotenv) ── + APP_PRIVATE_KEY: string; GITHUB_APP_ID: string; GITHUB_APP_SLUG?: string; @@ -30,7 +30,7 @@ export function createNodeEnv(stubs: { SESSION_STORE: SessionStore; APP_KV: KeyValueStore; REVIEW_QUEUE: QueueProducer; - REVIEW_ORCHESTRATOR?: JobOrchestrator; // Mark as optional + REVIEW_ORCHESTRATOR?: JobOrchestrator; }): NodeAppBindings { const requireEnv = (key: string) => { const val = process.env[key]; diff --git a/apps/node/src/index.ts b/apps/node/src/index.ts index 5649148d..d471cbe7 100644 --- a/apps/node/src/index.ts +++ b/apps/node/src/index.ts @@ -1,7 +1,7 @@ import * as dotenv from 'dotenv'; import * as path from 'node:path'; -dotenv.config({ path: path.resolve(process.cwd(), '../../.dev.vars') }); // Fallback if running from apps/node -dotenv.config({ path: path.resolve(process.cwd(), '.dev.vars') }); // Root level +dotenv.config({ path: path.resolve(process.cwd(), '../../.dev.vars') }); +dotenv.config({ path: path.resolve(process.cwd(), '.dev.vars') }); import { serve } from '@hono/node-server'; import { createApiRouter } from '@codraoss/api'; @@ -25,7 +25,7 @@ const __dirname = dirname(__filename); const redisUrl = process.env.REDIS_URL || 'redis://localhost:6379'; const redisClient = new Redis(redisUrl, { maxRetriesPerRequest: null, - tls: redisUrl.startsWith('rediss://') ? { rejectUnauthorized: false } : undefined, // For managed Redis + tls: redisUrl.startsWith('rediss://') ? { rejectUnauthorized: false } : undefined, // Managed Redis }); redisClient.on('error', (err) => { logger.error('[Redis Error]', err); @@ -50,14 +50,14 @@ app.onError((err, c) => { return c.text('Custom Error: ' + err.message, 500); }); -// Static file serving + const distClientPath = path.resolve(__dirname, process.cwd().endsWith('node/dist') ? '../../dist/client' : '../dist/client'); app.use('/assets/*', serveStatic({ root: distClientPath })); app.use('/*.svg', serveStatic({ root: distClientPath })); app.use('/*.ico', serveStatic({ root: distClientPath })); app.get('*', serveStatic({ root: distClientPath, path: 'index.html' })); -app.get('/health', (c) => c.text('OK')); // Healthcheck endpoint +app.get('/health', (c) => c.text('OK')); const port = parseInt(process.env.PORT || '3000', 10); @@ -74,7 +74,7 @@ if (process.env.START_API !== 'false') { ...env, deps: createNodeApiDeps(env), }; - // Ensure runWithDb receives correct DbEnv type + try { return await runWithDb({ ...env, HYPERDRIVE: env.DATABASE_CONFIG }, () => app.fetch(request, apiEnv as any)); } catch (e) { console.error('SERVE ERROR:', e); throw e; } }, port, @@ -83,7 +83,7 @@ if (process.env.START_API !== 'false') { logger.info(`Codra Node server running on http://localhost:${info.port}`); }); - // Add server to shutdown sequence + if (server) { const originalClose = server.close; server.close = () => originalClose(() => { @@ -95,7 +95,7 @@ if (process.env.START_API !== 'false') { const shutdown = async () => { logger.info('Shutting down Codra Node server...'); - // Close BullMQ worker first to prevent new jobs + // Close BullMQ worker if (worker) { logger.info('Waiting for active BullMQ jobs to finish...'); await worker.close(); @@ -109,11 +109,7 @@ const shutdown = async () => { logger.info('Redis client closed.'); } - // Close Postgres connection pool - // The postgres.js client automatically manages its pool. - // runWithDb client is scoped, but a top-level client for specific operations might exist in the future - // For now, no explicit close needed for `postgres.js` unless it's a global client. - // If `runWithDb` is the only entry point, it manages its own connections. + // Postgres client manages its own pool. logger.info('All services stopped. Exiting.'); process.exit(0); diff --git a/apps/node/src/runtime.ts b/apps/node/src/runtime.ts index 42c829d2..485d1de0 100644 --- a/apps/node/src/runtime.ts +++ b/apps/node/src/runtime.ts @@ -3,13 +3,13 @@ import type { DbEnv } from '@codraoss/db/env'; import { TokenTracker } from '@codraoss/core/token-tracker'; import type { NodeAppBindings } from './env'; -// Adapters from @codraoss/db/repositories + import { makeJobStore, makeFileReviewStore, makeLearningStore, makeModelConfigReader, makeReviewSettingsReader, makeWebhookDeliveryReader } from '@codraoss/db/repositories'; -// Config + import { loadRepoConfig } from '@codraoss/api/platform'; -// Node-specific adapters + import { cryptoIds, systemClock } from '@codraoss/node-adapters'; import { makeTelemetrySink } from './adapters/telemetry'; import { diff --git a/apps/worker/package.json b/apps/worker/package.json index 5f5629db..7bc6ce5a 100644 --- a/apps/worker/package.json +++ b/apps/worker/package.json @@ -1,6 +1,6 @@ { "name": "@codraoss/worker", - "version": "0.9.12", + "version": "0.9.13", "private": true, "type": "module", "dependencies": { diff --git a/apps/worker/src/worker-env.d.ts b/apps/worker/src/worker-env.d.ts index c59da585..ba16eb88 100644 --- a/apps/worker/src/worker-env.d.ts +++ b/apps/worker/src/worker-env.d.ts @@ -1,6 +1,6 @@ /* eslint-disable */ // Generated by Wrangler by running `wrangler types ./src/worker-env.d.ts` (hash: 76d8ec86de20e8aacd6c22180ec1b53d) -// Runtime types generated with workerd@1.20260918.1 2026-04-16 nodejs_compat +// Runtime types generated with workerd@1.20260921.1 2026-04-16 nodejs_compat interface __BaseEnv_Env { APP_KV: KVNamespace; HYPERDRIVE: Hyperdrive; @@ -10674,6 +10674,29 @@ declare abstract class AiGateway { }): Promise; getUrl(provider?: AIGatewayProviders | string): Promise; // eslint-disable-line } +/** A parameter accepted by an Analytics SQL query. */ +type AnalyticsSQLParameter = string | number | boolean | null; +/** An Analytics SQL query and its optional positional or named parameters. */ +interface AnalyticsSQLQuery { + query: string; + params?: readonly AnalyticsSQLParameter[] | Readonly>; +} +/** Execution statistics returned by Analytics SQL. */ +interface AnalyticsSQLStatistics { + elapsed_ms: number; + rows_read: number; + bytes_read: number; +} +/** The rows and execution statistics returned by an Analytics SQL query. */ +interface AnalyticsSQLResult = Record> { + data: T[]; + rows: number; + statistics: AnalyticsSQLStatistics; +} +/** An Analytics SQL binding. */ +interface AnalyticsSQLBinding { + query = Record>(request: AnalyticsSQLQuery): Promise>; +} // Copyright (c) 2022-2025 Cloudflare, Inc. // Licensed under the Apache 2.0 license found in the LICENSE file or at: // https://opensource.org/licenses/Apache-2.0 diff --git a/docker-compose.yml b/docker-compose.yml index 430cb958..27292d61 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,7 +5,7 @@ services: image: postgres:15-alpine environment: POSTGRES_USER: postgres - POSTGRES_PASSWORD: password + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-codra_secure_default_pass} POSTGRES_DB: codra ports: - "5432:5432" @@ -36,7 +36,7 @@ services: ports: - "3000:3000" environment: - - DATABASE_URL=postgres://postgres:password@postgres:5432/codra + - DATABASE_URL=postgres://postgres:${POSTGRES_PASSWORD:-codra_secure_default_pass}@postgres:5432/codra - REDIS_URL=redis://redis:6379 - START_WORKER=true - PORT=3000 diff --git a/package-lock.json b/package-lock.json index 1ad9c43d..deb8f494 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "codra", - "version": "0.9.6", + "version": "0.9.13", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "codra", - "version": "0.9.6", + "version": "0.9.13", "license": "AGPL-3.0-only", "workspaces": [ "packages/*", diff --git a/package.json b/package.json index 9da8f25c..1e688991 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codra", - "version": "0.9.6", + "version": "0.9.13", "description": "Open-source code review engine", "private": true, "author": "Devarshi Shimpi", diff --git a/packages/api/package.json b/packages/api/package.json index 718e6efb..1dd148fa 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -1,6 +1,6 @@ { "name": "@codraoss/api", - "version": "0.9.12", + "version": "0.9.13", "description": "Codra's HTTP surface as a mountable Hono router, wired through ports.", "author": "Devarshi Shimpi", "license": "AGPL-3.0-only", @@ -16,18 +16,9 @@ "type": "module", "sideEffects": false, "exports": { - ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.js" - }, - "./logger": { - "types": "./dist/logger.d.ts", - "import": "./dist/logger.js" - }, - "./platform": { - "types": "./dist/platform/index.d.ts", - "import": "./dist/platform/index.js" - } + ".": "./src/index.ts", + "./logger": "./src/logger.ts", + "./platform": "./src/platform/index.ts" }, "files": [ "dist", @@ -35,7 +26,21 @@ "README.md" ], "publishConfig": { - "access": "public" + "access": "public", + "exports": { + ".": { + "import": "./dist/index.js", + "types": "./dist/index.d.ts" + }, + "./logger": { + "import": "./dist/logger.js", + "types": "./dist/logger.d.ts" + }, + "./platform": { + "import": "./dist/platform/index.js", + "types": "./dist/platform/index.d.ts" + } + } }, "scripts": { "build": "tsup", @@ -46,11 +51,11 @@ "dependencies": { "hono": "^4.12.34", "zod": "^4.3.6", - "@codraoss/schema": "*", - "@codraoss/core": "*", - "@codraoss/db": "*", - "@codraoss/provider-github": "*", - "@codraoss/models": "*" + "@codraoss/schema": "workspace:*", + "@codraoss/core": "workspace:*", + "@codraoss/db": "workspace:*", + "@codraoss/provider-github": "workspace:*", + "@codraoss/models": "workspace:*" }, "devDependencies": { "@types/node": "^22.0.3", diff --git a/packages/core/package.json b/packages/core/package.json index 8b6bb82a..3f872975 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@codraoss/core", - "version": "0.9.12", + "version": "0.9.13", "description": "The Codra review engine: pure, transport- and platform-agnostic code review logic behind ports.", "author": "Devarshi Shimpi", "license": "AGPL-3.0-only", @@ -123,7 +123,7 @@ "postpack": "node ../../scripts/swap-publish-exports.mjs restore" }, "dependencies": { - "@codraoss/schema": "*", + "@codraoss/schema": "workspace:*", "jsonrepair": "^3.15.0", "picomatch": "^4.0.7", "zod": "^4.3.6" diff --git a/packages/db/package.json b/packages/db/package.json index 46bb56b0..6e7125db 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -1,6 +1,6 @@ { "name": "@codraoss/db", - "version": "0.9.12", + "version": "0.9.13", "description": "Codra's Postgres persistence layer behind repository interfaces.", "author": "Devarshi Shimpi", "license": "AGPL-3.0-only", @@ -67,8 +67,8 @@ "postpack": "node ../../scripts/swap-publish-exports.mjs restore" }, "dependencies": { - "@codraoss/schema": "*", - "@codraoss/core": "*", + "@codraoss/schema": "workspace:*", + "@codraoss/core": "workspace:*", "postgres": "^3.4.9" }, "devDependencies": { diff --git a/packages/db/src/client.ts b/packages/db/src/client.ts index a1af66bf..c038f720 100644 --- a/packages/db/src/client.ts +++ b/packages/db/src/client.ts @@ -15,7 +15,7 @@ function createDbClient(env: DbEnv): DbClient { fetch_types: false, prepare: false, onnotice: () => {}, - ssl: env.HYPERDRIVE.connectionString.includes('sslmode=require') ? { rejectUnauthorized: false } : false, + ssl: env.HYPERDRIVE.connectionString.includes('sslmode=require') ? { rejectUnauthorized: process.env.NODE_ENV !== 'production' } : false, }); return { @@ -92,7 +92,7 @@ async function withStaleConnectionRecovery(env: DbEnv, op: (db: DbClient) => try { return await op(getDb(env)); } catch (error) { - if (inScope || !isStaleConnectionError(error)) throw error; + if (inScope || env.workerMode !== false || !isStaleConnectionError(error)) throw error; const connectionString = env.HYPERDRIVE.connectionString; fallbackClients.delete(connectionString); diff --git a/packages/models/package.json b/packages/models/package.json index ca597e43..d3836ded 100644 --- a/packages/models/package.json +++ b/packages/models/package.json @@ -1,6 +1,6 @@ { "name": "@codraoss/models", - "version": "0.9.12", + "version": "0.9.13", "description": "LLM provider adapters and model catalog for the Codra review engine.", "author": "Devarshi Shimpi", "license": "AGPL-3.0-only", @@ -78,8 +78,8 @@ "postpack": "node ../../scripts/swap-publish-exports.mjs restore" }, "dependencies": { - "@codraoss/schema": "*", - "@codraoss/core": "*" + "@codraoss/schema": "workspace:*", + "@codraoss/core": "workspace:*" }, "devDependencies": { "tsup": "^8.0.2" diff --git a/packages/models/src/internal/model-chain-runner.ts b/packages/models/src/internal/model-chain-runner.ts index 707cbc6b..b98736a3 100644 --- a/packages/models/src/internal/model-chain-runner.ts +++ b/packages/models/src/internal/model-chain-runner.ts @@ -16,7 +16,7 @@ import type { ModelInput, ModelResponse } from '../types'; // Import from services/model.ts, not here -- four specs vi.mock that specifier. -// Implementation detail, NOT new public API: kept private on ModelRunner because three specs reach these via `(service as any)`. +// `ModelChainContext` kept private as an implementation detail. export type ModelChainContext = { selectModel(params: { totalLineCount: number; config: RepoConfig }): { primary: string; fallbacks: string[] }; resolveModel(model: string): Promise; @@ -97,16 +97,16 @@ export async function generateSummary(ctx: ModelChainContext, params: { throw lastError; } -// Best-effort: any throw here means "verification unavailable", keeping the pre-verification findings. +// Best-effort: failures retain pre-verification findings. export async function verifyFindings(ctx: ModelChainContext, params: { candidates: VerifyCandidate[]; config: RepoConfig }): Promise { const { primary, fallbacks } = ctx.selectModel({ totalLineCount: 0, config: params.config }); const modelsToTry = [primary, ...fallbacks]; const input: ModelInput = { systemPrompt: VERIFY_SYSTEM_PROMPT, userPrompt: buildVerifyPrompt(params.candidates), - // Must be the verify grammar, not the file-review one -- that schema makes strict decoding unsatisfiable and the pass a silent no-op. + // Must use verify grammar, not file-review. responseSchema: VERIFY_RESPONSE_SCHEMA as unknown as ModelInput['responseSchema'], - // A missing verdict is not a pass; a truncated list would silently withhold findings. + // Truncated list silently withholds findings. truncationIntolerant: true, }; const requestedTimeoutMs = clampTimeoutToChainBudget(verifyTimeoutMs(params.candidates.length)); @@ -117,7 +117,7 @@ export async function verifyFindings(ctx: ModelChainContext, params: { candidate const recordGateWait = (waitedMs: number) => { gateWaitMs += waitedMs; }; for (const [modelIndex, currentModel] of modelsToTry.entries()) { - // Matters more here than in reviewFileChunk: finalize can't hibernate, so a full model chain through an outage can burn the whole invocation budget and fail the job terminally. + // Finalize can't hibernate; prevent full-chain budget burn. if (modelIndex > 0 && ctx.tracker?.isNearLimit()) { logger.warn('Stopping the verification chain; subrequest budget for this invocation is nearly exhausted', { skippedModels: modelsToTry.slice(modelIndex), diff --git a/packages/models/src/internal/model-rate-limits.ts b/packages/models/src/internal/model-rate-limits.ts index 16a5c473..66b28d7d 100644 --- a/packages/models/src/internal/model-rate-limits.ts +++ b/packages/models/src/internal/model-rate-limits.ts @@ -9,12 +9,12 @@ export interface RateLimitPersistence { } export class ModelRateLimitBook { - // Slot held before client timeout starts (Workers: 6 concurrent conns/invocation). + // Slot held before client timeout starts. private readonly callGate = new ModelCallGate(); private readonly modelRateLimits = new Map(); - // Keyed by model, not provider, so an all-Google chain doesn't serialize. + // Keyed by model. private readonly tokenMeteredModels = new Map(); private hydrated: Promise | null = null; diff --git a/packages/models/src/internal/model-review-chain.ts b/packages/models/src/internal/model-review-chain.ts index cc3d6ee7..fe4d9803 100644 --- a/packages/models/src/internal/model-review-chain.ts +++ b/packages/models/src/internal/model-review-chain.ts @@ -15,7 +15,6 @@ import type { ModelChainContext } from './model-chain-runner'; import type { ModelRateLimitBook } from './model-rate-limits'; import type { ModelChainProgressStore } from './model-chain-progress'; -// Past two quota failures per file burns subrequests for nothing. const MAX_QUOTA_FAILURES_PER_FILE = 2; export type ModelReviewContext = ModelChainContext & { @@ -49,8 +48,8 @@ export async function runModelChain(ctx: ModelReviewContext, params: { }); const wholeChain = [primary, ...fallbacks]; - // Resume index is the min across bin members, clamped so it can't empty the chain. - const recorded = await Promise.all(progressLabels.map((key) => ctx.chainProgress.startIndexFor(key))); +// Resume index is min across bin members, clamped to prevent emptying chain. +const recorded = await Promise.all(progressLabels.map((key) => ctx.chainProgress.startIndexFor(key))); const startIndex = Math.min(Math.min(...recorded), Math.max(wholeChain.length - 1, 0)); const modelsToTry = wholeChain.slice(startIndex); if (startIndex > 0) { @@ -134,8 +133,8 @@ export async function runModelChain(ctx: ModelReviewContext, params: { continue; } - // Floor applies even to the primary model, not just fallbacks. - if (ctx.tracker && !ctx.tracker.hasRemainingSubrequests(SUBREQUEST_HEADROOM_FOR_MODEL_CALL)) { +// Floor applies even to primary model, not just fallbacks. +if (ctx.tracker && !ctx.tracker.hasRemainingSubrequests(SUBREQUEST_HEADROOM_FOR_MODEL_CALL)) { logger.warn(`Deferring ${label}: not enough subrequest budget left to commit a prompt`, { subrequests: ctx.tracker.getSubrequestCount(), needed: SUBREQUEST_HEADROOM_FOR_MODEL_CALL, @@ -167,8 +166,8 @@ export async function runModelChain(ctx: ModelReviewContext, params: { ctx.tracker.record(response.modelUsed, response.inputTokens, response.outputTokens); } - // isLastModel uses the absolute chain index so resumed jobs don't accept prematurely. - const parsed = params.parse(response.rawText, { +// `isLastModel` uses the absolute chain index for resumed jobs. +const parsed = params.parse(response.rawText, { isLastModel: startIndex + modelIndex >= wholeChain.length - 1, }); await ctx.chainProgress.noteSuccess(currentModel); @@ -177,8 +176,8 @@ export async function runModelChain(ctx: ModelReviewContext, params: { return { ...response, userPrompt, parsed }; } catch (error) { const isLastModel = startIndex + modelIndex >= wholeChain.length - 1; - // Salvage a truncated partial from the final model rather than fail outright. - const partial = isLastModel ? partialResponseOf(error) : null; +// Salvage truncated partials from the final model. +const partial = isLastModel ? partialResponseOf(error) : null; if (partial) { try { const parsed = params.parse(partial.rawText, { isLastModel: true }); @@ -259,8 +258,8 @@ export async function runModelChain(ctx: ModelReviewContext, params: { retryCause, ); - // Skipped once at chain's end, so a fresh retry starts clean rather than looping. - if (attemptedFailedThrough > 0 && attemptedFailedThrough < wholeChain.length) { +// Skipped at chain end, so a fresh retry starts clean. +if (attemptedFailedThrough > 0 && attemptedFailedThrough < wholeChain.length) { await Promise.all(progressLabels.map((key) => ctx.chainProgress.advance(key, attemptedFailedThrough))); Object.defineProperty(error, 'nextChainIndex', { value: attemptedFailedThrough, configurable: true }); } else { @@ -270,8 +269,8 @@ export async function runModelChain(ctx: ModelReviewContext, params: { } if (!attemptedAnyModel) { - // Unwrap so a permanent operator error isn't hidden behind a transient wrapper. - if (lastError !== undefined) { +// Unwrap permanent operator error. +if (lastError !== undefined) { if (isTransientModelFailure(lastError)) { throw new RetryableModelError(`Every model for ${label} failed to resolve; retrying later.`, lastError); } diff --git a/packages/models/src/internal/model-review-file.ts b/packages/models/src/internal/model-review-file.ts index 78145ec9..decf1e08 100644 --- a/packages/models/src/internal/model-review-file.ts +++ b/packages/models/src/internal/model-review-file.ts @@ -195,7 +195,7 @@ export type BatchReviewOutcome = ModelResponse & { userPrompt: string; }; -// Caller must not record batch.missing as reviewed when fanning out to file rows. +// Do not record batch.missing as reviewed. export async function reviewFiles(ctx: ModelReviewContext, params: { files: readonly FileDiff[]; prTitle: string | null; diff --git a/packages/models/src/internal/model-support.ts b/packages/models/src/internal/model-support.ts index bcb8443e..9117f41a 100644 --- a/packages/models/src/internal/model-support.ts +++ b/packages/models/src/internal/model-support.ts @@ -2,7 +2,7 @@ import { normalizeModelId } from '@codraoss/schema'; import { isTimeoutMessage, matchesAnyTransientSubstring } from '@codraoss/schema/transient-errors'; import { UnparseableModelResponseError } from '../types'; -// Hook for future legacy ID rewrites, applied before resolution. +// Hook for legacy ID rewrites. const MODEL_ALIASES: Record = {}; export function mergeCounts(sources: Array | undefined>): Record { @@ -22,14 +22,14 @@ export function estimatePromptTokens(systemPrompt: string, userPrompt: string): export const PROMPT_FIT_SAFETY_FACTOR = 0.8; -// Floor to reject misparsed request quotas, not real token buckets. +// Floor to reject misparsed request quotas. export const MIN_PLAUSIBLE_TOKEN_BUCKET = 1_000; export function isPlausibleTokenBucket(limitTokens: number | undefined): boolean { return typeof limitTokens === 'number' && limitTokens >= MIN_PLAUSIBLE_TOKEN_BUCKET; } -// Lives here (not with callers) to avoid vi.mock TypeError in specs. +// Extracted to avoid vi.mock TypeError in specs. export function nextChainIndexOf(error: unknown): number | null { const value = (error as { nextChainIndex?: unknown } | null)?.nextChainIndex; return typeof value === 'number' && Number.isInteger(value) && value > 0 ? value : null; @@ -43,7 +43,7 @@ export const MAX_METERED_QUEUE_DEPTH = 2; const QUOTA_VIOLATION_PATTERN = /metric:\s*(\S+?),\s*limit:\s*(\d[\d_,]*)/gi; -// Excludes request-count quotas, which would otherwise disable the model. +// Excludes request-count quotas. const TOKEN_QUOTA_METRIC = /(?:input_token|output_token|token_count|_tokens)/i; export function parseRateLimitFromError(error: unknown): { limitTokens?: number; retryAfterMs?: number } { diff --git a/packages/models/src/providers/cloudflare.ts b/packages/models/src/providers/cloudflare.ts index 48c191ed..889a06e0 100644 --- a/packages/models/src/providers/cloudflare.ts +++ b/packages/models/src/providers/cloudflare.ts @@ -8,11 +8,10 @@ export interface CloudflareAiBinding { run(model: string, args: unknown, options?: unknown): Promise; } -// Reasoning models under strict-JSON can burn the token budget thinking and never emit; fail fast and defer. +// Reasoning models under strict-JSON can burn the token budget thinking and never emit. const CLOUDFLARE_TIMEOUT_MS = MODEL_TIMEOUT_MAX_MS; const CLOUDFLARE_DEFAULT_OUTPUT_TOKENS = OUTPUT_TOKENS_FLOOR; -// Workers AI context windows vary widely by model, so this stays modest next to Gemini's: an over-large -// `max_completion_tokens` is refused by the smaller models rather than clamped. +// Workers AI context windows vary widely, so this stays modest: larger `max_completion_tokens` are clamped. const CLOUDFLARE_MAX_OUTPUT_TOKENS = 16_384; type UnknownRecord = Record; @@ -117,7 +116,7 @@ function extractCloudflareUsage(result: unknown) { }; } -// Grammar comes from the CALLER: hardcoding the file-review schema here once forced the verifier to emit a file-review object, silently defaulting `results` to `[]`. +// Grammar comes from CALLER. function buildCloudflareInferenceRequest(input: ModelInput) { const prompts = jsonOnlyPrompts(input); return { @@ -237,9 +236,9 @@ export async function reviewWithCloudflare( providerName = 'Cloudflare', options?: { timeoutMs?: number }, ): Promise { - // Single attempt: a retry would spend another subrequest on a model that just failed, when the fallback chain is about to try another. - const timeoutMs = options?.timeoutMs ?? CLOUDFLARE_TIMEOUT_MS; - let timer: ReturnType | undefined; +// Single attempt: retries would waste subrequests on a failing model. +const timeoutMs = options?.timeoutMs ?? CLOUDFLARE_TIMEOUT_MS; +let timer: ReturnType | undefined; // Promise.race only stops us awaiting; the binding's abort signal is what actually cancels the still-running subrequest. const controller = new AbortController(); diff --git a/packages/models/src/providers/google.ts b/packages/models/src/providers/google.ts index 0c80b877..240b4e2c 100644 --- a/packages/models/src/providers/google.ts +++ b/packages/models/src/providers/google.ts @@ -14,12 +14,10 @@ import { const GEMINI_TIMEOUT_MS = MODEL_TIMEOUT_MAX_MS; const GEMINI_MAX_RETRIES = 2; const GEMINI_DEFAULT_OUTPUT_TOKENS = OUTPUT_TOKENS_FLOOR; -// 65k leaves room for thinking tokens plus dense multi-file output. const GEMINI_MAX_OUTPUT_TOKENS = 65_536; const GEMINI_MAX_RETRY_DELAY_MS = 5_000; const DEFAULT_GEMINI_BASE_URL = 'https://generativelanguage.googleapis.com/v1beta'; -// 429 handled separately; only retryable if a cool-off is stated. function isRetryableGeminiStatus(status: number) { return status === 408 || status === 500 || status === 502 || status === 503 || status === 504 || status === 524; } @@ -77,8 +75,8 @@ export function classifySchemaRejection(status: number, message: string): 'confi function isRetryableTransportError(error: unknown) { if (!(error instanceof Error)) return false; - // Skip retrying timeouts (caller already grants up to 2m); defer to fallback chain. - if (error.name === 'TimeoutError' || error.message.toLowerCase().includes('timed out')) return false; +// Skip retrying timeouts. +if (error.name === 'TimeoutError' || error.message.toLowerCase().includes('timed out')) return false; if (error.message.includes('fetch failed')) return true; return error instanceof TypeError; } @@ -110,7 +108,7 @@ export async function reviewWithGoogle( let currentCeiling = Math.min(GEMINI_MAX_OUTPUT_TOKENS, answerBudget + thinkingBudget); let ceilingRaised = false; const fail = (error: unknown): never => { - // Confident rejections only: a probe that failed anyway proves nothing, and latching would strip the schema from every later call in the job. A successful probe latches via `degraded` instead. + // Latch confident rejections only. if (schemaRejected && schemaRejectionBranch === 'confident' && typeof error === 'object' && error !== null) { Object.defineProperty(error, 'schemaDropped', { value: true, configurable: true }); } @@ -205,8 +203,8 @@ export async function reviewWithGoogle( continue; } - // Unexplained invalid-argument 400: strip optional features one at a time -- grammar, then thinking budget -- refunding the attempt each time. The latches bound this ladder to two extra probes. - if (response.status === 400 && /invalid argument/i.test(message)) { +// Unexplained invalid-argument 400: strip optional features one at a time (grammar, then thinking budget). +if (response.status === 400 && /invalid argument/i.test(message)) { if (responseJsonSchema && !schemaRejected) { schemaRejected = true; schemaRejectionBranch = 'catchall'; @@ -233,8 +231,8 @@ export async function reviewWithGoogle( const requestedDelayMs = response.status === 429 ? retryAfterDelayMs(response.headers.get('retry-after')) ?? requestedRetryDelayFromBody(message) : null; - // Unstated 429s back off ~60s, making them unretryable here; retry only short, stated cool-offs. - const isRetryable = response.status === 429 + // Retry only short, stated cool-offs. + const isRetryable = response.status === 429 ? requestedDelayMs !== null && requestedDelayMs <= GEMINI_MAX_RETRY_DELAY_MS : isRetryableGeminiStatus(response.status); const retryDelayMs = Math.min( @@ -318,7 +316,7 @@ export async function reviewWithGoogle( return fail(new Error('Gemini returned an empty response.')); } - // Attach partial text so a later fallback model can salvage it. + // Attach partial text for fallback models. if (truncated && input.truncationIntolerant) { const error = new UnparseableModelResponseError(model, 'finishReason=MAX_TOKENS'); attachPartialResponse(error, { diff --git a/packages/models/src/providers/openai.ts b/packages/models/src/providers/openai.ts index c218fa18..1bcf9ea5 100644 --- a/packages/models/src/providers/openai.ts +++ b/packages/models/src/providers/openai.ts @@ -4,8 +4,6 @@ import { ProviderRequestError, providerErrorMessage, jsonOnlyPrompts, type Model import { assertPublicBaseUrl } from '../url-guard'; import { MODEL_TIMEOUT_MAX_MS, resolveOutputTokenCeiling } from '../limits'; -// Fallback when the caller supplies no diff-size-aware budget. Shares the review ceiling so an -// omitting caller can never outlast the chain budget that governs everything else. const OPENAI_TIMEOUT_MS = MODEL_TIMEOUT_MAX_MS; const OPENAI_DEFAULT_OUTPUT_TOKENS = 4096; const OPENAI_MAX_OUTPUT_TOKENS = 16_384; diff --git a/packages/models/src/providers/vertex.ts b/packages/models/src/providers/vertex.ts index 40f1d21c..b732250f 100644 --- a/packages/models/src/providers/vertex.ts +++ b/packages/models/src/providers/vertex.ts @@ -10,22 +10,18 @@ import { resolveOutputTokenCeiling, } from '../limits'; -// Vertex's REST API rejects plain API keys and requires an OAuth2 token via RFC 7523 JWT-bearer grant, so `apiKey` here holds the full service-account JSON key, not a short API key string. +// Vertex API requires OAuth2; apiKey holds service-account JSON. const VERTEX_TIMEOUT_MS = MODEL_TIMEOUT_MAX_MS; const VERTEX_DEFAULT_OUTPUT_TOKENS = OUTPUT_TOKENS_FLOOR; -// Same Gemini models as the Google adapter, so the same ceiling and thinkingConfig (unbounded reasoning -// would otherwise consume the whole ceiling and leave a truncated answer). +// Same Gemini models as the Google adapter, so same ceiling and thinkingConfig applies. const VERTEX_MAX_OUTPUT_TOKENS = 65_536; -// Retries for a 429 only, and only while the caller's own timeout still has room. See the loop below -// for why resending an unchanged request is the correct response to this particular refusal. +// Retries for a 429 only, and only while caller's timeout has room. const VERTEX_QUOTA_RETRIES = 2; const VERTEX_QUOTA_BACKOFF_MS = 4_000; -// Room a resend needs to be worth starting at all; a Vertex 429 itself comes back in ~7s. const VERTEX_MIN_ATTEMPT_MS = 8_000; const OAUTH_TOKEN_URL = 'https://oauth2.googleapis.com/token'; const OAUTH_SCOPE = 'https://www.googleapis.com/auth/cloud-platform'; const ACCESS_TOKEN_LIFETIME_S = 3600; -// Refresh before real expiry so an in-flight review never starts a call with a token that expires mid-request. const TOKEN_REFRESH_MARGIN_MS = 60_000; interface VertexGenerateResponse { @@ -47,7 +43,7 @@ interface CachedToken { expiresAt: number; } -// Per-isolate cache, not per-request: saves a token mint (and a subrequest) on every file review after the first to hit a warm isolate. +// Per-isolate cache. const tokenCache = new Map(); function parseServiceAccountKey(raw: string): ServiceAccountKey { @@ -187,7 +183,7 @@ export async function reviewWithVertex( ], generationConfig: { responseMimeType: 'application/json', - // No `responseJsonSchema`: this adapter cannot drop a schema mid-flight, so a rejection would fail the file outright. + // No `responseJsonSchema`: this adapter cannot drop schema mid-flight. maxOutputTokens: ceiling, ...(includeThinking ? { thinkingConfig: { thinkingBudget } } : {}), // Same models as the Google adapter, so the same value keeps the two paths comparable. diff --git a/packages/models/src/runner.ts b/packages/models/src/runner.ts index a20723d3..d48ace7c 100644 --- a/packages/models/src/runner.ts +++ b/packages/models/src/runner.ts @@ -20,36 +20,29 @@ import { ModelRateLimitBook } from './internal/model-rate-limits'; import { ModelChainProgressStore } from './internal/model-chain-progress'; import { type ModelChainContext, generateSummary, verifyFindings } from './internal/model-chain-runner'; import { type ModelReviewContext, reviewFile, reviewFiles } from './internal/model-review-file'; -// Re-exported so test doubles can be typed against the real shape. export type { BatchReviewOutcome } from './internal/model-review-file'; import { pollReviewBatch, submitReviewBatch } from './internal/model-review-batch'; -// Re-exported: core/review.ts and two specs import these. export { RetryableModelError, isRetryableModelError, nextChainIndexOf } from './internal/model-support'; - export { PROMPT_FIT_SAFETY_FACTOR, estimatePromptTokens } from './internal/model-support'; -// Re-exported to avoid a sibling import (no-restricted-imports). export { ModelChainProgressStore } from './internal/model-chain-progress'; export { isPlausibleTokenBucket, parseRateLimitFromError } from './internal/model-support'; const PROVIDER_UNAVAILABLE_TTL_SECONDS = 24 * 60 * 60; export class ModelRunner { - // Caches the in-flight promise so concurrent calls for a model share one request. + // Caches in-flight config requests. private readonly resolvedModelCache = new Map>(); - // Keyed by model, not provider; backed by chainProgress so cool-offs persist across invocations. + // Backed by chainProgress to persist across invocations. private readonly rateLimits: ModelRateLimitBook; - // KV can't flip unavailable to available within one invocation, so cache per instance. + // Cached per instance. private readonly providerUnavailableCache = new Map>(); - // Confirmed unsupported for async batching this invocation; skip re-probing. private readonly asyncUnsupportedModels = new Set(); - - // Same, for constrained decoding, keyed by grammar so one refusal doesn't disable others. private readonly schemaUnsupportedModels = new Set(); - // Per-file progress so a deferral resumes instead of replaying. + // Per-file progress to resume defers. private readonly chainProgress: ModelChainProgressStore; constructor( @@ -149,10 +142,10 @@ export class ModelRunner { const normalized = normalizeModel(model); let pending = this.resolvedModelCache.get(normalized); if (!pending) { - // Cache the null "not configured" result too, so it isn't requeried. + // Cache null to prevent requery. pending = this.deps.getConfig(normalized); this.resolvedModelCache.set(normalized, pending); - // Drop cache entry on error so the next call retries. + // Drop cache on error so next call retries. pending.catch(() => this.resolvedModelCache.delete(normalized)); } const resolved = await pending; @@ -178,10 +171,10 @@ export class ModelRunner { config: ResolvedModelConfig, input: ModelInput, timeoutMs?: number, - // Excludes queue wait from the caller's timing budget. + // Excludes queue wait from caller's budget. onGateWait?: (waitedMs: number) => void, ): Promise { - // Resolve credentials before the gate slot so slow work doesn't hold one. + // Resolve credentials before gate so slow work doesn't hold it. if (config.apiFormat === 'cloudflare-workers-ai') { if (!this.deps.aiBinding) { throw new Error(`Provider ${config.providerName} requires a Cloudflare AI binding, but none was provided.`); @@ -197,7 +190,7 @@ export class ModelRunner { let response: ModelResponse; try { response = await this.rateLimits.runGated(config, onGateWait, () => { - // Read inside the gate: a hoisted read would race the opening wave. + // Read inside gate to prevent race conditions. const gatedInput = this.schemaUnsupportedModels.has(schemaKey) ? { ...input, responseSchema: undefined } : input; @@ -209,7 +202,7 @@ export class ModelRunner { ); }); } catch (error) { - // Latch failure too, so a schema-dropped retry doesn't repay the 400 and full prompt. + // Latch failure to avoid repeated 400s. if (isSchemaDroppedError(error)) this.schemaUnsupportedModels.add(schemaKey); throw error; } @@ -277,7 +270,7 @@ export class ModelRunner { return reviewFile(this.reviewCtx(), params); } - // batch.missing files must not be recorded as reviewed. + // Do not record batch.missing files. async reviewFiles(params: Parameters[1]) { return reviewFiles(this.reviewCtx(), params); } diff --git a/packages/models/src/types.ts b/packages/models/src/types.ts index 646622c9..33258b37 100644 --- a/packages/models/src/types.ts +++ b/packages/models/src/types.ts @@ -1,4 +1,4 @@ -// Re-exported from @codraoss/core/ports so existing @codraoss/models/types imports keep working. +// Re-export for compatibility. import type { ModelResponse as ModelResponseShape, ModelResponseSchema } from '@codraoss/core/ports'; export type { ModelResponse, ModelResponseSchema } from '@codraoss/core/ports'; diff --git a/packages/models/src/url-guard.ts b/packages/models/src/url-guard.ts index 29eb29f9..cd02bd92 100644 --- a/packages/models/src/url-guard.ts +++ b/packages/models/src/url-guard.ts @@ -1,5 +1,4 @@ -// SSRF guard for operator-supplied provider base URLs: without it, a `baseUrl` from the dashboard could point an adapter at the Worker's own network or a cloud metadata endpoint. -// Lives in one module because a per-adapter copy-paste version had already failed once (Anthropic fetched `config.baseUrl` unchecked); every adapter must call `assertPublicBaseUrl`. +// SSRF guard for custom provider base URLs. import { ProviderRequestError } from './types'; const PRIVATE_HOST_PATTERNS = [ @@ -9,11 +8,11 @@ const PRIVATE_HOST_PATTERNS = [ /^192\.168\./, /^169\.254\./, /^localhost$/i, - // IPv6: URL.hostname returns the literal WITH brackets ("[::1]"), so `isPrivateHost` strips them before testing. - /^::1?$/, // loopback and unspecified - /^f[cd][0-9a-f]{2}:/i, // fc00::/7 unique-local - /^fe[89ab][0-9a-f]:/i, // fe80::/10 link-local - /^::ffff:/i, // IPv4-mapped, e.g. ::ffff:127.0.0.1 + // IPv6 loopback, local, IPv4-mapped + /^::1?$/, + /^f[cd][0-9a-f]{2}:/i, + /^fe[89ab][0-9a-f]:/i, + /^::ffff:/i, ]; // Cloud instance-metadata endpoints, which are public-looking but reachable only from inside. @@ -35,7 +34,6 @@ export function isValidPublicUrl(urlString: string) { } } -// Throws the provider-shaped 400 every adapter already raises, so call sites stay one line. export function assertPublicBaseUrl(baseUrl: string | null | undefined, providerName: string) { if (baseUrl && !isValidPublicUrl(baseUrl)) { throw new ProviderRequestError(providerName, 400, 'Invalid provider base URL.'); diff --git a/packages/node-adapters/package.json b/packages/node-adapters/package.json index 55a02800..74378638 100644 --- a/packages/node-adapters/package.json +++ b/packages/node-adapters/package.json @@ -1,6 +1,6 @@ { "name": "@codraoss/node-adapters", - "version": "0.9.12", + "version": "0.9.13", "description": "Node.js platform adapters for Codra, including BullMQ, Redis, and Stateless Orchestration.", "type": "module", "main": "./src/index.ts", @@ -43,10 +43,10 @@ "typecheck": "tsc -p tsconfig.json" }, "dependencies": { - "@codraoss/api": "*", - "@codraoss/core": "*", - "@codraoss/db": "*", - "@codraoss/schema": "*", + "@codraoss/api": "workspace:*", + "@codraoss/core": "workspace:*", + "@codraoss/db": "workspace:*", + "@codraoss/schema": "workspace:*", "bullmq": "^6.3.8", "ioredis": "^6.0.0", "nanoid": "^5.0.9" diff --git a/packages/node-adapters/src/platform.ts b/packages/node-adapters/src/platform.ts index da811954..e5bdd696 100644 --- a/packages/node-adapters/src/platform.ts +++ b/packages/node-adapters/src/platform.ts @@ -1,10 +1,7 @@ import type { Clock, IdGenerator, KvStore } from '@codraoss/core/ports'; export function makeKvStore(appKv: KvStore): KvStore { - return { - get: (key) => appKv.get(key), - put: (key, value, options) => appKv.put(key, value, options), - }; + return appKv; } export const systemClock: Clock = { now: () => Date.now() }; diff --git a/packages/provider-github/package.json b/packages/provider-github/package.json index 7413c9d2..87b204a2 100644 --- a/packages/provider-github/package.json +++ b/packages/provider-github/package.json @@ -1,6 +1,6 @@ { "name": "@codraoss/provider-github", - "version": "0.9.12", + "version": "0.9.13", "description": "GitHub git-provider adapter for Codra: App auth, webhooks, OAuth, and review posting.", "author": "Devarshi Shimpi", "license": "AGPL-3.0-only", @@ -53,8 +53,8 @@ "postpack": "node ../../scripts/swap-publish-exports.mjs restore" }, "dependencies": { - "@codraoss/core": "*", - "@codraoss/schema": "*" + "@codraoss/core": "workspace:*", + "@codraoss/schema": "workspace:*" }, "devDependencies": { "tsup": "^8.0.2" diff --git a/packages/schema/package.json b/packages/schema/package.json index 048e988c..a0408012 100644 --- a/packages/schema/package.json +++ b/packages/schema/package.json @@ -1,6 +1,6 @@ { "name": "@codraoss/schema", - "version": "0.9.12", + "version": "0.9.13", "description": "Shared types and Zod contracts for Codra, the open-source code review engine.", "author": "Devarshi Shimpi", "license": "AGPL-3.0-only", diff --git a/packages/schema/src/api.ts b/packages/schema/src/api.ts index 10501047..8a26e71c 100644 --- a/packages/schema/src/api.ts +++ b/packages/schema/src/api.ts @@ -43,24 +43,19 @@ export const apiActions = [ export type KnownApiAction = (typeof apiActions)[number]; -// Open union: consumers can add their own action names while the known list keeps autocomplete. export type ApiAction = KnownApiAction | (string & {}); export type AuthSessionResponse = { user: AuthSessionUser; - // Omitted, or a '*' entry, means "allow everything". permissions?: string[]; }; -// Durable account record persisted in Postgres (account_settings). export type AccountSettings = { - // Stable, unique account id (uuid) - distinct from the GitHub user id. id: string; githubUserId: number; githubUsername: string; accountName: string | null; accountEmail: string | null; - // Presentation only (timestamps are always stored UTC); `null` means "follow the viewer's browser timezone". timezone: string | null; }; @@ -80,9 +75,6 @@ export type JobDetailResponse = { job: JobDetail; }; -/** Per-file reconstructed diff/prompt text, fetched on demand (see GET /api/jobs/:id/diffs) -- - diff_input isn't persisted in Postgres, so this comes from KV or a fresh GitHub fetch. Files - with no entry are unavailable (e.g. the underlying commits are gone). */ export type JobDiffsResponse = { diffs: Record; }; diff --git a/packages/schema/src/hex.ts b/packages/schema/src/hex.ts index f2de2e1c..e95e017f 100644 --- a/packages/schema/src/hex.ts +++ b/packages/schema/src/hex.ts @@ -1,6 +1,3 @@ -// Hex helpers, shared so the webhook verifier, the job store and the token minters agree. - -// Trims first: leading/trailing whitespace on a header-supplied value would shift every byte since length drives the slice offsets. export function hexToBytes(hex: string) { const clean = hex.trim().toLowerCase(); const bytes = new Uint8Array(clean.length / 2); @@ -12,7 +9,6 @@ export function hexToBytes(hex: string) { return bytes; } -// Cryptographically random hex, for OAuth state and session tokens. export function randomHex(size = 32) { const bytes = crypto.getRandomValues(new Uint8Array(size)); return Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0')).join(''); diff --git a/packages/schema/src/schema-claims.ts b/packages/schema/src/schema-claims.ts index 8a511ecf..9f74b905 100644 --- a/packages/schema/src/schema-claims.ts +++ b/packages/schema/src/schema-claims.ts @@ -1,6 +1,5 @@ import { reviewCategories } from './schema-enums'; -// Enforced, not just labelled; drops whole types and filters candidates. export const claimTypes = [ 'react_hook_missing_deps', 'react_missing_cleanup', @@ -47,7 +46,6 @@ export function toClaimType(value: unknown): ClaimType { return (claimTypes as readonly string[]).includes(value as string) ? (value as ClaimType) : 'other'; } -// Decidable from diff hunk alone? A Record, so new types are COMPILE ERRORS until classified. export const CLAIM_TYPE_DECIDABILITY: Record = { sql_injection: 'diff_local', unsafe_dom_sink: 'diff_local', @@ -71,12 +69,10 @@ export const CLAIM_TYPE_DECIDABILITY: Record CLAIM_TYPE_DECIDABILITY[type] !== 'diff_local', ); -// Generate candidates, never post. export const DEFAULT_SHADOW_RULE_IDS = [ 'empty-catch', 'debugger-statement', @@ -89,7 +85,6 @@ export const DEFAULT_SHADOW_RULE_IDS = [ 'insecure-random', ] as const; -// How finding ended its life; READ-VALIDATING. Retire by marking historical. export const findingDispositions = [ 'posted', 'severity', diff --git a/packages/schema/src/schema-enums.ts b/packages/schema/src/schema-enums.ts index 24797c47..bd9694f5 100644 --- a/packages/schema/src/schema-enums.ts +++ b/packages/schema/src/schema-enums.ts @@ -1,5 +1,3 @@ -// Leaf module: plain string-literal arrays shared by schema.ts, schema-claims.ts, and -// schema-repo-config.ts. Kept dependency-free so none of those three risk an import cycle. export const reviewTriggers = ['auto', 'mention', 'retry'] as const; export const jobStatuses = ['queued', 'running', 'done', 'failed', 'superseded', 'cancelled', 'stopped'] as const; export const fileStatuses = ['pending', 'done', 'skipped', 'failed'] as const; diff --git a/packages/schema/src/schema-repo-config.ts b/packages/schema/src/schema-repo-config.ts index b8964aee..ccc4c424 100644 --- a/packages/schema/src/schema-repo-config.ts +++ b/packages/schema/src/schema-repo-config.ts @@ -31,14 +31,8 @@ export const reviewConfigSchema = z.object({ large_file_threshold_lines: z.number().int().min(1).max(5_000).default(200), max_diff_lines_per_file: z.number().int().min(1).max(5_000).default(800), batch_small_files: z.boolean().default(true), - // Presentation cap: comments actually posted to the PR. Findings past this are still recorded - // (disposition 'cap') and shown on the dashboard; nothing upstream of posting should read it. max_comments: z.number().int().min(1).max(150).default(10), - // How many findings the pipeline works with (generator, verifier, dashboard); independent of - // max_comments so tightening the posted cap no longer quietly shrinks the review itself. review_breadth: z.number().int().min(1).max(150).default(25), - // Off by default: adds a large single file's post-change content as read-only context, costing - // one extra GitHub subrequest per qualifying file (see FILE_CONTEXT_CHAR_BUDGET). full_file_context: z.boolean().default(false), min_severity: z.enum(reviewSeverities).default('P3'), language_gates: z @@ -98,7 +92,6 @@ export const repoConfigSchema = z.object({ ) .nullable() .optional(), - // Findings are UNIONED with the primary's, never voted on (F1 0.200 vs 0.149 best-single; agreement is not evidence). Never pair across a capability gap: strong + much weaker measured BELOW strong alone. secondary: z .object({ model: z.string(), diff --git a/packages/schema/src/schema.ts b/packages/schema/src/schema.ts index 4388f9f1..a920905d 100644 --- a/packages/schema/src/schema.ts +++ b/packages/schema/src/schema.ts @@ -44,7 +44,6 @@ import { REPO_CONFIG_CACHE_VERSION, } from './constants'; -// Re-exported for server use; client imports directly to keep zod out of browser bundle. export { reviewSeverities, reviewConcurrencyLevels, diff --git a/packages/ui/package.json b/packages/ui/package.json index 67870ca3..35c3e849 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@codraoss/ui", - "version": "0.9.12", + "version": "0.9.13", "description": "Codra's reusable React design-system primitives and hooks.", "author": "Devarshi Shimpi", "license": "AGPL-3.0-only", @@ -105,7 +105,7 @@ "sonner": ">=2.0.0" }, "dependencies": { - "@codraoss/schema": "*", + "@codraoss/schema": "workspace:*", "@base-ui/react": "^1.6.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", diff --git a/packages/ui/src/components/bar-sparkline.tsx b/packages/ui/src/components/bar-sparkline.tsx index bd705480..add399c1 100644 --- a/packages/ui/src/components/bar-sparkline.tsx +++ b/packages/ui/src/components/bar-sparkline.tsx @@ -2,19 +2,13 @@ import { useMemo } from 'react'; import { cn } from '../lib/utils'; interface BarSparklineProps { - /** Raw daily series; aggregated into `bars` buckets for the mini chart. */ data: number[]; - /** Bar color (hex). A vertical gradient is derived from it. */ color: string; bars?: number; className?: string; } -/** - * Resamples `data` to exactly `bars` values (summed down or nearest-neighbour - * stretched up) so the chart renders a stable bar count even though the trend - * query omits inactive days and a quiet range can yield far fewer points. - */ +// Resamples data to exactly bars values (summed down or nearest-neighbour stretched up). function bucketize(data: number[], bars: number): number[] { const out = Array(bars).fill(0); if (data.length === 0) return out; @@ -34,8 +28,6 @@ function bucketize(data: number[], bars: number): number[] { return out; } -// Lightweight divs rather than a charting lib. -// Minimum height share so zero/quiet periods still render as a visible bar, not an invisible nub. const BASELINE_PCT = 32; export function BarSparkline({ data, color, bars = 8, className }: BarSparklineProps) { diff --git a/packages/ui/src/components/button.tsx b/packages/ui/src/components/button.tsx index ee067d6c..8c2ecb23 100644 --- a/packages/ui/src/components/button.tsx +++ b/packages/ui/src/components/button.tsx @@ -7,7 +7,6 @@ import { buttonVariants } from '../components/button-variants'; type Shape = 'base' | 'square' | 'circle'; -// Icon-only shapes: collapse padding and match width to the height of each size. const SQUARE_WIDTH: Record = { xs: 'w-6 p-0', sm: 'w-8 p-0', @@ -27,9 +26,7 @@ export interface ButtonProps VariantProps { asChild?: boolean; shape?: Shape; - /** Element rendered before children (skipped while `loading`). */ icon?: React.ReactNode; - /** Shows a spinner and disables the button. */ loading?: boolean; } @@ -37,7 +34,6 @@ const Button = React.forwardRef( ({ className, variant, size, shape = 'base', asChild = false, icon, loading, children, ...props }, ref) => { const classes = cn(buttonVariants({ variant, size }), shapeClass(shape, size), className); - // `asChild` renders the child itself with props merged onto it (Base UI's equivalent of the former Radix Slot). return useRender({ render: asChild && React.isValidElement(children) ? children : undefined, defaultTagName: 'button', @@ -67,7 +63,6 @@ export interface LinkButtonProps VariantProps { shape?: Shape; icon?: React.ReactNode; - /** Open in a new tab with safe rel attributes. */ external?: boolean; } diff --git a/packages/ui/src/components/chart-primitives.tsx b/packages/ui/src/components/chart-primitives.tsx index 33a8f60f..3f90841a 100644 --- a/packages/ui/src/components/chart-primitives.tsx +++ b/packages/ui/src/components/chart-primitives.tsx @@ -28,8 +28,6 @@ export function GraphShell({ className?: string; }) { return ( - // Same chrome as the dashboard stat cards: card face carries the title, the chart itself sits - // in a recessed inner panel.
- {/* Dot texture lives on the recessed face, where the chart reads against it. */} {legend && (
@@ -58,18 +55,11 @@ export function GraphShell({ } export interface SeriesMarkerProps { - /** Flat CSS colour. Ignored when `hatched` is set, which paints its own fill. */ color?: string; - /** The cross-hatched fill used for the input-token series. */ hatched?: boolean; - /** A dashed rule instead of a swatch, for series drawn as a dashed line. */ dashed?: boolean; } -/** - * The swatch that identifies a series. Shared by the legend and the tooltip so a series looks the - * same in both - a tooltip dot that doesn't match its legend chip reads as a different series. - */ export function SeriesMarker({ color, hatched, dashed }: SeriesMarkerProps) { if (dashed) { return ( diff --git a/packages/ui/src/components/load-error.tsx b/packages/ui/src/components/load-error.tsx index fa587947..6901ab03 100644 --- a/packages/ui/src/components/load-error.tsx +++ b/packages/ui/src/components/load-error.tsx @@ -3,18 +3,14 @@ import { Button } from '../components/button'; import { cn } from '../lib/utils'; interface LoadErrorProps { - /** Friendly headline, e.g. "Couldn't load dashboard data". */ title?: string; - /** Raw error detail (e.g. "Failed to fetch"), shown as a mono chip. */ detail?: string | null; - /** Guidance line under the title; defaults to connection-check copy. */ hint?: string; onRetry?: () => void; retrying?: boolean; className?: string; } -/** Friendly copy up front, raw error as a small mono chip - instead of a bare toast echoing "Failed to fetch". */ export function LoadError({ title = "Couldn't load data", detail, diff --git a/packages/ui/src/components/motion/smooth-scroll.tsx b/packages/ui/src/components/motion/smooth-scroll.tsx index 052eda8b..c64fd684 100644 --- a/packages/ui/src/components/motion/smooth-scroll.tsx +++ b/packages/ui/src/components/motion/smooth-scroll.tsx @@ -11,7 +11,6 @@ import { useRef, } from 'react'; -// Named fn, not a lib/ease token: Lenis needs a (t) => number easing fn, not bezier points. const EASE_SCROLL = (t: number) => Math.min(1, 1.001 - 2 ** (-10 * t)); export type ScrollTarget = number | string | HTMLElement; @@ -23,13 +22,10 @@ export type ScrollToOptions = { }; export type SmoothScrollApi = { - /** Null on the reduced-motion / native path. */ lenis: Lenis | null; scrollY: MotionValue; progress: MotionValue; - /** px/frame. */ velocity: MotionValue; - /** Jumps instantly under reduced motion. */ scrollTo: (target: ScrollTarget, options?: ScrollToOptions) => void; }; @@ -37,14 +33,11 @@ const SmoothScrollContext = createContext(null); export interface SmoothScrollProps { children: ReactNode; - /** True drives window scroll; false scrolls a contained area. */ root?: boolean; - /** Lower = smoother, heavier. */ lerp?: number; duration?: number; orientation?: 'vertical' | 'horizontal'; wheelMultiplier?: number; - /** Off by default: native touch momentum is already good on mobile. */ touch?: boolean; className?: string; } @@ -208,7 +201,6 @@ export function SmoothScroll({ smoothWheel: true, syncTouch: touch, easing: EASE_SCROLL, - // Else Lenis preventDefault()s every wheel event, blocking nested scroll areas. allowNestedScroll: true, }} > diff --git a/packages/ui/src/components/motion/stepped-slider.tsx b/packages/ui/src/components/motion/stepped-slider.tsx index 739fc57a..9568cf56 100644 --- a/packages/ui/src/components/motion/stepped-slider.tsx +++ b/packages/ui/src/components/motion/stepped-slider.tsx @@ -1,5 +1,4 @@ -// Adapted from a min/max/step range slider so labeled steps (e.g. Low/Medium/High/Max) line up on -// evenly spaced stops; the value readout sits statically above the track (no floating/portal tooltip). +// Stepped slider with static value readout. import { domAnimation, LazyMotion, @@ -26,8 +25,6 @@ import { useIsDarkMode } from '../../hooks/use-is-dark-mode'; const SPRING_GLIDE = { stiffness: 700, damping: 50, mass: 0.5 } as const; const SPRING_BOUNCY = { type: 'spring', stiffness: 500, damping: 14, mass: 0.7 } as const; -// Deterministic 0..1 seed from the slider's id, so two sliders maxed out at once get slightly -// different hues/timing instead of pulsing in perfect unison. function seedFromId(id: string | undefined) { if (!id) return 0; let hash = 0; @@ -50,9 +47,7 @@ export interface SteppedSliderProps { min?: number; max?: number; step?: number; - /** Tick dots + labels rendered at each defined step. */ steps?: SteppedSliderStep[]; - /** Live value readout shown above the track, and used as the accessible aria-valuetext. Defaults to the raw value. */ formatValue?: (value: number) => string; disabled?: boolean; className?: string; @@ -83,9 +78,6 @@ export function SteppedSlider({ const trackRef = useRef(null); const [internal, setInternal] = useState(defaultValue); const [active, setActive] = useState(false); - // Decoupled from the committed value so onValueChange fires once per gesture (on release), - // not on every pointer-move tick. Mirrored in a ref so release can read the pending value - // without doing the commit inside a state updater. const [dragValue, setDragValue] = useState(null); const dragValueRef = useRef(null); const controlled = value !== undefined; @@ -93,8 +85,6 @@ export function SteppedSlider({ const current = active && dragValue !== null ? clamp(dragValue, min, max) : committedValue; const percent = ((current - min) / (max - min)) * 100; const isMaxed = current === max; - - // Hue-shifted and timed per instance so simultaneous maxed-out sliders don't pulse in unison. const hue = 185 + (seed - 0.5) * 16; const glowColor = isDark ? `oklch(68% 0.14 ${hue})` : `oklch(52% 0.13 ${hue})`; const fillGradient = isDark @@ -209,7 +199,6 @@ export function SteppedSlider({ disabled ? 'pointer-events-none opacity-50' : 'cursor-grab active:cursor-grabbing', )} > - {/* Pattern repeats every 10px, shifting by exactly one period, so the loop point is never visible. */} {isMaxed ? (
- {/* Inset so end dots don't clip; sized up at max so they don't blend into the dot texture. */}
{steps.map((tick) => { const tp = ((tick.value - min) / (max - min)) * 100; @@ -248,7 +236,6 @@ export function SteppedSlider({ })}
- {/* Own layer so only opacity animates - Motion's box-shadow interpolator can't parse CSS custom properties in the color stops. */} {isMaxed && !reduce && ( - {/* layoutRoot: the indicator's layoutId measures in page coordinates, so without this - it would replay scroll offsets as movement inside fixed/scrolled containers. */} {children} @@ -137,7 +132,6 @@ export function TabsTrigger({ ); } - // Segment uses a neutral raised surface (vs. pill's max-contrast) so it reads as a standard segmented control. const isSegment = variant === 'segment'; const radius = variant === 'pill' ? 'rounded-full' : 'rounded-[5px]'; const indicatorBg = isSegment ? 'bg-ui-base shadow-sm ring-1 ring-ui-line' : 'bg-primary'; diff --git a/packages/ui/src/components/onboarding-card.tsx b/packages/ui/src/components/onboarding-card.tsx index 5f722622..ef920e23 100644 --- a/packages/ui/src/components/onboarding-card.tsx +++ b/packages/ui/src/components/onboarding-card.tsx @@ -42,7 +42,6 @@ export function OnboardingCard({
- {/* Header */}

{allDone ? completedTitle : title} @@ -63,7 +62,6 @@ export function OnboardingCard({ )}

- {/* Segmented bar */}
- {/* Check circle */} - - - - - {/* Text */} - {step.label} -
+ {/* Check circle */} + + + + {step.label} +
); if (step.onClick) { diff --git a/packages/ui/src/components/select-panel.tsx b/packages/ui/src/components/select-panel.tsx index 233dd1ae..296bb04b 100644 --- a/packages/ui/src/components/select-panel.tsx +++ b/packages/ui/src/components/select-panel.tsx @@ -109,7 +109,6 @@ export function SelectPanel({ overflow: 'hidden', pointerEvents: open ? 'auto' : 'none', }} - // Flush against the trigger, then separates into its own rounded pill. className="z-50 border border-ui-line bg-ui-base shadow-lg shadow-black/[0.04] dark:shadow-black/40" > onHighlight(index)} onClick={() => onSelect(option.value)} - className={cn( - // `whitespace-nowrap`: on the 72px "rows per page" select, the check icon ate the width and "10" wrapped to stacked digits. - 'flex w-full items-center justify-between gap-2 whitespace-nowrap rounded-md px-2.5 py-1.5 text-left text-sm outline-none transition-colors', + className={cn( + 'flex w-full items-center justify-between gap-2 whitespace-nowrap rounded-md px-2.5 py-1.5 text-left text-sm outline-none transition-colors', selected ? 'bg-ui-brand/10 font-medium text-ui-brand' : 'text-ui-default hover:bg-ui-fill hover:text-ui-strong focus-visible:bg-ui-fill', diff --git a/packages/ui/src/components/select-shared.ts b/packages/ui/src/components/select-shared.ts index daae2415..1479fee8 100644 --- a/packages/ui/src/components/select-shared.ts +++ b/packages/ui/src/components/select-shared.ts @@ -7,7 +7,6 @@ export interface SelectOption { label: string; } -/** Trigger box in viewport coordinates; the portaled panel is positioned from it. */ export interface TriggerRect { left: number; width: number; @@ -15,7 +14,7 @@ export interface TriggerRect { bottom: number; } -// Spring with bounce powers the unfold; per-property timings on the panel choreograph it. +// Spring with bounce powers the unfold. export const CHEVRON_TRANSITION: Transition = { type: 'spring', duration: 0.4, bounce: 0.3 }; // Compounds per option; 0.035 delayed the last item ~0.9s on long lists, so 0.02 is the compromise. @@ -28,5 +27,5 @@ export const ITEM_VARIANTS: Variants = { show: { opacity: 1, y: 0, filter: 'blur(0px)' }, }; -/** Snaps a property to its target: the side not being choreographed, and every reduced-motion path. */ +/** Snaps a property to its target. */ export const INSTANT_TRANSITION: Transition = { duration: 0 }; diff --git a/packages/ui/src/components/select.tsx b/packages/ui/src/components/select.tsx index c94dd325..03ba5f05 100644 --- a/packages/ui/src/components/select.tsx +++ b/packages/ui/src/components/select.tsx @@ -25,7 +25,6 @@ interface SelectProps { triggerClassName?: string; triggerStyle?: CSSProperties; leadingIcon?: ReactNode; - /** Decides the dropdown's background so it stays distinguishable from where the trigger sits. */ variant?: 'page' | 'card'; } @@ -56,12 +55,10 @@ export function Select({ const [rect, setRect] = useState(null); const [highlightedIndex, setHighlightedIndex] = useState(0); const optionRefs = useRef>([]); - /** What last moved the highlight - only keyboard moves should auto-scroll. */ const highlightSource = useRef<'keyboard' | 'pointer'>('keyboard'); const selectedOption = options.find((opt) => opt.value === value); - // Depends on `open` only: keying off `options`/`value` yanked the highlight back when a caller passed a freshly-built array mid-interaction. useEffect(() => { if (!open) return; highlightSource.current = 'keyboard'; @@ -70,7 +67,6 @@ export function Select({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [open]); - // Keyboard only: on hover this looped (hover → scrollIntoView → reposition → mouseenter again), causing jitter. useEffect(() => { if (!open || highlightSource.current !== 'keyboard') return; optionRefs.current[highlightedIndex]?.scrollIntoView({ block: 'nearest' }); @@ -83,8 +79,6 @@ export function Select({ const target = e.target as Node; if (triggerRef.current?.contains(target)) return; if (panelRef.current?.contains(target)) return; - // The label forwards its click to the trigger, so treating it as "outside" would close the - // panel here and let that forwarded click reopen it -- one click, no visible change. if (labelRef.current?.contains(target)) return; setOpen(false); }; @@ -106,7 +100,6 @@ export function Select({ return () => observer.disconnect(); }, []); - // Flips upward when there's no room below; batched to one animation frame since scroll/resize fire faster than repaints. useLayoutEffect(() => { if (!open) return; const trigger = triggerRef.current; @@ -122,7 +115,6 @@ export function Select({ setPlacement(below < h + 16 && above > below ? 'top' : 'bottom'); }; const scheduleUpdate = (e?: Event) => { - // Scrolling the list doesn't move the trigger; recomputing only causes flicker. if (e && e.target instanceof Node && panelRef.current?.contains(e.target)) return; if (frame !== null) return; frame = requestAnimationFrame(update); @@ -230,7 +222,6 @@ export function Select({ />
- {/* Portaled to so it can't be clipped by an ancestor's stacking context. */} {createPortal( Date: Wed, 23 Sep 2026 02:30:26 +0530 Subject: [PATCH 07/16] chore: update dependencies to specific versions across all packages --- apps/node/package.json | 14 ++-- apps/worker/package.json | 12 ++-- package-lock.json | 98 +++++++++++++-------------- package.json | 14 ++-- packages/api/package.json | 10 +-- packages/core/package.json | 2 +- packages/db/package.json | 4 +- packages/models/package.json | 4 +- packages/node-adapters/package.json | 8 +-- packages/provider-github/package.json | 4 +- packages/ui/package.json | 2 +- 11 files changed, 86 insertions(+), 86 deletions(-) diff --git a/apps/node/package.json b/apps/node/package.json index bca66afc..71547e69 100644 --- a/apps/node/package.json +++ b/apps/node/package.json @@ -10,13 +10,13 @@ "typecheck": "tsc -p tsconfig.json" }, "dependencies": { - "@codraoss/api": "*", - "@codraoss/core": "*", - "@codraoss/db": "*", - "@codraoss/models": "*", - "@codraoss/node-adapters": "*", - "@codraoss/provider-github": "*", - "@codraoss/schema": "*", + "@codraoss/api": "^0.9.13", + "@codraoss/core": "^0.9.13", + "@codraoss/db": "^0.9.13", + "@codraoss/models": "^0.9.13", + "@codraoss/node-adapters": "^0.9.13", + "@codraoss/provider-github": "^0.9.13", + "@codraoss/schema": "^0.9.13", "@hono/node-server": "^1.11.5", "bullmq": "^6.3.8", "dotenv": "^16.4.7", diff --git a/apps/worker/package.json b/apps/worker/package.json index 7bc6ce5a..887e4e2b 100644 --- a/apps/worker/package.json +++ b/apps/worker/package.json @@ -4,12 +4,12 @@ "private": true, "type": "module", "dependencies": { - "@codraoss/api": "*", - "@codraoss/core": "*", - "@codraoss/db": "*", - "@codraoss/models": "*", - "@codraoss/provider-github": "*", - "@codraoss/schema": "*", + "@codraoss/api": "^0.9.13", + "@codraoss/core": "^0.9.13", + "@codraoss/db": "^0.9.13", + "@codraoss/models": "^0.9.13", + "@codraoss/provider-github": "^0.9.13", + "@codraoss/schema": "^0.9.13", "hono": "^4.12.34" }, "devDependencies": { diff --git a/package-lock.json b/package-lock.json index deb8f494..6714ccc2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,13 +14,13 @@ ], "dependencies": { "@base-ui/react": "^1.6.0", - "@codraoss/api": "*", - "@codraoss/core": "*", - "@codraoss/db": "*", - "@codraoss/models": "*", - "@codraoss/provider-github": "*", - "@codraoss/schema": "*", - "@codraoss/ui": "*", + "@codraoss/api": "^0.9.13", + "@codraoss/core": "^0.9.13", + "@codraoss/db": "^0.9.13", + "@codraoss/models": "^0.9.13", + "@codraoss/provider-github": "^0.9.13", + "@codraoss/schema": "^0.9.13", + "@codraoss/ui": "^0.9.13", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "hono": "^4.12.34", @@ -74,15 +74,15 @@ }, "apps/node": { "name": "@codraoss/node-server", - "version": "0.9.12", - "dependencies": { - "@codraoss/api": "*", - "@codraoss/core": "*", - "@codraoss/db": "*", - "@codraoss/models": "*", - "@codraoss/node-adapters": "*", - "@codraoss/provider-github": "*", - "@codraoss/schema": "*", + "version": "0.9.13", + "dependencies": { + "@codraoss/api": "^0.9.13", + "@codraoss/core": "^0.9.13", + "@codraoss/db": "^0.9.13", + "@codraoss/models": "^0.9.13", + "@codraoss/node-adapters": "^0.9.13", + "@codraoss/provider-github": "^0.9.13", + "@codraoss/schema": "^0.9.13", "@hono/node-server": "^1.11.5", "bullmq": "^6.3.8", "dotenv": "^16.4.7", @@ -113,14 +113,14 @@ }, "apps/worker": { "name": "@codraoss/worker", - "version": "0.9.12", - "dependencies": { - "@codraoss/api": "*", - "@codraoss/core": "*", - "@codraoss/db": "*", - "@codraoss/models": "*", - "@codraoss/provider-github": "*", - "@codraoss/schema": "*", + "version": "0.9.13", + "dependencies": { + "@codraoss/api": "^0.9.13", + "@codraoss/core": "^0.9.13", + "@codraoss/db": "^0.9.13", + "@codraoss/models": "^0.9.13", + "@codraoss/provider-github": "^0.9.13", + "@codraoss/schema": "^0.9.13", "hono": "^4.12.34" }, "devDependencies": { @@ -11490,14 +11490,14 @@ }, "packages/api": { "name": "@codraoss/api", - "version": "0.9.12", + "version": "0.9.13", "license": "AGPL-3.0-only", "dependencies": { - "@codraoss/core": "*", - "@codraoss/db": "*", - "@codraoss/models": "*", - "@codraoss/provider-github": "*", - "@codraoss/schema": "*", + "@codraoss/core": "^0.9.13", + "@codraoss/db": "^0.9.13", + "@codraoss/models": "^0.9.13", + "@codraoss/provider-github": "^0.9.13", + "@codraoss/schema": "^0.9.13", "hono": "^4.12.34", "zod": "^4.3.6" }, @@ -11525,10 +11525,10 @@ }, "packages/core": { "name": "@codraoss/core", - "version": "0.9.12", + "version": "0.9.13", "license": "AGPL-3.0-only", "dependencies": { - "@codraoss/schema": "*", + "@codraoss/schema": "^0.9.13", "jsonrepair": "^3.15.0", "picomatch": "^4.0.7", "zod": "^4.3.6" @@ -11540,11 +11540,11 @@ }, "packages/db": { "name": "@codraoss/db", - "version": "0.9.12", + "version": "0.9.13", "license": "AGPL-3.0-only", "dependencies": { - "@codraoss/core": "*", - "@codraoss/schema": "*", + "@codraoss/core": "^0.9.13", + "@codraoss/schema": "^0.9.13", "postgres": "^3.4.9" }, "devDependencies": { @@ -11571,11 +11571,11 @@ }, "packages/models": { "name": "@codraoss/models", - "version": "0.9.12", + "version": "0.9.13", "license": "AGPL-3.0-only", "dependencies": { - "@codraoss/core": "*", - "@codraoss/schema": "*" + "@codraoss/core": "^0.9.13", + "@codraoss/schema": "^0.9.13" }, "devDependencies": { "tsup": "^8.0.2" @@ -11583,13 +11583,13 @@ }, "packages/node-adapters": { "name": "@codraoss/node-adapters", - "version": "0.9.12", + "version": "0.9.13", "license": "AGPL-3.0-only", "dependencies": { - "@codraoss/api": "*", - "@codraoss/core": "*", - "@codraoss/db": "*", - "@codraoss/schema": "*", + "@codraoss/api": "^0.9.13", + "@codraoss/core": "^0.9.13", + "@codraoss/db": "^0.9.13", + "@codraoss/schema": "^0.9.13", "bullmq": "^6.3.8", "ioredis": "^6.0.0", "nanoid": "^5.0.9" @@ -11636,11 +11636,11 @@ }, "packages/provider-github": { "name": "@codraoss/provider-github", - "version": "0.9.12", + "version": "0.9.13", "license": "AGPL-3.0-only", "dependencies": { - "@codraoss/core": "*", - "@codraoss/schema": "*" + "@codraoss/core": "^0.9.13", + "@codraoss/schema": "^0.9.13" }, "devDependencies": { "tsup": "^8.0.2" @@ -11648,7 +11648,7 @@ }, "packages/schema": { "name": "@codraoss/schema", - "version": "0.9.12", + "version": "0.9.13", "license": "AGPL-3.0-only", "dependencies": { "zod": "^4.3.6" @@ -11659,11 +11659,11 @@ }, "packages/ui": { "name": "@codraoss/ui", - "version": "0.9.12", + "version": "0.9.13", "license": "AGPL-3.0-only", "dependencies": { "@base-ui/react": "^1.6.0", - "@codraoss/schema": "*", + "@codraoss/schema": "^0.9.13", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "sugar-high": "^2.0.3", diff --git a/package.json b/package.json index 1e688991..d3d418b0 100644 --- a/package.json +++ b/package.json @@ -71,13 +71,13 @@ }, "dependencies": { "@base-ui/react": "^1.6.0", - "@codraoss/api": "*", - "@codraoss/core": "*", - "@codraoss/db": "*", - "@codraoss/models": "*", - "@codraoss/provider-github": "*", - "@codraoss/schema": "*", - "@codraoss/ui": "*", + "@codraoss/api": "^0.9.13", + "@codraoss/core": "^0.9.13", + "@codraoss/db": "^0.9.13", + "@codraoss/models": "^0.9.13", + "@codraoss/provider-github": "^0.9.13", + "@codraoss/schema": "^0.9.13", + "@codraoss/ui": "^0.9.13", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "hono": "^4.12.34", diff --git a/packages/api/package.json b/packages/api/package.json index 1dd148fa..cb01ac9b 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -51,11 +51,11 @@ "dependencies": { "hono": "^4.12.34", "zod": "^4.3.6", - "@codraoss/schema": "workspace:*", - "@codraoss/core": "workspace:*", - "@codraoss/db": "workspace:*", - "@codraoss/provider-github": "workspace:*", - "@codraoss/models": "workspace:*" + "@codraoss/schema": "^0.9.13", + "@codraoss/core": "^0.9.13", + "@codraoss/db": "^0.9.13", + "@codraoss/provider-github": "^0.9.13", + "@codraoss/models": "^0.9.13" }, "devDependencies": { "@types/node": "^22.0.3", diff --git a/packages/core/package.json b/packages/core/package.json index 3f872975..435030e0 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -123,7 +123,7 @@ "postpack": "node ../../scripts/swap-publish-exports.mjs restore" }, "dependencies": { - "@codraoss/schema": "workspace:*", + "@codraoss/schema": "^0.9.13", "jsonrepair": "^3.15.0", "picomatch": "^4.0.7", "zod": "^4.3.6" diff --git a/packages/db/package.json b/packages/db/package.json index 6e7125db..fabab8b8 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -67,8 +67,8 @@ "postpack": "node ../../scripts/swap-publish-exports.mjs restore" }, "dependencies": { - "@codraoss/schema": "workspace:*", - "@codraoss/core": "workspace:*", + "@codraoss/schema": "^0.9.13", + "@codraoss/core": "^0.9.13", "postgres": "^3.4.9" }, "devDependencies": { diff --git a/packages/models/package.json b/packages/models/package.json index d3836ded..c1e6e1b2 100644 --- a/packages/models/package.json +++ b/packages/models/package.json @@ -78,8 +78,8 @@ "postpack": "node ../../scripts/swap-publish-exports.mjs restore" }, "dependencies": { - "@codraoss/schema": "workspace:*", - "@codraoss/core": "workspace:*" + "@codraoss/schema": "^0.9.13", + "@codraoss/core": "^0.9.13" }, "devDependencies": { "tsup": "^8.0.2" diff --git a/packages/node-adapters/package.json b/packages/node-adapters/package.json index 74378638..f4392b14 100644 --- a/packages/node-adapters/package.json +++ b/packages/node-adapters/package.json @@ -43,10 +43,10 @@ "typecheck": "tsc -p tsconfig.json" }, "dependencies": { - "@codraoss/api": "workspace:*", - "@codraoss/core": "workspace:*", - "@codraoss/db": "workspace:*", - "@codraoss/schema": "workspace:*", + "@codraoss/api": "^0.9.13", + "@codraoss/core": "^0.9.13", + "@codraoss/db": "^0.9.13", + "@codraoss/schema": "^0.9.13", "bullmq": "^6.3.8", "ioredis": "^6.0.0", "nanoid": "^5.0.9" diff --git a/packages/provider-github/package.json b/packages/provider-github/package.json index 87b204a2..e69d74ac 100644 --- a/packages/provider-github/package.json +++ b/packages/provider-github/package.json @@ -53,8 +53,8 @@ "postpack": "node ../../scripts/swap-publish-exports.mjs restore" }, "dependencies": { - "@codraoss/core": "workspace:*", - "@codraoss/schema": "workspace:*" + "@codraoss/core": "^0.9.13", + "@codraoss/schema": "^0.9.13" }, "devDependencies": { "tsup": "^8.0.2" diff --git a/packages/ui/package.json b/packages/ui/package.json index 35c3e849..079223fa 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -105,7 +105,7 @@ "sonner": ">=2.0.0" }, "dependencies": { - "@codraoss/schema": "workspace:*", + "@codraoss/schema": "^0.9.13", "@base-ui/react": "^1.6.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", From b3d791a5efeb7e441cea584a162b4d3250ef6a0e Mon Sep 17 00:00:00 2001 From: Devarshi Shimpi Date: Wed, 23 Sep 2026 02:36:12 +0530 Subject: [PATCH 08/16] fix: reorder export fields in package.json for consistency --- packages/api/package.json | 12 ++++++------ packages/node-adapters/package.json | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/api/package.json b/packages/api/package.json index cb01ac9b..a9aa98ee 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -29,16 +29,16 @@ "access": "public", "exports": { ".": { - "import": "./dist/index.js", - "types": "./dist/index.d.ts" + "types": "./dist/index.d.ts", + "import": "./dist/index.js" }, "./logger": { - "import": "./dist/logger.js", - "types": "./dist/logger.d.ts" + "types": "./dist/logger.d.ts", + "import": "./dist/logger.js" }, "./platform": { - "import": "./dist/platform/index.js", - "types": "./dist/platform/index.d.ts" + "types": "./dist/platform/index.d.ts", + "import": "./dist/platform/index.js" } } }, diff --git a/packages/node-adapters/package.json b/packages/node-adapters/package.json index f4392b14..8276a6a6 100644 --- a/packages/node-adapters/package.json +++ b/packages/node-adapters/package.json @@ -15,8 +15,8 @@ "types": "./dist/index.d.ts", "exports": { ".": { - "import": "./dist/index.js", - "types": "./dist/index.d.ts" + "types": "./dist/index.d.ts", + "import": "./dist/index.js" } } }, From 1b1cdcfdb52115ea7cd0c78afbbf5d6ef2f2acc8 Mon Sep 17 00:00:00 2001 From: Devarshi Shimpi Date: Wed, 23 Sep 2026 02:50:21 +0530 Subject: [PATCH 09/16] chore: update configuration files for improved build process and module resolution --- packages/node-adapters/package.json | 1 + packages/node-adapters/tsconfig.json | 13 ++++++++----- packages/node-adapters/tsup.config.json | 7 ++++--- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/packages/node-adapters/package.json b/packages/node-adapters/package.json index 8276a6a6..a5bfcadf 100644 --- a/packages/node-adapters/package.json +++ b/packages/node-adapters/package.json @@ -3,6 +3,7 @@ "version": "0.9.13", "description": "Node.js platform adapters for Codra, including BullMQ, Redis, and Stateless Orchestration.", "type": "module", + "sideEffects": false, "main": "./src/index.ts", "types": "./dist/index.d.ts", "exports": { diff --git a/packages/node-adapters/tsconfig.json b/packages/node-adapters/tsconfig.json index 391e21ce..6e673328 100644 --- a/packages/node-adapters/tsconfig.json +++ b/packages/node-adapters/tsconfig.json @@ -1,10 +1,13 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - "outDir": "dist", - "rootDir": "src", - "types": ["node"] + "module": "ESNext", + "moduleResolution": "Bundler", + "types": ["node"], + "composite": false, + "declaration": false, + "emitDeclarationOnly": false, + "noEmit": true }, - "include": ["src/**/*"], - "exclude": ["node_modules", "dist"] + "include": ["src/**/*"] } diff --git a/packages/node-adapters/tsup.config.json b/packages/node-adapters/tsup.config.json index 28a3fea0..369fd559 100644 --- a/packages/node-adapters/tsup.config.json +++ b/packages/node-adapters/tsup.config.json @@ -1,10 +1,11 @@ { "entry": ["src/index.ts"], "format": ["esm"], - "outDir": "dist", - "clean": true, - "sourcemap": true, + "dts": true, "splitting": false, + "sourcemap": true, + "clean": true, "target": "node20", + "outDir": "dist", "noExternal": [] } From 61fa9049f95abf37b3b7b9c465a50e09cb5fa9e8 Mon Sep 17 00:00:00 2001 From: Devarshi Shimpi Date: Wed, 23 Sep 2026 03:03:29 +0530 Subject: [PATCH 10/16] chore: add node-adapters to build:packages script for comprehensive builds --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d3d418b0..2673463a 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ ], "scripts": { "build": "vite build && npm run cf-typegen", - "build:packages": "npm run build -w @codraoss/schema && npm run build -w @codraoss/core && npm run build -w @codraoss/provider-github && npm run build -w @codraoss/db && npm run build -w @codraoss/models && npm run build -w @codraoss/api && npm run build -w @codraoss/ui", + "build:packages": "npm run build -w @codraoss/schema && npm run build -w @codraoss/core && npm run build -w @codraoss/provider-github && npm run build -w @codraoss/db && npm run build -w @codraoss/models && npm run build -w @codraoss/api && npm run build -w @codraoss/ui && npm run build -w @codraoss/node-adapters", "build:all": "npm run build --workspaces --if-present", "cf-typegen": "cd apps/worker && wrangler types ./src/worker-env.d.ts", "deploy": "npm run build && npm run migrate && cd apps/worker && wrangler deploy", From f48421157510919a2eb99581520c01ce5d9cfb79 Mon Sep 17 00:00:00 2001 From: Devarshi Shimpi Date: Wed, 23 Sep 2026 03:13:14 +0530 Subject: [PATCH 11/16] feat: add build coverage check to CI workflow and scripts --- .github/workflows/ci.yml | 3 +++ package.json | 3 ++- scripts/check-package-builds.mjs | 43 ++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 scripts/check-package-builds.mjs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 97818689..6113b5e6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -60,6 +60,9 @@ jobs: - name: Install dependencies run: npm ci + - name: Check build coverage + run: npm run check:builds + - name: Build packages run: npm run build:packages diff --git a/package.json b/package.json index 2673463a..dd8f7fdd 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,8 @@ "typecheck": "tsc --noEmit", "typecheck:all": "npm run typecheck --workspaces --if-present", "release": "npm run build:packages", - "check:exports": "node scripts/check-package-exports.mjs" + "check:exports": "node scripts/check-package-exports.mjs", + "check:builds": "node scripts/check-package-builds.mjs" }, "devDependencies": { "@arethetypeswrong/cli": "^0.18.5", diff --git a/scripts/check-package-builds.mjs b/scripts/check-package-builds.mjs new file mode 100644 index 00000000..0a3022f7 --- /dev/null +++ b/scripts/check-package-builds.mjs @@ -0,0 +1,43 @@ +import { readFileSync, existsSync, readdirSync } from 'node:fs'; +import { join, dirname, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const root = resolve(dirname(fileURLToPath(import.meta.url)), '..'); +const packagesDir = join(root, 'packages'); + +const rootPkg = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')); +const buildPackages = rootPkg.scripts?.['build:packages'] ?? ''; + +const errors = []; +const checked = []; + +for (const name of readdirSync(packagesDir)) { + const pkgDir = join(packagesDir, name); + const pkgJsonPath = join(pkgDir, 'package.json'); + if (!existsSync(pkgJsonPath)) continue; + + const pkg = JSON.parse(readFileSync(pkgJsonPath, 'utf8')); + if (pkg.private) continue; + if (!pkg.scripts?.build) continue; + + const id = pkg.name ?? name; + checked.push(id); + + // build:packages invokes each workspace via `npm run build -w ` + if (!buildPackages.includes(`-w ${id}`) && !buildPackages.includes(`-w ${name}`)) { + errors.push( + `${id}: has a "build" script but is missing from root "build:packages". ` + + `CI lints every packages/*/ with publint, so its dist/ is never built on a fresh runner. ` + + `Add "npm run build -w ${id}" to "build:packages" in package.json.`, + ); + } +} + +if (errors.length) { + console.error('Package build coverage check failed:\n'); + for (const e of errors) console.error(' - ' + e); + console.error('\nChecked: ' + checked.join(', ')); + process.exit(1); +} + +console.log(`Package build coverage check passed: build:packages covers all publishable packages (${checked.join(', ')}).`); From 65fec2b1d1b7918adb2a22a8a77e6289ba783f5b Mon Sep 17 00:00:00 2001 From: Devarshi Shimpi Date: Wed, 23 Sep 2026 10:05:43 +0530 Subject: [PATCH 12/16] fix: enhance Docker support and improve error handling in services --- apps/node/Dockerfile | 3 +++ apps/node/src/adapters/services.ts | 6 +++--- apps/node/src/api-deps.ts | 1 + apps/node/src/index.ts | 4 ++-- docs/deployment/docker.md | 7 +++++-- packages/core/src/ports/git-provider.ts | 4 +++- packages/db/src/client.ts | 2 +- packages/node-adapters/src/redis-queue.ts | 2 +- packages/node-adapters/tsconfig.json | 4 ++-- packages/ui/src/components/onboarding-card.tsx | 2 +- 10 files changed, 22 insertions(+), 13 deletions(-) diff --git a/apps/node/Dockerfile b/apps/node/Dockerfile index 537bfc19..420380b8 100644 --- a/apps/node/Dockerfile +++ b/apps/node/Dockerfile @@ -24,6 +24,9 @@ COPY packages ./packages # Build the node server and its dependencies RUN npm run build --workspace=@codraoss/node-server +# Prune development dependencies before copying to runner stage +RUN npm prune --omit=dev + # Build runner stage FROM node:20-alpine AS runner WORKDIR /app diff --git a/apps/node/src/adapters/services.ts b/apps/node/src/adapters/services.ts index 5b36b2ba..6579abd3 100644 --- a/apps/node/src/adapters/services.ts +++ b/apps/node/src/adapters/services.ts @@ -14,7 +14,7 @@ export function makeModelFactory(env: NodeAppBindings) { return (jobId: string, tracker: TokenTracker): ReviewModel => new ModelRunner({ kv: env.APP_KV as any, secretStore: { - getSecret: async (key) => env[key as keyof NodeAppBindings] as string || process.env[key] || null, + getSecret: async (key) => (env[key as keyof NodeAppBindings] as string | undefined | null) ?? process.env[key] ?? null, }, getConfig: (modelId) => getResolvedModelConfig({ HYPERDRIVE: env.DATABASE_CONFIG, APP_KV: env.APP_KV }, modelId), aiBinding: undefined, @@ -29,8 +29,8 @@ export function makeFormatterFactory(env: NodeAppBindings) { export function makeGitHubClientFactory(env: NodeAppBindings): GitProviderFactory { return { - forInstallation(installationId: string): ReviewGitProvider { - return new GitHubService(env, installationId); + forInstallation(installationId: string, tracker?: TokenTracker): ReviewGitProvider { + return new GitHubService(env, installationId, tracker); }, }; } diff --git a/apps/node/src/api-deps.ts b/apps/node/src/api-deps.ts index 6007ca62..10d2c7d7 100644 --- a/apps/node/src/api-deps.ts +++ b/apps/node/src/api-deps.ts @@ -20,6 +20,7 @@ export function createNodeApiDeps(env: NodeAppBindings) { logger.info(`[API Deps] Terminated job workflow for job ${job.id}`); } catch (error) { logger.error(`[API Deps] Failed to terminate job workflow for job ${job.id}: ${error}`); + throw error; } } else { logger.warn(`[API Deps] QueueAdapter does not support deleteJob. Cannot terminate ${job.id}`); diff --git a/apps/node/src/index.ts b/apps/node/src/index.ts index d471cbe7..7eff93bf 100644 --- a/apps/node/src/index.ts +++ b/apps/node/src/index.ts @@ -1,4 +1,4 @@ -import * as dotenv from 'dotenv'; +import * as dotenv from 'dotenv'; import * as path from 'node:path'; dotenv.config({ path: path.resolve(process.cwd(), '../../.dev.vars') }); dotenv.config({ path: path.resolve(process.cwd(), '.dev.vars') }); @@ -25,7 +25,7 @@ const __dirname = dirname(__filename); const redisUrl = process.env.REDIS_URL || 'redis://localhost:6379'; const redisClient = new Redis(redisUrl, { maxRetriesPerRequest: null, - tls: redisUrl.startsWith('rediss://') ? { rejectUnauthorized: false } : undefined, // Managed Redis + tls: redisUrl.startsWith('rediss://') ? { rejectUnauthorized: process.env.REDIS_INSECURE_TLS !== 'true' } : undefined, // Managed Redis }); redisClient.on('error', (err) => { logger.error('[Redis Error]', err); diff --git a/docs/deployment/docker.md b/docs/deployment/docker.md index edc0c922..33553a04 100644 --- a/docs/deployment/docker.md +++ b/docs/deployment/docker.md @@ -26,7 +26,7 @@ APP_URL=https://codra.yourdomain.com ENVIRONMENT=production # Database & Redis (these map to the docker-compose.yml service names) -DATABASE_URL=postgres://postgres:password@postgres:5432/codra +DATABASE_URL=postgres://postgres:codra_secure_default_pass@postgres:5432/codra REDIS_URL=redis://redis:6379 # GitHub App Secrets @@ -34,7 +34,10 @@ GITHUB_APP_ID=your_app_id GITHUB_CLIENT_ID=your_client_id GITHUB_CLIENT_SECRET=your_client_secret GITHUB_APP_WEBHOOK_SECRET=your_webhook_secret -APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----" +# Note: For Docker Compose, you can preserve actual newlines in the private key without \n literals: +APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY----- +... +-----END RSA PRIVATE KEY-----" # Application Settings AUTH_CALLBACK_URL=https://codra.yourdomain.com/api/auth/callback diff --git a/packages/core/src/ports/git-provider.ts b/packages/core/src/ports/git-provider.ts index 223b49ae..60343772 100644 --- a/packages/core/src/ports/git-provider.ts +++ b/packages/core/src/ports/git-provider.ts @@ -46,6 +46,8 @@ export interface ReviewGitProvider { removeIssueLabelsIfPresent(owner: string, repo: string, prNumber: number, labels: string[]): Promise; } +import type { TokenTracker } from '../token-tracker'; + export interface GitProviderFactory { - forInstallation(installationId: string): ReviewGitProvider; + forInstallation(installationId: string, tracker?: TokenTracker): ReviewGitProvider; } diff --git a/packages/db/src/client.ts b/packages/db/src/client.ts index c038f720..d6d1d697 100644 --- a/packages/db/src/client.ts +++ b/packages/db/src/client.ts @@ -92,7 +92,7 @@ async function withStaleConnectionRecovery(env: DbEnv, op: (db: DbClient) => try { return await op(getDb(env)); } catch (error) { - if (inScope || env.workerMode !== false || !isStaleConnectionError(error)) throw error; + if (env.workerMode === false || inScope || !isStaleConnectionError(error)) throw error; const connectionString = env.HYPERDRIVE.connectionString; fallbackClients.delete(connectionString); diff --git a/packages/node-adapters/src/redis-queue.ts b/packages/node-adapters/src/redis-queue.ts index 704ac38b..f9c8ec8a 100644 --- a/packages/node-adapters/src/redis-queue.ts +++ b/packages/node-adapters/src/redis-queue.ts @@ -10,7 +10,7 @@ export class RedisQueueAdapter implements QueueProducer { jobOptions.delay = options.delaySeconds * 1000; } - await this.queue.add('review-job', message, jobOptions); + await this.queue.add('codra-reviews', message, jobOptions); } async deleteJob(jobId: string): Promise { diff --git a/packages/node-adapters/tsconfig.json b/packages/node-adapters/tsconfig.json index 6e673328..b857284e 100644 --- a/packages/node-adapters/tsconfig.json +++ b/packages/node-adapters/tsconfig.json @@ -5,9 +5,9 @@ "moduleResolution": "Bundler", "types": ["node"], "composite": false, - "declaration": false, + "declaration": true, "emitDeclarationOnly": false, - "noEmit": true + "noEmit": false }, "include": ["src/**/*"] } diff --git a/packages/ui/src/components/onboarding-card.tsx b/packages/ui/src/components/onboarding-card.tsx index ef920e23..58f673c5 100644 --- a/packages/ui/src/components/onboarding-card.tsx +++ b/packages/ui/src/components/onboarding-card.tsx @@ -103,7 +103,7 @@ export function OnboardingCard({ > - {step.label} + {step.label}
); From 41ec19fd5fb0591053a2836d1a1e6fe35489a2b4 Mon Sep 17 00:00:00 2001 From: Devarshi Shimpi Date: Wed, 23 Sep 2026 11:54:53 +0530 Subject: [PATCH 13/16] fix: update Dockerfile and docker-compose for improved security and consistency --- apps/node/Dockerfile | 4 ++-- apps/node/src/index.ts | 12 +++++++----- docker-compose.yml | 4 ++-- packages/core/tsconfig.json | 11 ----------- packages/node-adapters/src/worker.ts | 3 +-- packages/ui/src/components/onboarding-card.tsx | 6 +++--- 6 files changed, 15 insertions(+), 25 deletions(-) diff --git a/apps/node/Dockerfile b/apps/node/Dockerfile index 420380b8..e038d672 100644 --- a/apps/node/Dockerfile +++ b/apps/node/Dockerfile @@ -1,4 +1,4 @@ -FROM node:20-alpine AS builder +FROM node:20-alpine3.24 AS builder WORKDIR /app # Install build dependencies if needed @@ -28,7 +28,7 @@ RUN npm run build --workspace=@codraoss/node-server RUN npm prune --omit=dev # Build runner stage -FROM node:20-alpine AS runner +FROM node:20-alpine3.24 AS runner WORKDIR /app ENV NODE_ENV=production diff --git a/apps/node/src/index.ts b/apps/node/src/index.ts index 7eff93bf..9d6b8078 100644 --- a/apps/node/src/index.ts +++ b/apps/node/src/index.ts @@ -85,10 +85,13 @@ if (process.env.START_API !== 'false') { if (server) { - const originalClose = server.close; - server.close = () => originalClose(() => { - logger.info('HTTP server closed.'); - }) as any; + const originalClose = server.close.bind(server); + server.close = function(callback?: (err?: Error) => void) { + return originalClose((err?: Error) => { + logger.info('HTTP server closed.'); + if (callback) callback(err); + }); + } as typeof server.close; } } @@ -112,7 +115,6 @@ const shutdown = async () => { // Postgres client manages its own pool. logger.info('All services stopped. Exiting.'); - process.exit(0); }; process.on('SIGTERM', shutdown); diff --git a/docker-compose.yml b/docker-compose.yml index 27292d61..0a9de3b7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,7 +5,7 @@ services: image: postgres:15-alpine environment: POSTGRES_USER: postgres - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-codra_secure_default_pass} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-INSECURE_DEFAULT_CHANGE_ME_IN_PROD} POSTGRES_DB: codra ports: - "5432:5432" @@ -36,7 +36,7 @@ services: ports: - "3000:3000" environment: - - DATABASE_URL=postgres://postgres:${POSTGRES_PASSWORD:-codra_secure_default_pass}@postgres:5432/codra + - DATABASE_URL=postgres://postgres:${POSTGRES_PASSWORD:-INSECURE_DEFAULT_CHANGE_ME_IN_PROD}@postgres:5432/codra - REDIS_URL=redis://redis:6379 - START_WORKER=true - PORT=3000 diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json index afff666a..05ae1d08 100644 --- a/packages/core/tsconfig.json +++ b/packages/core/tsconfig.json @@ -1,19 +1,8 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - // The base sets ES2024 only. The engine reaches for AbortController/AbortSignal (timeout.ts), - // crypto.subtle + TextEncoder (verify.ts), console (logger.ts) and fetch types -- all of which - // live in the DOM lib. Deliberately NOT @cloudflare/workers-types: KVNamespace must never - // resolve inside this package, which is what forces the KvStore port to exist. "lib": ["ES2024", "DOM"], "types": ["node"], - - // No emit, and therefore no project reference to ../schema. Packages here have no build step: - // @codraoss/schema is consumed as raw TS source through its `exports` map, exactly as the root - // program consumes it. Keeping the base's composite/declaration settings would instead demand - // that schema be built to dist/ first (TS6305) -- a build nothing in this repo performs. - // This project exists to typecheck the package against a NARROWER lib/types than the root - // program, which is what catches a stray KVNamespace or hono import. "composite": false, "declaration": false, "emitDeclarationOnly": false, diff --git a/packages/node-adapters/src/worker.ts b/packages/node-adapters/src/worker.ts index 6c5789e4..9c55d1c1 100644 --- a/packages/node-adapters/src/worker.ts +++ b/packages/node-adapters/src/worker.ts @@ -20,8 +20,7 @@ export function startWorker( } const reviewRuntime = createRuntime(); - // Need to cast to any since DbEnv is dynamically merged downstream - const orchestrator = new NodeOrchestrator(reviewRuntime as any, queue); + const orchestrator = new NodeOrchestrator(reviewRuntime, queue); await orchestrator.startReviewJob(job.id ?? 'unknown', parseResult.data); }, { connection: redisConnection } diff --git a/packages/ui/src/components/onboarding-card.tsx b/packages/ui/src/components/onboarding-card.tsx index 58f673c5..41d6c1a7 100644 --- a/packages/ui/src/components/onboarding-card.tsx +++ b/packages/ui/src/components/onboarding-card.tsx @@ -63,7 +63,7 @@ export function OnboardingCard({
@@ -85,7 +85,7 @@ export function OnboardingCard({ {steps.map((step) => { const content = (
Date: Wed, 23 Sep 2026 14:43:11 +0530 Subject: [PATCH 14/16] fix: update paths and error handling in node services for improved stability --- apps/node/src/index.ts | 4 ++-- packages/db/src/client.ts | 2 +- packages/node-adapters/src/node-orchestrator.ts | 2 +- packages/node-adapters/src/redis-queue.ts | 2 +- scripts/check-package-builds.mjs | 3 ++- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/apps/node/src/index.ts b/apps/node/src/index.ts index 9d6b8078..b4221ea1 100644 --- a/apps/node/src/index.ts +++ b/apps/node/src/index.ts @@ -51,7 +51,7 @@ app.onError((err, c) => { }); -const distClientPath = path.resolve(__dirname, process.cwd().endsWith('node/dist') ? '../../dist/client' : '../dist/client'); +const distClientPath = path.resolve(__dirname, '../../../dist/client'); app.use('/assets/*', serveStatic({ root: distClientPath })); app.use('/*.svg', serveStatic({ root: distClientPath })); app.use('/*.ico', serveStatic({ root: distClientPath })); @@ -75,7 +75,7 @@ if (process.env.START_API !== 'false') { deps: createNodeApiDeps(env), }; - try { return await runWithDb({ ...env, HYPERDRIVE: env.DATABASE_CONFIG }, () => app.fetch(request, apiEnv as any)); } catch (e) { console.error('SERVE ERROR:', e); throw e; } + try { return await runWithDb({ ...env, HYPERDRIVE: env.DATABASE_CONFIG }, () => app.fetch(request, apiEnv as any)); } catch (e) { console.error('SERVE ERROR:', e); return new Response('Internal Server Error', { status: 500 }); } }, port, hostname: '0.0.0.0', diff --git a/packages/db/src/client.ts b/packages/db/src/client.ts index d6d1d697..73fc8172 100644 --- a/packages/db/src/client.ts +++ b/packages/db/src/client.ts @@ -99,7 +99,7 @@ async function withStaleConnectionRecovery(env: DbEnv, op: (db: DbClient) => const fresh = createDbClient(env); fallbackClients.set(connectionString, fresh); - return await op(fresh); + return op(fresh); } } diff --git a/packages/node-adapters/src/node-orchestrator.ts b/packages/node-adapters/src/node-orchestrator.ts index 28c1080a..43529ee7 100644 --- a/packages/node-adapters/src/node-orchestrator.ts +++ b/packages/node-adapters/src/node-orchestrator.ts @@ -12,7 +12,7 @@ export class NodeOrchestrator implements JobOrchestrator { async startReviewJob(id: string, params: ReviewJobMessage): Promise { return runWithDb(this.env, async () => { - const currentParams = { ...params }; + const currentParams = { jobId: id, ...params }; currentParams.phase = currentParams.phase ?? 'prepare'; const result = await runReview(this.env, currentParams); diff --git a/packages/node-adapters/src/redis-queue.ts b/packages/node-adapters/src/redis-queue.ts index f9c8ec8a..a711d701 100644 --- a/packages/node-adapters/src/redis-queue.ts +++ b/packages/node-adapters/src/redis-queue.ts @@ -10,7 +10,7 @@ export class RedisQueueAdapter implements QueueProducer { jobOptions.delay = options.delaySeconds * 1000; } - await this.queue.add('codra-reviews', message, jobOptions); + await this.queue.add(this.queue.name, message, jobOptions); } async deleteJob(jobId: string): Promise { diff --git a/scripts/check-package-builds.mjs b/scripts/check-package-builds.mjs index 0a3022f7..7f968ef2 100644 --- a/scripts/check-package-builds.mjs +++ b/scripts/check-package-builds.mjs @@ -24,7 +24,8 @@ for (const name of readdirSync(packagesDir)) { checked.push(id); // build:packages invokes each workspace via `npm run build -w ` - if (!buildPackages.includes(`-w ${id}`) && !buildPackages.includes(`-w ${name}`)) { + const isCovered = new RegExp(`-w\\s+${id}(?:\\s|$)`).test(buildPackages) || new RegExp(`-w\\s+${name}(?:\\s|$)`).test(buildPackages); + if (!isCovered) { errors.push( `${id}: has a "build" script but is missing from root "build:packages". ` + `CI lints every packages/*/ with publint, so its dist/ is never built on a fresh runner. ` + From fd7b6317cc7ccc698e4e4d297c6bce6ed8324b3d Mon Sep 17 00:00:00 2001 From: Devarshi Shimpi Date: Wed, 23 Sep 2026 14:51:07 +0530 Subject: [PATCH 15/16] fix: update createRuntime type to include DbEnv for improved type safety --- packages/node-adapters/src/worker.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/node-adapters/src/worker.ts b/packages/node-adapters/src/worker.ts index 9c55d1c1..8327c9b6 100644 --- a/packages/node-adapters/src/worker.ts +++ b/packages/node-adapters/src/worker.ts @@ -2,12 +2,13 @@ import { Worker, type Job } from 'bullmq'; import { reviewJobMessageSchema, type ReviewJobMessage } from '@codraoss/schema'; import { NodeOrchestrator } from './node-orchestrator'; import type { ReviewRuntime, QueueProducer } from '@codraoss/core/ports'; +import type { DbEnv } from '@codraoss/db/env'; import type Redis from 'ioredis'; export function startWorker( redisConnection: Redis, queue: QueueProducer, - createRuntime: () => ReviewRuntime, + createRuntime: () => ReviewRuntime & DbEnv, logger: { info: (msg: string) => void, error: (msg: string, err?: any) => void } ): Worker { const worker = new Worker( From 599b87012ba0d32238bf2854949886e2ac9bce38 Mon Sep 17 00:00:00 2001 From: Devarshi Shimpi Date: Wed, 23 Sep 2026 15:32:42 +0530 Subject: [PATCH 16/16] fix: update tsconfig to ensure correct rootDir and outDir settings --- packages/node-adapters/tsconfig.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/node-adapters/tsconfig.json b/packages/node-adapters/tsconfig.json index b857284e..a4131722 100644 --- a/packages/node-adapters/tsconfig.json +++ b/packages/node-adapters/tsconfig.json @@ -7,7 +7,9 @@ "composite": false, "declaration": true, "emitDeclarationOnly": false, - "noEmit": false + "noEmit": false, + "rootDir": "src", + "outDir": "dist" }, "include": ["src/**/*"] }