From 14200edb6023442305ce23bf5256ac0f4dccaf87 Mon Sep 17 00:00:00 2001 From: Croissant Le Doux Date: Thu, 16 Jul 2026 11:08:24 -0400 Subject: [PATCH] feat: scaffold outreach engine monorepo on the novelpad-desktop stack Workspaces: config (copied), outreach-core (schema + actions/queries + hard gates), outreach-ai (Gemini client + embeddings copies, profiler and mission-fit-judge agent stubs), outreach-worker (DBOS executor with nightly ingest + hourly expiry workflows), outreach-review (RR7 review queue v0). Initial drizzle migration incl. pgvector extension. Stack contract: Yarn 4.5.0 + Turbo, Node 22.16, Drizzle 0.44.6 + pgvector, DBOS 4.17.6, @google/genai on Vertex, gemini-embedding-001 @1536, React Router v7. Files copied from novelpad-desktop carry provenance headers @ 62c56b87. Co-Authored-By: Claude Fable 5 --- .gitignore | 15 + .yarnrc.yml | 1 + README.md | 40 + apps/outreach-review/README.md | 53 + apps/outreach-review/app/assets/main.css | 7 + apps/outreach-review/app/db.server.ts | 33 + apps/outreach-review/app/root.tsx | 56 + apps/outreach-review/app/routes.ts | 3 + apps/outreach-review/app/routes/_index.tsx | 109 + apps/outreach-review/package.json | 40 + apps/outreach-review/react-router.config.ts | 5 + apps/outreach-review/tailwind.config.ts | 7 + apps/outreach-review/tsconfig.json | 28 + apps/outreach-review/vite.config.ts | 21 + apps/outreach-worker/.env.example | 13 + apps/outreach-worker/README.md | 61 + apps/outreach-worker/package.json | 31 + apps/outreach-worker/src/main.ts | 76 + .../src/workflows/expire-grants.ts | 70 + .../src/workflows/ingest-grants.ts | 137 + apps/outreach-worker/tsconfig.json | 18 + docs/plan.md | 256 + package.json | 32 + packages/config/README.md | 1 + packages/config/package.json | 21 + packages/config/shadcn.css | 710 ++ packages/config/tailwind.config.ts | 125 + packages/config/tsconfig.base.json | 17 + .../config/vite-plugins/react-virtualized.js | 32 + packages/outreach-ai/README.md | 54 + packages/outreach-ai/package.json | 32 + .../src/agents/mission-fit-judge/run.ts | 64 + .../src/agents/mission-fit-judge/schema.ts | 20 + .../src/agents/org-profiler/run.ts | 52 + .../src/agents/org-profiler/schema.test.ts | 56 + .../src/agents/org-profiler/schema.ts | 36 + packages/outreach-ai/src/embeddings.ts | 61 + packages/outreach-ai/src/gemini.ts | 67 + packages/outreach-ai/src/index.ts | 7 + packages/outreach-ai/src/models.ts | 18 + packages/outreach-ai/tsconfig.json | 21 + packages/outreach-core/README.md | 54 + packages/outreach-core/bin/create-migration | 16 + packages/outreach-core/drizzle.config.ts | 14 + .../server/1784214387_initial-schema.sql | 134 + .../server/meta/1784214387_snapshot.json | 1120 +++ .../drizzle/server/meta/_journal.json | 13 + packages/outreach-core/package.json | 46 + packages/outreach-core/src/db/db.ts | 17 + packages/outreach-core/src/db/index.ts | 2 + packages/outreach-core/src/db/schema.ts | 339 + .../actions/expire-closed-grants.server.ts | 28 + .../src/grants/actions/index.server.ts | 2 + .../grants/actions/insert-grants.server.ts | 55 + .../outreach-core/src/grants/index.server.ts | 2 + .../src/grants/queries/index.server.ts | 1 + .../grants/queries/list-open-grants.server.ts | 15 + packages/outreach-core/src/index.server.ts | 6 + packages/outreach-core/src/index.ts | 9 + .../src/matches/actions/index.server.ts | 2 + .../matches/actions/insert-match.server.ts | 28 + .../actions/set-match-review.server.ts | 35 + .../src/matches/hard-gates.test.ts | 153 + .../outreach-core/src/matches/hard-gates.ts | 173 + .../outreach-core/src/matches/index.server.ts | 6 + .../src/matches/queries/index.server.ts | 1 + .../list-pending-review-matches.server.ts | 41 + .../src/orgs/actions/index.server.ts | 1 + .../src/orgs/actions/insert-org.server.ts | 30 + .../outreach-core/src/orgs/index.server.ts | 2 + .../src/orgs/queries/index.server.ts | 1 + .../queries/list-orgs-in-icp-band.server.ts | 14 + .../src/pipeline/actions/index.server.ts | 1 + .../actions/record-pipeline-event.server.ts | 29 + .../src/pipeline/index.server.ts | 2 + .../src/pipeline/queries/index.server.ts | 1 + .../list-pipeline-events-for-org.server.ts | 16 + packages/outreach-core/tsconfig.json | 15 + turbo.json | 20 + yarn.lock | 6687 +++++++++++++++++ 80 files changed, 11637 insertions(+) create mode 100644 .gitignore create mode 100644 .yarnrc.yml create mode 100644 README.md create mode 100644 apps/outreach-review/README.md create mode 100644 apps/outreach-review/app/assets/main.css create mode 100644 apps/outreach-review/app/db.server.ts create mode 100644 apps/outreach-review/app/root.tsx create mode 100644 apps/outreach-review/app/routes.ts create mode 100644 apps/outreach-review/app/routes/_index.tsx create mode 100644 apps/outreach-review/package.json create mode 100644 apps/outreach-review/react-router.config.ts create mode 100644 apps/outreach-review/tailwind.config.ts create mode 100644 apps/outreach-review/tsconfig.json create mode 100644 apps/outreach-review/vite.config.ts create mode 100644 apps/outreach-worker/.env.example create mode 100644 apps/outreach-worker/README.md create mode 100644 apps/outreach-worker/package.json create mode 100644 apps/outreach-worker/src/main.ts create mode 100644 apps/outreach-worker/src/workflows/expire-grants.ts create mode 100644 apps/outreach-worker/src/workflows/ingest-grants.ts create mode 100644 apps/outreach-worker/tsconfig.json create mode 100644 docs/plan.md create mode 100644 package.json create mode 100644 packages/config/README.md create mode 100644 packages/config/package.json create mode 100644 packages/config/shadcn.css create mode 100644 packages/config/tailwind.config.ts create mode 100644 packages/config/tsconfig.base.json create mode 100644 packages/config/vite-plugins/react-virtualized.js create mode 100644 packages/outreach-ai/README.md create mode 100644 packages/outreach-ai/package.json create mode 100644 packages/outreach-ai/src/agents/mission-fit-judge/run.ts create mode 100644 packages/outreach-ai/src/agents/mission-fit-judge/schema.ts create mode 100644 packages/outreach-ai/src/agents/org-profiler/run.ts create mode 100644 packages/outreach-ai/src/agents/org-profiler/schema.test.ts create mode 100644 packages/outreach-ai/src/agents/org-profiler/schema.ts create mode 100644 packages/outreach-ai/src/embeddings.ts create mode 100644 packages/outreach-ai/src/gemini.ts create mode 100644 packages/outreach-ai/src/index.ts create mode 100644 packages/outreach-ai/src/models.ts create mode 100644 packages/outreach-ai/tsconfig.json create mode 100644 packages/outreach-core/README.md create mode 100755 packages/outreach-core/bin/create-migration create mode 100644 packages/outreach-core/drizzle.config.ts create mode 100644 packages/outreach-core/drizzle/server/1784214387_initial-schema.sql create mode 100644 packages/outreach-core/drizzle/server/meta/1784214387_snapshot.json create mode 100644 packages/outreach-core/drizzle/server/meta/_journal.json create mode 100644 packages/outreach-core/package.json create mode 100644 packages/outreach-core/src/db/db.ts create mode 100644 packages/outreach-core/src/db/index.ts create mode 100644 packages/outreach-core/src/db/schema.ts create mode 100644 packages/outreach-core/src/grants/actions/expire-closed-grants.server.ts create mode 100644 packages/outreach-core/src/grants/actions/index.server.ts create mode 100644 packages/outreach-core/src/grants/actions/insert-grants.server.ts create mode 100644 packages/outreach-core/src/grants/index.server.ts create mode 100644 packages/outreach-core/src/grants/queries/index.server.ts create mode 100644 packages/outreach-core/src/grants/queries/list-open-grants.server.ts create mode 100644 packages/outreach-core/src/index.server.ts create mode 100644 packages/outreach-core/src/index.ts create mode 100644 packages/outreach-core/src/matches/actions/index.server.ts create mode 100644 packages/outreach-core/src/matches/actions/insert-match.server.ts create mode 100644 packages/outreach-core/src/matches/actions/set-match-review.server.ts create mode 100644 packages/outreach-core/src/matches/hard-gates.test.ts create mode 100644 packages/outreach-core/src/matches/hard-gates.ts create mode 100644 packages/outreach-core/src/matches/index.server.ts create mode 100644 packages/outreach-core/src/matches/queries/index.server.ts create mode 100644 packages/outreach-core/src/matches/queries/list-pending-review-matches.server.ts create mode 100644 packages/outreach-core/src/orgs/actions/index.server.ts create mode 100644 packages/outreach-core/src/orgs/actions/insert-org.server.ts create mode 100644 packages/outreach-core/src/orgs/index.server.ts create mode 100644 packages/outreach-core/src/orgs/queries/index.server.ts create mode 100644 packages/outreach-core/src/orgs/queries/list-orgs-in-icp-band.server.ts create mode 100644 packages/outreach-core/src/pipeline/actions/index.server.ts create mode 100644 packages/outreach-core/src/pipeline/actions/record-pipeline-event.server.ts create mode 100644 packages/outreach-core/src/pipeline/index.server.ts create mode 100644 packages/outreach-core/src/pipeline/queries/index.server.ts create mode 100644 packages/outreach-core/src/pipeline/queries/list-pipeline-events-for-org.server.ts create mode 100644 packages/outreach-core/tsconfig.json create mode 100644 turbo.json create mode 100644 yarn.lock diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9a3e49d --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +node_modules/ +dist/ +build/ +.react-router/ +*.tsbuildinfo +*.log +.env +.env.local +.turbo/ +.yarn/* +!.yarn/patches +!.yarn/releases +!.yarn/plugins +coverage/ +.DS_Store diff --git a/.yarnrc.yml b/.yarnrc.yml new file mode 100644 index 0000000..3186f3f --- /dev/null +++ b/.yarnrc.yml @@ -0,0 +1 @@ +nodeLinker: node-modules diff --git a/README.md b/README.md new file mode 100644 index 0000000..28600e3 --- /dev/null +++ b/README.md @@ -0,0 +1,40 @@ +# HelmDocs Grant-Match Outreach Engine + +Automated pipeline that matches open grants to NH nonprofits, then runs a personalized email drip whose success metric is a booked demo call. The grant match **is** the lead magnet — match accuracy is the prime engineering directive. + +Architecture doc: `docs/plan.md`. Build plan and phase status: `~/.claude/plans/helmdocs-outreach.md`. + +## Relationship to novelpad-desktop + +This repo deliberately mirrors the [novelpad-desktop](https://gitea.stephenmann.io/NovelPad/novelpad-desktop) stack so a future merge is a directory move, not a rewrite: + +- Yarn 4.5.0 + Turbo monorepo, Node 22.16 (volta), TypeScript ^5.9.3 +- Postgres + pgvector + Drizzle (0.44.6, pinned via `resolutions`), actions/queries pattern +- DBOS for durable jobs/cron (worker-app pattern) +- `@google/genai` on Vertex; embeddings `gemini-embedding-001` @ 1536 dims (identical to HelmDocs RAG — never mix embedding models) +- React Router v7 for the review UI + +**Merge rules**: all packages stay in the `@novelpad/` scope with names that never collide with novelpad-desktop workspaces (`outreach-*`). Files copied from novelpad-desktop carry a provenance header (`source path @ commit`) — diff against origin at merge time. No live code sharing; drift is fixed at merge, by design. + +**Boundary**: separate repo and separate Postgres on purpose — outreach tooling and sourced contact data stay outside the compliance-scoped product repo/DB. + +## Workspaces + +| Workspace | Purpose | +|---|---| +| `packages/config` | Shared tsconfig/tailwind base (copied from novelpad-desktop) | +| `packages/outreach-core` | Drizzle schema (grants, orgs, profiles, contacts, matches, pipeline_events), actions/queries, hard gates | +| `packages/outreach-ai` | Gemini client, embeddings, org-profiler + mission-fit-judge agents | +| `apps/outreach-worker` | DBOS executor: ingestion crons, expiry sweep, scoring runs, Apollo sync | +| `apps/outreach-review` | Human review queue (RR7). No auth by design — run behind IAP/localhost | + +## Development + +``` +yarn install +yarn build +yarn typecheck +yarn test +``` + +Migrations: `yarn create:migration ` inside `packages/outreach-core` (server-only — no client DB in this repo). diff --git a/apps/outreach-review/README.md b/apps/outreach-review/README.md new file mode 100644 index 0000000..9ca0b0d --- /dev/null +++ b/apps/outreach-review/README.md @@ -0,0 +1,53 @@ +# @novelpad/outreach-review + +Human review queue for (org, grant) matches produced by the scoring pipeline in `packages/outreach-core`. A reviewer sees each pending match — organization, hero grant, total score, easy-win flag — and approves or rejects it. Approved matches are picked up downstream and synced into Apollo sequences. + +## Auth + +**There is no authentication in this app, by design.** It is a tiny internal tool intended to run behind: + +- Google IAP (Identity-Aware Proxy) in any hosted environment, or +- `localhost` only, for local development + +Do **not** add `better-auth` (or any other auth library) to this workspace. If this app ever needs to leave an IAP-fronted network, that's a deliberate follow-up decision, not something to bolt on ad hoc here. + +## Development + +``` +yarn dev # react-router dev +yarn build # react-router build +yarn start # serve the production build +yarn typecheck # react-router typegen && tsc --noEmit +``` + +Requires `DATABASE_URL` pointing at the outreach Postgres instance (see `packages/outreach-core`). Optional: `PG_POOL_MAX`, `PG_CONNECTION_TIMEOUT_MS`. + +## Data contract + +This app is a thin consumer of `@novelpad/outreach-core`. It assumes the following exports (server-side query/action functions live under the `./server` subpath, matching the `packages/core` convention in novelpad-desktop): + +```ts +// @novelpad/outreach-core +export const schema: /* drizzle schema */; +export type NpOutreachDatabase = /* PgDatabase<..., typeof schema> */; +export interface PendingReviewMatch { + id: string; + orgName: string; + heroGrantTitle: string; + totalScore: number; + isEasyWin: boolean; +} +export type MatchReviewDecision = 'approved' | 'rejected'; + +// @novelpad/outreach-core/server +export function serverListPendingReviewMatches( + db: NpOutreachDatabase, +): Promise; +export function serverSetMatchReview( + db: NpOutreachDatabase, + matchId: string, + decision: MatchReviewDecision, +): Promise; +``` + +If `packages/outreach-core`'s matches domain (`src/matches/queries`, `src/matches/actions`) lands with different names or a different `PendingReviewMatch` shape, update `app/db.server.ts` and `app/routes/_index.tsx` to match rather than reshaping data client-side. diff --git a/apps/outreach-review/app/assets/main.css b/apps/outreach-review/app/assets/main.css new file mode 100644 index 0000000..b786c86 --- /dev/null +++ b/apps/outreach-review/app/assets/main.css @@ -0,0 +1,7 @@ +/* Deliberately font-free (see root.tsx) — this app doesn't pull in + @novelpad/config/shadcn.css because that file's first several lines are + @fontsource imports. Keeping the review queue on system fonts avoids + shipping the full novelpad-desktop font set for a tiny internal tool. */ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/apps/outreach-review/app/db.server.ts b/apps/outreach-review/app/db.server.ts new file mode 100644 index 0000000..6c76922 --- /dev/null +++ b/apps/outreach-review/app/db.server.ts @@ -0,0 +1,33 @@ +// Copied/adapted from novelpad-desktop apps/website/app/db/db.server.ts @ 62c56b87 +import { schema, type NpOutreachDatabase } from '@novelpad/outreach-core'; +import { drizzle } from 'drizzle-orm/node-postgres'; +import pkg from 'pg'; + +const { Pool } = pkg; + +// HMR-safe singleton. Without this, Vite re-evaluates this module on hot +// reload and instantiates a fresh Pool, while the previous pool lingers with +// open connections until idleTimeoutMillis fires — see the equivalent note +// in novelpad-desktop's apps/website/app/db/db.server.ts. +const g = globalThis as unknown as { + __outreachPool?: InstanceType; + __outreachDb?: NpOutreachDatabase; +}; + +const pool = + g.__outreachPool ?? + (g.__outreachPool = new Pool({ + connectionString: process.env.DATABASE_URL, + application_name: 'outreach-review', + max: Number(process.env.PG_POOL_MAX ?? 10), + idleTimeoutMillis: 30000, + connectionTimeoutMillis: Number( + process.env.PG_CONNECTION_TIMEOUT_MS ?? 15000, + ), + })); + +const db = + g.__outreachDb ?? + (g.__outreachDb = drizzle(pool, { schema }) as unknown as NpOutreachDatabase); + +export { db, pool }; diff --git a/apps/outreach-review/app/root.tsx b/apps/outreach-review/app/root.tsx new file mode 100644 index 0000000..e9251f1 --- /dev/null +++ b/apps/outreach-review/app/root.tsx @@ -0,0 +1,56 @@ +import { isRouteErrorResponse, Links, Meta, Outlet, Scripts } from 'react-router'; + +import type { Route } from './+types/root.js'; +import appCssUrl from './assets/main.css?url'; + +export function Layout({ children }: { children: React.ReactNode }) { + return ( + + + + + HelmDocs Outreach Review + + + + + + {children} + + + + ); +} + +export default function App() { + return ; +} + +export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) { + let message = 'Oops!'; + let details = 'An unexpected error occurred.'; + let stack: string | undefined; + + if (isRouteErrorResponse(error)) { + message = error.status === 404 ? '404' : 'Error'; + details = + error.status === 404 + ? 'The requested page could not be found.' + : error.statusText || details; + } else if (import.meta.env.DEV && error && error instanceof Error) { + details = error.message; + stack = error.stack; + } + + return ( +
+

{message}

+

{details}

+ {stack && ( +
+          {stack}
+        
+ )} +
+ ); +} diff --git a/apps/outreach-review/app/routes.ts b/apps/outreach-review/app/routes.ts new file mode 100644 index 0000000..17cbb9d --- /dev/null +++ b/apps/outreach-review/app/routes.ts @@ -0,0 +1,3 @@ +import { index, type RouteConfig } from '@react-router/dev/routes'; + +export default [index('routes/_index.tsx')] satisfies RouteConfig; diff --git a/apps/outreach-review/app/routes/_index.tsx b/apps/outreach-review/app/routes/_index.tsx new file mode 100644 index 0000000..2bf79d0 --- /dev/null +++ b/apps/outreach-review/app/routes/_index.tsx @@ -0,0 +1,109 @@ +import { + serverListPendingReviewMatches, + serverSetMatchReview, +} from '@novelpad/outreach-core/server'; +import { Form } from 'react-router'; + +import { db } from '#~/db.server.js'; + +import type { Route } from './+types/_index.js'; + +export async function loader({ request }: Route.LoaderArgs) { + if (request.method === 'HEAD') return; + + const matches = await serverListPendingReviewMatches(db); + return { matches }; +} + +export async function action({ request }: Route.ActionArgs) { + if (request.method === 'HEAD') return; + + const formData = await request.formData(); + const matchId = String(formData.get('matchId') ?? ''); + const decision = formData.get('decision'); + + if (!matchId || (decision !== 'approved' && decision !== 'rejected')) { + throw new Response('Invalid review submission', { status: 400 }); + } + + await serverSetMatchReview(db, { + matchId, + reviewStatus: decision, + rejectReason: decision === 'rejected' ? 'other' : undefined, + }); + return { ok: true as const }; +} + +export default function ReviewQueue({ loaderData }: Route.ComponentProps) { + const matches = loaderData?.matches ?? []; + + return ( +
+

Grant Match Review Queue

+ + {matches.length === 0 ? ( +

+ No pending matches to review. +

+ ) : ( + + + + + + + + + + + + {matches.map((match) => ( + + + + + + + + ))} + +
+ Organization + + Grant + + Score + + Easy Win + + Actions +
{match.orgName}{match.grantTitle}{match.totalScore}{match.easyWin ? 'Yes' : 'No'} +
+
+ + + +
+
+ + + +
+
+
+ )} +
+ ); +} diff --git a/apps/outreach-review/package.json b/apps/outreach-review/package.json new file mode 100644 index 0000000..504617a --- /dev/null +++ b/apps/outreach-review/package.json @@ -0,0 +1,40 @@ +{ + "name": "@novelpad/outreach-review", + "packageManager": "yarn@4.5.0", + "type": "module", + "version": "0.0.0", + "private": true, + "scripts": { + "dev": "react-router dev", + "build": "react-router build", + "start": "react-router-serve ./build/server/index.js", + "typecheck": "react-router typegen && tsc --noEmit" + }, + "imports": { + "#~/*": "./app/*" + }, + "dependencies": { + "@novelpad/outreach-core": "workspace:^", + "@react-router/node": "^7.9.1", + "@react-router/serve": "^7.9.1", + "drizzle-orm": "0.44.6", + "isbot": "4", + "pg": "8.20.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "react-router": "^7.9.1" + }, + "devDependencies": { + "@novelpad/config": "workspace:^", + "@react-router/dev": "^7.9.1", + "@types/node": "^22", + "@types/pg": "8.20.0", + "@types/react": "^19.0.9", + "@types/react-dom": "^19.0.3", + "autoprefixer": "^10.4.20", + "postcss": "^8.4.47", + "tailwindcss": "^3.4.13", + "typescript": "^5.9.3", + "vite": "^5.4.9" + } +} diff --git a/apps/outreach-review/react-router.config.ts b/apps/outreach-review/react-router.config.ts new file mode 100644 index 0000000..51e8967 --- /dev/null +++ b/apps/outreach-review/react-router.config.ts @@ -0,0 +1,5 @@ +import type { Config } from '@react-router/dev/config'; + +export default { + ssr: true, +} satisfies Config; diff --git a/apps/outreach-review/tailwind.config.ts b/apps/outreach-review/tailwind.config.ts new file mode 100644 index 0000000..67ba6e9 --- /dev/null +++ b/apps/outreach-review/tailwind.config.ts @@ -0,0 +1,7 @@ +import tailwindConfig from '@novelpad/config/tailwind.config.js'; +import type { Config } from 'tailwindcss'; + +export default { + ...tailwindConfig, + content: ['./app/**/*.{js,ts,jsx,tsx}'], +} satisfies Config; diff --git a/apps/outreach-review/tsconfig.json b/apps/outreach-review/tsconfig.json new file mode 100644 index 0000000..ad3919a --- /dev/null +++ b/apps/outreach-review/tsconfig.json @@ -0,0 +1,28 @@ +{ + "extends": "@novelpad/config/tsconfig.base.json", + "include": [ + "app/**/*.ts", + "app/**/*.tsx", + ".react-router/types/**/*" + ], + "exclude": ["node_modules", "build"], + "compilerOptions": { + "lib": ["DOM", "DOM.Iterable", "ES2022"], + "types": ["node", "vite/client"], + "target": "ES2022", + "module": "ES2022", + "moduleResolution": "bundler", + "jsx": "react-jsx", + "rootDirs": [".", "./.react-router/types"], + "baseUrl": ".", + "paths": { + "#~/*": ["./app/*"] + }, + "esModuleInterop": true, + "verbatimModuleSyntax": false, + "noEmit": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "noUncheckedIndexedAccess": false + } +} diff --git a/apps/outreach-review/vite.config.ts b/apps/outreach-review/vite.config.ts new file mode 100644 index 0000000..13c667a --- /dev/null +++ b/apps/outreach-review/vite.config.ts @@ -0,0 +1,21 @@ +import { reactRouter } from '@react-router/dev/vite'; +import tailwindcss from 'tailwindcss'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [reactRouter()], + build: { + target: 'es2022', + sourcemap: process.env.NODE_ENV !== 'production', + }, + ssr: { + // pg is Node-only (uses net/tls) and should load from Node's module + // resolver rather than Vite's SSR bundler, matching apps/website. + external: ['pg'], + }, + css: { + postcss: { + plugins: [tailwindcss()], + }, + }, +}); diff --git a/apps/outreach-worker/.env.example b/apps/outreach-worker/.env.example new file mode 100644 index 0000000..930da0d --- /dev/null +++ b/apps/outreach-worker/.env.example @@ -0,0 +1,13 @@ +# Postgres connection string shared by the DBOS system schema, the pg Pool, +# and the Drizzle client over the outreach schema. Required — main.ts throws +# on boot if this is unset. +DATABASE_URL=postgres://postgres:postgres@localhost:5432/helmdocs_outreach + +# Path to a GCP service account key JSON used by ingestion/scoring steps that +# call Google-hosted APIs (e.g. @google/genai subscoring, Drive-backed org +# profile lookups). Not read directly by main.ts yet; present here so it's +# provisioned alongside DATABASE_URL for the workflows that will need it. +GCP_SERVICE_ACCOUNT_KEY_PATH=./secrets/gcp-service-account.json + +# Optional: cap the pg Pool size (defaults to 20 — see main.ts). +# PG_POOL_MAX=20 diff --git a/apps/outreach-worker/README.md b/apps/outreach-worker/README.md new file mode 100644 index 0000000..3d9a1bf --- /dev/null +++ b/apps/outreach-worker/README.md @@ -0,0 +1,61 @@ +# @novelpad/outreach-worker + +DBOS executor process for the HelmDocs grant-match outreach engine. Runs the +scheduled workflows that keep the outreach schema's grant catalog fresh — +nightly ingestion from upstream sources and an hourly expiry sweep — decoupled +from any HTTP-facing app in this monorepo (mirrors the `workflow-worker` split +in `novelpad-desktop`). + +## Boot order (load-bearing) + +`src/main.ts` imports `./workflows/ingest-grants.js` and +`./workflows/expire-grants.js` **before** calling `DBOS.launch()`. Each of +those modules calls `DBOS.registerWorkflow` + `DBOS.registerScheduled` at +module-evaluation time — DBOS only dispatches scheduled/queued jobs for +functions that were registered before launch, so importing them after launch +(or not at all) silently means the cron jobs never fire. + +The `db` handle is then threaded into each workflow module via its +`set*Deps` injector (`setIngestGrantsDeps` / `setExpireGrantsDeps`), also +before launch — DBOS serializes scheduled-function arguments, so a Drizzle +client can't be passed through the scheduler call itself. This is the same +module-scope-registry pattern novelpad-desktop's `workflow-worker` uses for +`setStartDeps`. + +``` +1. import workflow modules → registers ingestGrants / expireGrants +2. build pg Pool + drizzle(db) +3. set*Deps({ db }) → populates each workflow's registry +4. DBOS.setConfig(...) +5. DBOS.launch() → scheduler starts firing +``` + +## Workflows + +- **`ingestGrants`** (`0 3 * * *`, nightly) — `fetchGrantsGov` (stub; real + implementation calls the Grants.gov Search2 API, + `POST https://api.grants.gov/v1/api/search2`) → `normalize` → upsert via + `serverInsertGrants` from `@novelpad/outreach-core/server`. NH state + postings and 990-PF extracts land as additional fetch+normalize steps + later. +- **`expireGrants`** (`0 * * * *`, hourly) — marks grants whose close date has + passed as closed via `serverExpireClosedGrants`, so they drop out of the + active match/scoring pool. + +Both are registered as a DBOS workflow *and* a scheduled function referencing +the same function object (`DBOS.registerWorkflow` then `DBOS.registerScheduled`) +— see the doc comments in `src/workflows/*.ts` for why the dual registration +is required. + +## Scripts + +- `yarn dev` — `node --env-file=.env --env-file-if-exists=.env.local --import tsx/esm ./src/main.ts` +- `yarn build` — esbuild bundle to `build/main.js` (`--packages=external`) +- `yarn start` — run the built bundle +- `yarn typecheck` — `tsc --noEmit` + +## Environment + +Copy `.env.example` to `.env` and fill in `DATABASE_URL` (required — `main.ts` +throws on boot without it) and `GCP_SERVICE_ACCOUNT_KEY_PATH` (used by +future ingestion/scoring steps that call Google-hosted APIs). diff --git a/apps/outreach-worker/package.json b/apps/outreach-worker/package.json new file mode 100644 index 0000000..28584ba --- /dev/null +++ b/apps/outreach-worker/package.json @@ -0,0 +1,31 @@ +{ + "name": "@novelpad/outreach-worker", + "packageManager": "yarn@4.5.0", + "type": "module", + "version": "1.0.0", + "private": true, + "scripts": { + "dev": "node --env-file=.env --env-file-if-exists=.env.local --import tsx/esm ./src/main.ts", + "build": "esbuild src/main.ts --bundle --platform=node --format=esm --outfile=build/main.js --packages=external", + "start": "node ./build/main.js", + "typecheck": "tsc --noEmit" + }, + "imports": { + "#~/*": "./src/*" + }, + "dependencies": { + "@dbos-inc/dbos-sdk": "4.17.6", + "@dbos-inc/drizzle-datasource": "4.17.6", + "@novelpad/outreach-core": "workspace:^", + "drizzle-orm": "0.44.6", + "pg": "8.20.0", + "tsx": "^4.19.2" + }, + "devDependencies": { + "@novelpad/config": "workspace:^", + "@types/node": "^22", + "@types/pg": "8.20.0", + "esbuild": "^0.24.0", + "typescript": "^5.9.3" + } +} diff --git a/apps/outreach-worker/src/main.ts b/apps/outreach-worker/src/main.ts new file mode 100644 index 0000000..03d3627 --- /dev/null +++ b/apps/outreach-worker/src/main.ts @@ -0,0 +1,76 @@ +// Copied/adapted from novelpad-desktop apps/workflow-worker/src/main.ts @ 62c56b87 +/** + * Outreach worker — DBOS executor process. + * + * Runs the grant-match outreach engine's scheduled workflows (nightly grant + * ingestion, hourly expiry sweep, and future scoring/sync jobs) against the + * outreach Postgres schema. Kept as its own process (mirroring novelpad's + * workflow-worker split) so long-running ingestion/scoring steps don't share + * a runtime with any future HTTP-facing app in this monorepo. + * + * Boot order is load-bearing (see ADR 0007 in novelpad-desktop): + * 1. Import the workflow modules for side-effect registration + * (`DBOS.registerWorkflow` / `DBOS.registerScheduled`) BEFORE + * `DBOS.launch()` — DBOS only dispatches scheduled/queued jobs for + * workflows that were registered before launch. + * 2. Build the pg Pool + Drizzle db. + * 3. `DBOS.setConfig(...)`. + * 4. `DBOS.launch()` — register-mark closes; the scheduler starts firing. + */ +import { DBOS } from '@dbos-inc/dbos-sdk'; +import { DrizzleDataSource } from '@dbos-inc/drizzle-datasource'; +import { schema } from '@novelpad/outreach-core'; +import { drizzle } from 'drizzle-orm/node-postgres'; +import pg from 'pg'; + +// Importing these modules registers `ingestGrants` (nightly) and +// `expireGrants` (hourly) as DBOS workflows + scheduled functions as a side +// effect of module evaluation. Must happen before `DBOS.launch()` below. Each +// module also exports a `set*Deps` injector — DBOS serializes +// scheduled-function args, so the `db` handle can't be passed through the +// scheduler; it's threaded in via this module-scope registry instead (same +// pattern as novelpad-desktop's `setStartDeps`). +import { setExpireGrantsDeps } from './workflows/expire-grants.js'; +import { setIngestGrantsDeps } from './workflows/ingest-grants.js'; + +if (process.env.DATABASE_URL == null) { + throw new Error('outreach-worker: DATABASE_URL is required'); +} + +const { Pool } = pg; +const dbosClientConfig = { + connectionString: process.env.DATABASE_URL, + application_name: 'helmdocs-outreach-worker', +} satisfies pg.ClientConfig; +const pool = new Pool({ + ...dbosClientConfig, + max: Number(process.env.PG_POOL_MAX ?? 20), +}); + +export const db = drizzle(pool, { schema }); + +async function main() { + // DBOS system schema lives in this same database; initialize it with the + // same connection settings as the worker pool. + await DrizzleDataSource.initializeDBOSSchema(dbosClientConfig); + + // Inject the shared `db` handle into each scheduled workflow's deps + // registry before launch, so the first scheduled tick (which may fire + // immediately on an `ExactlyOncePerInterval` catch-up) always has it. + setIngestGrantsDeps({ db }); + setExpireGrantsDeps({ db }); + + DBOS.setConfig({ + name: 'helmdocs-outreach-worker', + systemDatabasePool: pool, + runAdminServer: false, + }); + await DBOS.launch(); + + console.log('[outreach-worker] launched; scheduled workflows active'); +} + +main().catch((err) => { + console.error('[outreach-worker] fatal:', err); + process.exit(1); +}); diff --git a/apps/outreach-worker/src/workflows/expire-grants.ts b/apps/outreach-worker/src/workflows/expire-grants.ts new file mode 100644 index 0000000..3515447 --- /dev/null +++ b/apps/outreach-worker/src/workflows/expire-grants.ts @@ -0,0 +1,70 @@ +/** + * Hourly grant expiry sweep. + * + * Marks grants whose close date has passed as closed so they drop out of the + * active match/scoring pool. See `ingest-grants.ts` for the registration + * pattern this mirrors (deps-registry + dual workflow/scheduled registration). + */ +import { DBOS, SchedulerMode } from '@dbos-inc/dbos-sdk'; +import type { schema } from '@novelpad/outreach-core'; +import { serverExpireClosedGrants } from '@novelpad/outreach-core/server'; +import type { NodePgDatabase } from 'drizzle-orm/node-postgres'; + +export type OutreachDb = NodePgDatabase; + +export interface ExpireGrantsDeps { + readonly db: OutreachDb; +} + +let registeredDeps: ExpireGrantsDeps | null = null; + +export function setExpireGrantsDeps(deps: ExpireGrantsDeps): void { + registeredDeps = deps; +} + +function getExpireGrantsDeps(): ExpireGrantsDeps { + if (registeredDeps == null) { + throw new Error( + 'ExpireGrantsDeps not registered. Call setExpireGrantsDeps() before DBOS.launch().', + ); + } + return registeredDeps; +} + +async function expireClosedGrants(db: OutreachDb): Promise { + await serverExpireClosedGrants(db); +} +const expireClosedGrantsStep = DBOS.registerStep(expireClosedGrants, { + name: 'expireClosedGrants', + retriesAllowed: true, + maxAttempts: 3, +}); + +async function runExpireGrants(): Promise { + const { db } = getExpireGrantsDeps(); + await expireClosedGrantsStep(db); +} + +const g = globalThis as unknown as { + __outreachExpireGrantsRegistered?: boolean; +}; + +if (!g.__outreachExpireGrantsRegistered) { + g.__outreachExpireGrantsRegistered = true; + + const expireGrants = async (_scheduledTime: Date, _startedAt: Date) => { + try { + await runExpireGrants(); + } catch (err) { + console.error('[expire-grants] pass failed:', err); + throw err; + } + }; + + DBOS.registerWorkflow(expireGrants, { name: 'expireGrants' }); + DBOS.registerScheduled(expireGrants, { + crontab: '0 * * * *', + name: 'expireGrants', + mode: SchedulerMode.ExactlyOncePerInterval, + }); +} diff --git a/apps/outreach-worker/src/workflows/ingest-grants.ts b/apps/outreach-worker/src/workflows/ingest-grants.ts new file mode 100644 index 0000000..c7cdd25 --- /dev/null +++ b/apps/outreach-worker/src/workflows/ingest-grants.ts @@ -0,0 +1,137 @@ +/** + * Nightly grant ingestion workflow. + * + * Pulls open grant opportunities from upstream sources (currently just + * Grants.gov; NH state postings and 990-PF extracts land as additional + * fetch+normalize steps later) and upserts them into the outreach schema. + * + * Registration follows the pattern established in novelpad-desktop's + * `packages/core/src/workflow/dbos/register-readiness-reconcile.server.ts`: + * the scheduled function must ALSO be registered as a plain workflow (both + * registrations referencing the same function object), and deps are pulled + * from a module-scope registry populated before `DBOS.launch()` — DBOS + * serializes workflow args, so closures/functions can't cross that boundary. + */ +import { DBOS, SchedulerMode } from '@dbos-inc/dbos-sdk'; +import type { schema } from '@novelpad/outreach-core'; +import { + serverInsertGrants, + type NewGrantInput, +} from '@novelpad/outreach-core/server'; +import type { NodePgDatabase } from 'drizzle-orm/node-postgres'; + +export type OutreachDb = NodePgDatabase; + +/** Shape of a single opportunity as returned by the Grants.gov Search2 API. */ +export interface RawGrantsGovOpportunity { + readonly opportunityId: string; + readonly opportunityNumber: string; + readonly opportunityTitle: string; + readonly agencyCode: string | null; + readonly openDate: string | null; + readonly closeDate: string | null; + readonly awardCeiling: number | null; + readonly awardFloor: number | null; +} + +export interface IngestGrantsDeps { + readonly db: OutreachDb; +} + +let registeredDeps: IngestGrantsDeps | null = null; + +export function setIngestGrantsDeps(deps: IngestGrantsDeps): void { + registeredDeps = deps; +} + +function getIngestGrantsDeps(): IngestGrantsDeps { + if (registeredDeps == null) { + throw new Error( + 'IngestGrantsDeps not registered. Call setIngestGrantsDeps() before DBOS.launch().', + ); + } + return registeredDeps; +} + +/** + * Fetch open opportunities from Grants.gov. + * + * Real implementation calls the Grants.gov Search2 API + * (`POST https://api.grants.gov/v1/api/search2`, JSON body with + * `oppStatuses: "posted"` and pagination via `rows`/`startRecordNum`). Stubbed + * to an empty array until that integration lands. + */ +async function fetchGrantsGov(): Promise> { + // TODO(outreach): call Grants.gov Search2 API and paginate through results. + return []; +} +const fetchGrantsGovStep = DBOS.registerStep(fetchGrantsGov, { + name: 'fetchGrantsGov', + retriesAllowed: true, + maxAttempts: 3, +}); + +function normalize( + raw: ReadonlyArray, +): ReadonlyArray { + return raw.map((opp) => ({ + // Stable identity across re-crawls; also the schema's upsert key. + sourceUrl: `https://www.grants.gov/search-results-detail/${opp.opportunityId}`, + source: 'grants_gov' as const, + title: opp.opportunityTitle, + funder: opp.agencyCode ?? 'Unknown federal agency', + openDate: opp.openDate == null ? null : new Date(opp.openDate), + closeDate: opp.closeDate == null ? null : new Date(opp.closeDate), + // Schema stores whole dollars (award amounts are never sub-dollar). + awardFloor: opp.awardFloor == null ? null : Math.round(opp.awardFloor), + awardCeiling: opp.awardCeiling == null ? null : Math.round(opp.awardCeiling), + lastVerifiedAt: new Date(), + })); +} + +async function upsertGrants( + db: OutreachDb, + grants: ReadonlyArray, +): Promise { + if (grants.length === 0) return; + await serverInsertGrants(db, grants); +} +const upsertGrantsStep = DBOS.registerStep(upsertGrants, { + name: 'upsertGrants', + retriesAllowed: true, + maxAttempts: 3, +}); + +async function runIngestGrants(): Promise { + const { db } = getIngestGrantsDeps(); + + const raw = await fetchGrantsGovStep(); + const normalized = normalize(raw); + await upsertGrantsStep(db, normalized); +} + +const g = globalThis as unknown as { + __outreachIngestGrantsRegistered?: boolean; +}; + +if (!g.__outreachIngestGrantsRegistered) { + g.__outreachIngestGrantsRegistered = true; + + const ingestGrants = async (_scheduledTime: Date, _startedAt: Date) => { + try { + await runIngestGrants(); + } catch (err) { + console.error('[ingest-grants] pass failed:', err); + throw err; + } + }; + + // Must be registered as BOTH a workflow and a scheduled function, + // referencing the same function object — see module doc comment. + DBOS.registerWorkflow(ingestGrants, { name: 'ingestGrants' }); + DBOS.registerScheduled(ingestGrants, { + crontab: '0 3 * * *', + name: 'ingestGrants', + mode: SchedulerMode.ExactlyOncePerInterval, + }); +} diff --git a/apps/outreach-worker/tsconfig.json b/apps/outreach-worker/tsconfig.json new file mode 100644 index 0000000..d6b7ce9 --- /dev/null +++ b/apps/outreach-worker/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "@novelpad/config/tsconfig.base.json", + "compilerOptions": { + "outDir": "./build", + "rootDir": "./src", + "module": "ESNext", + "moduleResolution": "Bundler", + "target": "ES2022", + "lib": ["ES2022"], + "types": ["node"], + "noEmit": true, + "paths": { + "#~/*": ["./src/*"] + } + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "build"] +} diff --git a/docs/plan.md b/docs/plan.md new file mode 100644 index 0000000..1f578ee --- /dev/null +++ b/docs/plan.md @@ -0,0 +1,256 @@ +# HelmDocs Grant-Match Outreach Engine — Solution Architecture & Campaign Plan + +**Goal:** An automated pipeline that matches open grants to NH nonprofits, then runs a personalized email drip whose success metric is a booked demo call with high purchase intent. + +**Core thesis of the funnel:** The grant match *is* the lead magnet. You are not cold-emailing a pitch — you are cold-emailing a found asset ("here is $480K you're well positioned for"). The software sale rides on the credibility of that match. This means the single most important engineering requirement in the whole system is **match accuracy**. One email claiming a grant the org isn't eligible for burns the sender domain's reputation *and* the brand's. Every architectural decision below flows from that constraint. + +--- + +## System Overview + +``` +┌─────────────────┐ ┌──────────────────┐ ┌──────────────────┐ +│ Grant Ingestion │ │ Nonprofit Intake │ │ Contact Sourcing │ +│ (Grants.gov API,│ │ (NHDOJ PDF + │ │ (Apollo API + │ +│ 990-PFs, RFP │ │ IRS BMF/990 │ │ email verifier) │ +│ feeds, state) │ │ enrichment) │ └────────┬─────────┘ +└────────┬────────┘ └────────┬─────────┘ │ + │ │ │ + ▼ ▼ │ +┌─────────────────┐ ┌──────────────────┐ │ +│ Grant Store │ │ Org Profiler │ │ +│ Postgres + │ │ (web research → │ │ +│ pgvector │ │ structured │ │ +│ (hybrid: SQL │ │ profile w/ │ │ +│ facts + vector │ │ citations) │ │ +│ mission text) │ └────────┬─────────┘ │ +└────────┬────────┘ │ │ + │ │ │ + └──────────┬────────────┘ │ + ▼ │ + ┌──────────────────────┐ │ + │ Scoring Engine │ │ + │ (deterministic gates │ │ + │ + LLM fit subscores)│ │ + └──────────┬───────────┘ │ + ▼ │ + ┌──────────────────────┐ │ + │ Human Review Queue │◄────────────────────────┘ + │ (approve match + │ + │ contact before send)│ + └──────────┬───────────┘ + ▼ + ┌──────────────────────┐ ┌─────────────────┐ + │ Apollo Sync Worker │─────►│ Google Calendar │ + │ → Apollo sequences │ │ booking + events │ + │ (send via our GWS │ │ table + feedback │ + │ lookalike inboxes) │ │ loop to scoring │ + └──────────────────────┘ └─────────────────┘ +``` + +--- + +## Stage 1 — Grant Corpus Ingestion + +**What it does:** Continuously pulls open grant opportunities from structured sources, normalizes them into one schema, and expires them the moment they close. + +**Sources, in priority order:** + +| Source | Access | Why it's in scope | +|---|---|---| +| Grants.gov Search2 API | Free, official REST API | Canonical federal source; structured JSON with eligibility codes, CFDA/ALN numbers, open/close dates. Zero scraping risk. | +| NH state agency portals (DHHS, DOE, DNCR, CDFA, Charitable Foundation of NH) | Mostly scrape/RSS; some post to Grants.gov | Geographic fit is your strongest scoring signal — a NH-only grant pool is dramatically less competitive than federal, which is exactly the "easy win" profile. | +| IRS 990-PF filings (ProPublica Nonprofit Explorer API / IRS bulk data) | Free | Private foundations must disclose every grant they made. This gives you *historical funder behavior*: a foundation that gave $40K to three NH youth-services orgs last year is a near-certain match for a fourth. Most foundation grants have no public RFP — this is how you find them anyway. | +| Candid Foundation Directory or Instrumentl | Paid (~$200–$400/mo) | Pre-structured private-foundation RFPs. Buy this instead of scraping GrantWatch-style aggregators — their ToS prohibit it and their data is secondhand anyway. | +| Philanthropy News Digest RFP feed | Free RSS | Cheap incremental coverage of open private RFPs. | + +**Normalized grant schema (the contract everything downstream depends on):** `funder`, `title`, `synopsis`, `eligibility_entity_types`, `geographic_scope`, `program_areas` (mapped to NTEE codes), `award_floor/ceiling`, `expected_awards_count`, `open_date`, `close_date`, `match_requirement`, `application_effort_estimate` (LLM-classified: LOI-only / short form / full federal), `source_url`, `last_verified_at`. + +**Storage: Postgres with pgvector — not a pure RAG store.** Reasoning: eligibility, geography, and deadlines are *hard constraints*, and hard constraints must be enforced by deterministic SQL filters, not by semantic retrieval that might surface a closed or ineligible grant because the text "felt similar." Vector embeddings are used only for the one genuinely fuzzy dimension — mission/program fit between grant synopsis and org profile. Hybrid retrieval: SQL WHERE clauses gate, vectors rank. + +**Freshness:** Nightly ingestion cron; a separate hourly sweep marks anything past `close_date` as expired; every grant that reaches the outreach stage gets a `last_verified_at` re-check within 48 hours of send. Justification: the entire pitch is time-sensitive money. Emailing a dead deadline is worse than not emailing. + +--- + +## Stage 2 — Scoring Engine + +**What it does:** For every (org, grant) pair that survives the hard gates, produces a 0–100 score plus a machine-readable rationale, and flags the "easy win" subset. + +**Two-layer design — deterministic gates, then weighted subscores:** + +*Layer 1, hard gates (SQL, non-negotiable):* entity type eligible (501(c)(3) in good standing), geography matches, deadline ≥ 21 days out, award ceiling ≥ a floor that makes the email non-trivial (e.g., $10K+). + +*Layer 2, weighted subscores (0–100):* + +| Signal | Weight | How it's computed | +|---|---|---| +| Mission/program fit | 30 | Embedding similarity between grant synopsis and org profile, *validated* by an LLM judge that must cite which org program matches which grant priority. Similarity alone false-positives on vocabulary overlap. | +| Funder precedent | 25 | From 990-PF history: has this funder given to orgs of this NTEE code / size / geography before? Strongest single predictor of foundation success. | +| Capacity fit | 15 | Award size vs. org revenue (from 990). A $480K federal grant to a $150K/yr org is a red flag, not a hook — federal funders screen for capacity, and savvy grant writers know it. Sweet spot: award is 10–75% of annual revenue. | +| Competition level | 15 | expected_awards ÷ estimated applicant pool; state/local and NH-restricted pools score high. | +| Effort-to-award ratio | 10 | LOI or short-form apps score high. This is also your product tie-in: "20 minutes in HelmDocs" must be plausible for the specific grant you cite. | +| Deadline runway | 5 | 3–10 weeks is ideal: urgent enough to act, long enough to realistically apply. | + +**"Easy win" flag** = total ≥ 75 AND funder-precedent ≥ threshold AND effort subscore ≥ threshold. These are the only matches that go into Email 1. + +**Why deterministic weights wrapped around LLM subscores, instead of "ask the agent to score it":** reproducibility (you can re-run and audit any score), tunability (when demo-booking data comes back, you adjust weights, not prompts), and defensibility (every claim in an outbound email traces to a scored fact with a source URL). The LLM contributes judgment inside narrow, checkable subtasks; it never gets to invent the final number. + +--- + +## Stage 3 — NH Nonprofit Intake + +**What it does:** Parses the NHDOJ Charitable Trusts registry PDF into the org master table, then enriches every org with IRS data. + +The NHDOJ PDF gives you name, registration status, and city — enough to establish "good standing," which matters because it's a hard eligibility gate and because emailing lapsed orgs wastes sends. Parse with pdfplumber or Camelot (it's a tabular layout); expect to handle multi-line org names and re-run when NHDOJ republishes (roughly monthly — diff against the prior run so new registrants enter the pipeline automatically; new registrants are often actively seeking first-time funding, which is a great segment). + +**Enrichment via ProPublica Nonprofit Explorer API (free) keyed on name+city fuzzy match → EIN:** pulls NTEE code (your program-area taxonomy for matching), total revenue (capacity scoring), fiscal year end (budget-cycle timing for outreach), and officer names (contact sourcing seed). Orgs that don't resolve to an EIN or have no 990 on file get down-prioritized rather than dropped — many small orgs file 990-N postcards and are still viable prospects, just with a lower-confidence profile. + +**Segmentation:** revenue $100K–$5M is the primary ICP band. Below that, orgs rarely have anyone in a grant-writing seat; above it, they have professional development staff with entrenched tools (still reachable, but a different message — segment them out rather than mixing). + +--- + +## Stage 4 — Org Profiling (Research Agent) + +**What it does:** For each org in the ICP band, an agent runs web research (search API + site scrape) and produces a *structured, citation-backed* profile. + +**Profile schema:** `mission_statement`, `programs[]` (name, description, population served), `service_geography`, `recent_news[]`, `known_funders[]` (from annual reports, website funder walls, and other funders' 990 disclosures), `staff[]` (ED, development/grants roles), `budget_band`, plus `sources[]` — every field carries the URL it came from. + +**Why citations are mandatory, not nice-to-have:** two downstream consumers depend on facts about the org being true. The scoring engine's mission-fit judgment is only as good as the profile, and Email 1's personalization line ("given your after-school STEM program in Nashua…") is the credibility hook of the whole campaign. A wrong personalization detail reads as spam-with-extra-steps and gets you marked as such. The profiler should emit a confidence field, and low-confidence profiles route to the human review queue rather than to the sequencer. + +**Cost control:** run profiling *after* a cheap pre-match pass. Don't deep-research all ~10K registered NH charities; first do a coarse match on NTEE code + geography against the grant corpus, and only profile the ~500–1,500 orgs that have at least one plausible grant. Research is the expensive stage (LLM tokens + search API calls); matching gates it. + +--- + +## Stage 5 — Contact Sourcing + +**What it does:** Attaches a verified decision-maker email to each org. + +**Recommendation: Apollo API (or Clay orchestrating Apollo + Hunter) — not direct scraping.** Three reasons. First, deliverability: scraped addresses have bounce rates that will torch a new sending domain within a week; licensed providers pre-verify. Second, legal: CAN-SPAM explicitly aggravates penalties for addresses harvested by automated means from websites, and many nonprofit sites' ToS prohibit it — for a product selling *to* compliance-minded grant professionals, that's a bad foundation. Third, titles: you need role targeting (Executive Director, Development Director, Grants Manager), which scrapers can't reliably give you. + +Waterfall: Apollo person-search by org domain + title → fall back to the officer names from the 990 → last resort, the org's published contact address (generic `info@` goes to a separate low-priority sequence with softer copy, since you don't know who's reading). Every address passes through a verifier (NeverBounce/ZeroBounce); anything below "valid" is excluded. Target: hard-bounce rate under 2%. + +--- + +## Stage 6 — Match Pipeline & Human Review + +**What it does:** Joins scored matches to verified contacts, and gates every send behind a lightweight approval step. + +Each org gets exactly one hero grant (its top-scoring easy win) and up to three runner-up grants held in reserve for Email 2. The review queue (a simple Retool/Next.js table is enough) shows: org profile summary, hero grant, score breakdown with the LLM's cited rationale, the rendered Email 1, and approve/reject/edit buttons. At launch, review 100%; once approval rate exceeds ~95% for two weeks, drop to sampling 20% plus auto-flagged cases (low profile confidence, deadline <30 days, award >$250K). + +**Why a human gate on an "automated" pipeline:** the failure mode isn't average quality, it's tail risk — one confidently wrong match forwarded around the tight-knit NH nonprofit community (and it is tight-knit) does more damage than 50 good emails do good. The gate costs ~20 seconds per lead and caps that risk. It's also your labeling function: every reject reason becomes training signal for scoring weights. + +--- + +## Stage 7 — The Drip Campaign + +**Sending infrastructure first, because copy doesn't matter if you land in spam:** buy 2–3 lookalike domains (e.g., helmdocs-app.com, tryhelmdocs.com) — never the primary domain. Full SPF/DKIM/DMARC on each, 2–3 Google Workspace inboxes across them, connected to Apollo via OAuth (Apollo sequences send through the connected mailbox, so reputation stays ours). Run a dedicated warmup tool on each inbox for 3–4 weeks before the first cold send, then ramp from ~20 to a ceiling of ~50 sends/inbox/day and enroll in weekly cohorts of 50–100. Email 1 should be plain text, no images, no tracking-heavy footers, and at most one link. NH's prospect pool is small enough (~1,000–1,500 ICP orgs) that this modest throughput covers the whole state in weeks — a feature, not a limitation, because it forces quality. + +**One compliance-critical copy change to your draft:** "You can secure $480,000" is a claim of outcome you can't guarantee, and it pattern-matches to advance-fee grant scams that nonprofits are specifically warned about. Reframe every instance as *fit*, not *promise*: "a $480K grant your org appears unusually well-positioned for." Same dopamine, none of the deception exposure (FTC/state UDAP), and it reads as more credible to a professional audience, not less. Also table stakes: physical mailing address in the footer, working one-click unsubscribe, honest subject lines, suppression list honored across all domains. + +**The sequence (4 touches, days 0 / 3 / 8 / 14):** + +| # | Day | Job of the email | Skeleton | +|---|---|---|---| +| 1 | 0 | Deliver the found asset. Sell nothing. | Subject: `{grant_name} — deadline {date}` · "Hi {first} — we track open grant funding for NH nonprofits. Based on {specific program, cited from their site}, {org} looks unusually well-positioned for {funder}'s {grant_name} — up to {amount}, {effort_level} application, closes {date}. Want the one-page brief on why it's a fit? Just reply 'send it.'" | +| 2 | 3 | Prove it's a system, not a fluke. | "That grant wasn't a one-off — our engine found {n} more open opportunities matching {org}'s programs, worth {total} combined. Here are the top two: … The full list takes 15 min to walk through: {calendar link}." | +| 3 | 8 | Bridge from *finding* money to *winning* it. Introduce the product via the free pilot. | "Finding the money is half the battle — {grant_name}'s application is {n} sections, and most teams lose 20+ hours to it. HelmDocs drafts it from documents you already have. {Beta user quote}. We're running free pilots for a handful of NH nonprofits: bring one grant — I'd suggest {grant_name} — and we'll put the full application through HelmDocs together. You keep the draft either way. 15 min to set it up: {link}" | +| 4 | 14 | Deadline-anchored breakup. Real urgency only. | "{grant_name} closes {close_date} — after that I'll stop emailing about it. The pilot offer stands until then: one working session, you leave with a full working draft, no commitment after. {link}. If this quarter's full, here's the one-page brief to file for later: {link}. Either way, good luck with it." | + +**Why this order works for the "high intent" success criterion:** Email 1's CTA is a *reply*, not a meeting — replies train inbox providers that you're wanted (deliverability) and self-select engaged leads. The demo ask only appears once the prospect has received real value twice, so the people who book in Emails 2–4 arrive pre-sold on the premise (there's money on the table, applying is the bottleneck, this tool removes it). That's what makes the demo high-intent rather than curiosity-driven. The urgency in Email 4 is real — the deadline is the funder's, not yours — which matters because grant professionals are professionally skeptical of manufactured scarcity. + +**The pilot offer (the campaign's commitment mechanism):** the post-demo motion is a free pilot — one grant application put through HelmDocs end to end. Frame the call itself around it: not "book a demo" but "bring {grant_name}, leave with a draft." This does four things. It converts the call from a vendor pitch into a working session the prospect has a selfish reason to attend; it aligns urgency with a real external deadline instead of manufactured scarcity; "you keep the draft either way" removes all downside and reads as confidence in the product; and the conversion moment becomes the draft materializing on screen — the product sells itself at the exact second the prospect's 20-hour problem visibly shrinks to 20 minutes. Cap the pilot scope explicitly (one application, one working session plus async review) so it doesn't turn into free consulting. + +**Proof posture with beta-stage evidence:** you have beta user quotes, not case studies — use them honestly and specifically. One concrete quote with a real detail ("it drafted our LOI narrative in one sitting — Development Director, youth services org") outperforms an inflated aggregate claim, and this audience fact-checks. Never invent metrics; once the first pilots convert, their before/after time-to-submit becomes Email 3's case study and the quotes move to the landing page. + +**A new hard gate this creates upstream:** because Emails 3–4 promise HelmDocs can draft *the specific grant being dangled*, the scoring engine's Layer 1 gates gain a fourth condition: `application_form_supported = true` for the hero grant. Only dangle grants the product can genuinely draft today — a pilot session that stalls on an unsupported form converts the highest-intent moment in the funnel into a refund of trust. Runner-up grants in Email 2 don't need this gate; the hero does. + +**Behavioral branches:** reply → Apollo auto-pauses the enrollment and a human takes over; the one-page grant brief goes out within the hour via SendGrid from the primary domain (speed-to-brief is your conversion moment). Booked call → Calendar webhook logs it, contact is removed from the sequence and suppressed. (Apollo's sequencer doesn't support click-triggered branching — e.g., sending Email 3 early on a link click — which is an acceptable loss at this volume; revisit only if the funnel stalls between Emails 2 and 3.) + +--- + +## Final Email Copy (v1, ready for merge fields) + +**Email 1 — Day 0 · Subject: `{grant_name} — closes {close_date}`** + +> Hi {first_name}, +> +> I run HelmDocs — we track every open grant available to New Hampshire nonprofits. Based on {org_short}'s {specific_program, cited from their site}, you look unusually well-positioned for {funder}'s **{grant_name}**: up to {amount}, {effort_descriptor} application, closes {close_date}. +> +> Want the one-page brief on why it's a fit? Reply "send it" and I'll get it over today. +> +> {sender_name} +> HelmDocs · {physical address} · {unsubscribe} + +**Email 2 — Day 3 · Subject: `{n} more for {org_short}`** + +> The {grant_name} match wasn't a one-off — our engine found {n} more open grants that fit {org_short}'s work, worth {combined_total} combined. The top two: +> +> {grant_2} — up to {amount_2}, closes {date_2} +> {grant_3} — up to {amount_3}, closes {date_3} +> +> Happy to walk you through the full list in 15 minutes: {calendar_link} + +**Email 3 — Day 8 · Subject: `the {grant_name} application, drafted`** + +> Finding the money is half the battle. {grant_name}'s application runs {n} sections, and most teams lose 20+ hours to it. +> +> That's the part HelmDocs handles: you upload documents you already have — your 990, program descriptions, past proposals — and it drafts the application. One of our early users put it this way: "{beta_quote}" ({role}, {org_type}). +> +> We're running free pilots for a handful of NH nonprofits right now. Bring one grant — I'd suggest {grant_name} — and we'll put the full application through HelmDocs together. You keep the draft either way. +> +> 15 minutes to set it up: {calendar_link} + +**Email 4 — Day 14 · Subject: `{grant_name} closes {close_date}`** + +> {grant_name} closes {close_date} — after that I'll stop emailing about it. +> +> If you want the application drafted before the deadline, the pilot offer stands: one working session, you leave with a full working draft, no commitment after. {calendar_link} +> +> If this quarter's already full, here's the one-page brief to file for later: {brief_link} +> +> Either way — good luck with it. + +Copy rules baked in: plain text; one link in Email 1 (none until the CTA); fit-language, never funding promises; every personalization token traces to a cited profile field; all merge fields are static values precomputed at enrollment (Apollo can't compute at send time — hence dates, not day counts); the only urgency is the funder's real deadline. + +--- + +## Stage 8 — Measurement & Feedback Loop + +Instrument the funnel end to end: deliverability (bounce <2%, spam complaints <0.1%), engagement (reply rate on Email 1 is the campaign's single most diagnostic number — target 5–8% for this level of personalization; below 3% means the match quality or the hook is off), conversion (brief requested → demo booked → show rate), and revenue (demo → trial → paid). + +The strategic loop: log the hero grant's score components for every lead, then regress demo bookings against them monthly. If funder-precedent-heavy matches book demos at 2x the rate of mission-fit-heavy matches, reweight the engine. The scoring engine isn't a static tool — it's the thing the campaign is continuously teaching. + +Second-order asset: every "reply 'send it'" response and every demo transcript tells you which grants NH nonprofits actually care about. That's product intelligence for HelmDocs itself (which application forms to template first) — feed it back. + +--- + +## Build Plan & Stack + +| Phase | Weeks | Deliverable | +|---|---|---| +| 1. Data spine | 1–2 | Postgres+pgvector schema; Grants.gov + PND ingestion; NHDOJ PDF parser; ProPublica enrichment | +| 2. Intelligence | 3–4 | Org profiler agent; scoring engine v1 with hard gates + weighted subscores; 990-PF funder-precedent index | +| 3. Pipeline | 5 | Apollo contact integration (API or CSV adapter); review queue UI (Next.js); Apollo sequence templates + sync worker (push enrollments, pull engagement events) | +| 4. Launch | 6 | Lookalike domains + Workspace inboxes warmed via dedicated warmup tool (start this in week 1!); connect inboxes to Apollo via OAuth; 100-lead pilot cohort, 100% human-reviewed; instrument dashboards from `pipeline_events` | +| 5. Scale | 7+ | Ramp volume; monthly weight tuning; add Candid/Instrumentl feed; expand beyond NH by swapping the Stage 3 source | + +**Stack (revised for TS + existing subscriptions):** + +| Layer | Choice | Notes | +|---|---|---| +| Language/runtime | TypeScript, Node 20+, pnpm monorepo | Packages: `ingest`, `profiler`, `scoring`, `sequencer`, `review-ui`, `shared` (schema + types) | +| Database | Postgres + pgvector, Drizzle ORM | Drizzle has first-class pgvector column support; one DB holds grants, orgs, matches, contacts, and pipeline events | +| Job orchestration | pg-boss | Postgres-backed job queue — cron schedules, retries, and dead-letter handling with zero extra infra (no Redis) | +| LLM access | Vercel AI SDK as the multi-provider connector | One `generateText`/`generateObject` interface; providers swap per task. LM Studio exposes an OpenAI-compatible endpoint, so `@ai-sdk/openai-compatible` pointed at `localhost:1234/v1` covers local models; `@ai-sdk/anthropic` (API key) covers frontier calls | +| Model routing | Tiered by task | Local (LM Studio): bulk classification, effort estimates, first-pass profile extraction, embeddings via `/v1/embeddings`. Frontier API: mission-fit judge, email-personalization QA — the two places where a wrong answer reaches a prospect. Note: Claude *subscriptions* are for interactive use; a production pipeline like this should authenticate with a Claude Platform API key (pay-as-you-go) | +| Embeddings | One local embedding model via LM Studio, dimension fixed in the pgvector column | Never mix embedding models — vectors from different models aren't comparable; re-embed everything if you ever switch | +| Contacts | Apollo (existing sub) | If the plan includes API access (Settings → Integrations → API), use it; otherwise the pipeline ingests Apollo CSV exports from a watched folder — same schema either way, API becomes a drop-in upgrade. Verification deferred (NeverBounce slot reserved in the contact module interface) | +| Sending | Apollo sequences sending through our own Google Workspace inboxes (lookalike domains) connected via OAuth | Apollo is the scheduler, not the infrastructure: it sends through whatever mailbox is connected, so domain reputation stays ours and portable. **Never** use Apollo's native/purchased mailboxes or domains — shared infrastructure, shared reputation. Built-in reply detection auto-pauses a contact's enrollment (our reply→human branch), plus per-mailbox rolling 24h throttling and opt-out handling. Warmup is *not* covered — use a dedicated warmup tool (~$25/mo) for the first month per inbox and hold the 30–50/day cap after. SendGrid stays for transactional mail only (brief delivery after a reply, pilot confirmations) from the primary domain | +| Sequencer package | Shrinks to an Apollo sync worker | Outbound: push approved matches as contacts with precomputed custom fields (hero grant name, amount, close date, personalization line) into the right sequence, enrolled in weekly cohorts of 50–100 (large enrollment spikes trigger spam filters). Inbound: pull engagement events (opens/replies/bounces per contact) into `pipeline_events` — API if the plan has it, else weekly CSV export through the same watched-folder adapter as contacts. Constraint: Apollo merge fields are simple substitution with no conditional logic or send-time computation, so every field must be precomputed at enrollment | +| Scheduling | Google Calendar appointment schedules | Booking link as the sequence CTA; a Calendar webhook (or daily poll) writes bookings into pipeline events | +| Tracking | `pipeline_events` table in Postgres + a CSV export script | Same effort as raw CSV, but joinable against scores for the feedback loop; export keeps the CSV workflow | +| Review queue | Self-built Next.js app on the same Drizzle schema | One screen: match card (org profile, hero grant, score breakdown w/ cited rationale, rendered Email 1), approve / edit / reject-with-reason. Reject reasons are an enum — they become the labels for tuning scoring weights | + +**SendGrid caution (important):** SendGrid's acceptable-use policy prohibits unsolicited email and purchased/sourced lists, and cold outreach on its shared IPs both risks account suspension and inherits the reputation of every other sender on the pool. It's built for transactional and opted-in marketing mail, which is the opposite of this workload. Cold sends therefore run through Apollo sequences → our own Google Workspace inboxes on lookalike domains (2–3 inboxes, ≤50 sends/day each — ample for ~1,500 prospects statewide). SendGrid keeps the job it's good at: transactional sends from the primary domain (brief-delivery emails after a reply, pilot confirmations). + +**Deliberate scope exclusions:** no LinkedIn automation at launch (adds channel risk before email is proven), no fully-autonomous sending (the human gate stays until the data earns its removal), and no expansion past NH until reply rate ≥5% — the model must work in a market small enough to hand-inspect before it's pointed at one that isn't. diff --git a/package.json b/package.json new file mode 100644 index 0000000..2a079da --- /dev/null +++ b/package.json @@ -0,0 +1,32 @@ +{ + "name": "helmdocs-grant-outreach-engine", + "packageManager": "yarn@4.5.0", + "private": true, + "scripts": { + "dev": "turbo dev", + "build": "turbo build", + "test": "turbo test", + "typecheck": "turbo typecheck", + "format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,md,yaml,yml}\"" + }, + "workspaces": { + "packages": [ + "apps/*", + "packages/*" + ] + }, + "devDependencies": { + "prettier": "^3.6.2", + "prettier-plugin-organize-imports": "^4.3.0", + "turbo": "^2.1.2", + "typescript": "^5.9.3" + }, + "resolutions": { + "drizzle-orm": "0.44.6", + "@types/pg": "8.20.0", + "pg": "8.20.0" + }, + "volta": { + "node": "22.16.0" + } +} diff --git a/packages/config/README.md b/packages/config/README.md new file mode 100644 index 0000000..c953e06 --- /dev/null +++ b/packages/config/README.md @@ -0,0 +1 @@ +# tsconfig diff --git a/packages/config/package.json b/packages/config/package.json new file mode 100644 index 0000000..3befde7 --- /dev/null +++ b/packages/config/package.json @@ -0,0 +1,21 @@ +{ + "name": "@novelpad/config", + "packageManager": "yarn@4.5.0", + "dependencies": { + "@fontsource-variable/bricolage-grotesque": "^5.2.10", + "@fontsource-variable/lora": "^5.2.8", + "@fontsource-variable/outfit": "^5.2.8", + "@fontsource-variable/source-serif-4": "^5.2.9", + "@fontsource/courier-prime": "^5.2.8", + "@fontsource/dm-serif-display": "^5.2.8", + "@fontsource/inter": "^5.2.8", + "@fontsource/libre-baskerville": "^5.2.8" + }, + "devDependencies": { + "@tailwindcss/container-queries": "^0.1.1", + "@tailwindcss/typography": "^0.5.19", + "tailwindcss": "^3.4.13", + "tailwindcss-animate": "^1.0.7", + "vite": "^5.4.11" + } +} diff --git a/packages/config/shadcn.css b/packages/config/shadcn.css new file mode 100644 index 0000000..2c13d3a --- /dev/null +++ b/packages/config/shadcn.css @@ -0,0 +1,710 @@ +@import '@fontsource/inter'; +@import '@fontsource-variable/bricolage-grotesque'; +@import '@fontsource-variable/outfit'; +@import '@fontsource-variable/source-serif-4'; +@import '@fontsource-variable/lora'; +@import '@fontsource/courier-prime'; +@import '@fontsource/dm-serif-display'; +/* Libre Baskerville — a user-selectable editor font (font-family-dropdown.tsx), + self-hosted so it keeps working offline after the Google-Fonts CDN removal. */ +@import '@fontsource/libre-baskerville'; + +@font-face { + font-family: 'Fenwick'; + src: url('/Fenwick-Bold.woff2') format('woff2'); + font-weight: bold; + font-style: normal; + font-display: swap; +} + +/* ── Dialog system surfaces (brand-agnostic; spec: docs/redesign/dialogs) ── + The dialog surface is intentionally NOT --background: it matches the elevated + floating-toolbar / popover color so modals read as the top layer. */ +:root { + --np-dialog-surface: #ffffff; + --np-dialog-line: rgba(28, 26, 21, 0.1); + --np-scrim: rgba(28, 26, 21, 0.34); + --np-dialog-shadow: 0 32px 90px rgba(28, 26, 21, 0.24), 0 2px 8px rgba(28, 26, 21, 0.1); + /* Popover-tier elevation — lighter than the dialog shadow. Shared by + popover/dropdown/select content for a consistent floating-surface feel. */ + --np-popover-shadow: 0 8px 24px rgba(28, 26, 21, 0.12), 0 2px 6px rgba(28, 26, 21, 0.08); + + /* Brand accent tokens consumed by the dialog system (IconBadge gradient, + DialogNote/DialogField brand tone). Defaulted here so they always resolve + pre-hydration (before BrandingProvider sets data-brand) and for any + unbranded root; the per-brand blocks below override them. Default = NovelPad. */ + --np-gradient: linear-gradient(135deg, #22D3EE 0%, #D946EF 55%, #EC4899 100%); + --np-gradient-soft: linear-gradient(135deg, rgba(34, 211, 238, .18) 0%, rgba(217, 70, 239, .18) 55%, rgba(236, 72, 153, .18) 100%); + --np-brand-ink: #D946EF; + --np-brand-soft: rgba(217, 70, 239, 0.13); +} + +.dark { + --np-dialog-surface: #1b2238; + --np-dialog-line: rgba(255, 255, 255, 0.1); + --np-scrim: rgba(6, 9, 20, 0.64); + --np-dialog-shadow: 0 32px 90px rgba(0, 0, 0, 0.62), inset 0 0 0 1px rgba(255, 255, 255, 0.02); + --np-popover-shadow: 0 10px 30px rgba(0, 0, 0, 0.5), inset 0 0 0 1px rgba(255, 255, 255, 0.02); +} + +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 222.2 47.4% 11.2%; + --muted: 210 40% 96.1%; + --muted-foreground: 215.4 16.3% 46.9%; + --popover: 0 0% 100%; + --popover-foreground: 222.2 47.4% 11.2%; + --border: 214.3 31.8% 91.4%; + --input: 214.3 31.8% 91.4%; + --card: 0 0% 100%; + --card-foreground: 222.2 47.4% 11.2%; + --primary: 222.2 47.4% 11.2%; + --primary-foreground: 210 40% 98%; + --secondary: 210 40% 96.1%; + --secondary-foreground: 222.2 47.4% 11.2%; + --accent: 210 40% 96.1%; + --accent-foreground: 222.2 47.4% 11.2%; + --constructive: 142.1 70.6% 45.3%; + --constructive-foreground: 210 40% 98%; + --destructive: 0 100% 50%; + --destructive-foreground: 210 40% 98%; + --ring: 215 20.2% 65.1%; + --highlight: 217 91% 60%; + --radius: 0.5rem; + /* Theme-aware radius scale. Values mirror the current Tailwind output so + introducing themes does not change today's NovelPad appearance. */ + --radius-sm: 0.25rem; + --radius-xs: 0.1875rem; + --radius-base: 0.25rem; + --radius-md: 0.375rem; + --radius-lg: 0.5rem; + --radius-xl: 0.75rem; + --radius-2xl: 1rem; + --radius-compact: 0.5625rem; + --radius-icon: 0.625rem; + --radius-control: 0.6875rem; + --radius-tile: 0.75rem; + --radius-badge: 0.8125rem; + --radius-panel: 0.875rem; + --radius-dialog: 1.125rem; + --font-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + --font-serif: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + --font-reading: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + --font-mono: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + --font-display: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + --sidebar-background: 0 0% 98%; + --sidebar-foreground: 240 5.3% 26.1%; + --sidebar-primary: 240 5.9% 10%; + --sidebar-primary-foreground: 0 0% 98%; + --sidebar-accent: 240 4.8% 95.9%; + --sidebar-accent-foreground: 240 5.9% 10%; + --sidebar-border: 220 13% 91%; + --sidebar-ring: 217.2 91.2% 59.8%; + --app-header-background: hsl(var(--background)); + --app-header-foreground: hsl(var(--foreground)); + --app-header-border: hsl(var(--border)); + --column: 210 40% 92.1%; + --column-foreground: 222.2 47.4% 11.2%; + --column-border: 210 40% 86.1%; + /* Board-specific surfaces default to today's muted canvas and floating + section stacks. Visual themes can override them independently without + changing muted UI throughout the rest of the application. */ + --board-background: var(--muted); + --board-action-foreground: hsl(var(--foreground)); + --board-column-gap: 1.5rem; + --board-padding-inline: 1.5rem; + --board-padding-block-start: 1.25rem; + --board-padding-block-end: 0rem; + --board-padding-block-end-stacked: 4rem; + --board-header-background: hsl(var(--background)); + --board-header-foreground: hsl(var(--foreground)); + --board-header-muted-background: hsl(var(--muted) / 0.5); + --board-header-muted-foreground: hsl(var(--muted-foreground)); + --board-header-border: hsl(var(--column-border)); + --board-header-border-width: 1px; + --board-header-control-border: hsl(var(--border)); + --board-footer-background: hsl(var(--muted)); + --board-footer-foreground: hsl(var(--muted-foreground)); + --board-footer-muted-foreground: hsl(var(--muted-foreground) / 0.7); + --board-footer-hover-background: hsl(var(--accent)); + --board-footer-border: hsl(var(--border)); + --board-footer-border-width: 1px; + --section-column-background: transparent; + --section-column-radius: 0rem; + /* Recessed board canvas (ink-0): darker than --background (chrome). */ + --canvas: 0 0% 100%; + } + + .dark { + --background: 224 71% 4%; + --foreground: 213 31% 91%; + --muted: 223 47% 11%; + --muted-foreground: 215.4 16.3% 56.9%; + --accent: 216 34% 17%; + --accent-foreground: 210 40% 98%; + --popover: 224 71% 4%; + --popover-foreground: 213 31% 91%; + --border: 216 34% 17%; + --input: 216 34% 17%; + --card: 224 71% 4%; + --card-foreground: 213 31% 91%; + --primary: 210 40% 98%; + --primary-foreground: 222.2 47.4% 1.2%; + --secondary: 222.2 47.4% 11.2%; + --secondary-foreground: 210 40% 98%; + --constructive: 142.1 44.1% 37.1%; + --constructive-foreground: 210 40% 98%; + --destructive: 0 63% 42%; + --destructive-foreground: 210 40% 98%; + --ring: 216 34% 17%; + --highlight: 217 91% 60%; + --sidebar-background: 240 5.9% 10%; + --sidebar-foreground: 240 4.8% 95.9%; + --sidebar-primary: 224.3 76.3% 48%; + --sidebar-primary-foreground: 0 0% 100%; + --sidebar-accent: 240 3.7% 15.9%; + --sidebar-accent-foreground: 240 4.8% 95.9%; + --sidebar-border: 240 3.7% 15.9%; + --sidebar-ring: 217.2 91.2% 59.8%; + --column: 224 31% 22%; + --column-foreground: 213 31% 91%; + --column-border: 216 24% 30%; + --canvas: 224 71% 3%; + /* Raised card surface (lifts scene cards above the same-ink board in dark + mode). Brand blocks override this with their own on-brand value. */ + --card-raised: 226 39% 15.5%; + } + +} + +:root[data-brand="helmdocs"] { + --background: 0 0% 100%; + --foreground: 20 14.3% 4.1%; + --card: 0 0% 100%; + --card-foreground: 20 14.3% 4.1%; + --popover: 0 0% 100%; + --popover-foreground: 20 14.3% 4.1%; + --primary: 24.6 95% 53.1%; + --primary-foreground: 60 9.1% 97.8%; + --secondary: 60 4.8% 95.9%; + --secondary-foreground: 24 9.8% 10%; + --muted: 60 4.8% 95.9%; + --muted-foreground: 25 5.3% 44.7%; + --accent: 60 4.8% 95.9%; + --accent-foreground: 24 9.8% 10%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 60 9.1% 97.8%; + --border: 20 5.9% 90%; + --input: 20 5.9% 90%; + --ring: 24.6 95% 53.1%; + --highlight: 24.6 95% 53.1%; + --sidebar-background: 30 6% 97%; + --sidebar-foreground: 20 14.3% 4.1%; + --sidebar-primary: 24.6 95% 53.1%; + --sidebar-primary-foreground: 60 9.1% 97.8%; + --sidebar-accent: 60 4.8% 95.9%; + --sidebar-accent-foreground: 24 9.8% 10%; + --sidebar-border: 20 5.9% 90%; + --sidebar-ring: 24.6 95% 53.1%; + --column: 35 5% 92%; + --column-foreground: 20 14.3% 4.1%; + --column-border: 30 5% 85%; + --canvas: 0 0% 100%; +} + +.dark[data-brand="helmdocs"] { + --background: 20 14.3% 4.1%; + --foreground: 60 9.1% 97.8%; + --card: 20 14.3% 4.1%; + --card-foreground: 60 9.1% 97.8%; + --popover: 20 14.3% 4.1%; + --popover-foreground: 60 9.1% 97.8%; + --primary: 20.5 90.2% 48.2%; + --primary-foreground: 60 9.1% 97.8%; + --secondary: 12 6.5% 15.1%; + --secondary-foreground: 60 9.1% 97.8%; + --muted: 12 6.5% 15.1%; + --muted-foreground: 24 5.4% 63.9%; + --accent: 12 6.5% 15.1%; + --accent-foreground: 60 9.1% 97.8%; + --destructive: 0 72.2% 50.6%; + --destructive-foreground: 60 9.1% 97.8%; + --border: 12 6.5% 15.1%; + --input: 12 6.5% 15.1%; + --ring: 20.5 90.2% 48.2%; + --highlight: 20.5 90.2% 48.2%; + --sidebar-background: 20 10% 7%; + --sidebar-foreground: 60 9.1% 97.8%; + --sidebar-primary: 20.5 90.2% 48.2%; + --sidebar-primary-foreground: 60 9.1% 97.8%; + --sidebar-accent: 12 6.5% 15.1%; + --sidebar-accent-foreground: 60 9.1% 97.8%; + --sidebar-border: 12 6.5% 15.1%; + --sidebar-ring: 20.5 90.2% 48.2%; + --column: 15 10% 14%; + --column-foreground: 60 9.1% 97.8%; + --column-border: 12 8% 22%; + --canvas: 20 14.3% 3%; + /* HelmDocs cards already lift: --card (L 4.1%) is darker than the muted board + (L 15.1%), so the raised surface stays at --card — no NovelPad ink-navy. */ + --card-raised: 20 14.3% 4.1%; + /* Dialog surface: a neutral raised dark panel at the --secondary level, NOT + the NovelPad ink-navy (#1b2238 from the .dark default). Keeps modals lifted + off the near-black background while staying on-brand. */ + --np-dialog-surface: hsl(12 6.5% 15.1%); + --np-scrim: rgba(0, 0, 0, 0.64); +} + +/* HelmDocs brand fonts (Outfit — geometric sans for body + headings; + theme-agnostic). Outfit has no serif companion, so --font-serif (used by + headings, e.g. the auth card title) also points at Outfit. Reading + mono + stay on the system/Georgia stacks. Variable face registers as + "Outfit Variable" — lead the stack with that or the self-hosted face never + loads. */ +:root[data-brand="helmdocs"], +.dark[data-brand="helmdocs"] { + --font-sans: "Outfit Variable", "Outfit", ui-sans-serif, -apple-system, "Segoe UI", system-ui, sans-serif; + --font-serif: "Outfit Variable", "Outfit", ui-sans-serif, -apple-system, "Segoe UI", system-ui, sans-serif; + --font-reading: Georgia, "Times New Roman", serif; + --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; + --font-display: "Outfit Variable", "Outfit", ui-sans-serif, -apple-system, "Segoe UI", system-ui, sans-serif; + + /* HelmDocs brand sweep + accent: the orange ramp (mirrors --loader-gradient + and --primary), replacing the NovelPad cyan→fuchsia gradient in shared + dialog chrome (IconBadge, DialogNote/Field brand tone, step dots, etc.). */ + --np-gradient: linear-gradient(135deg, hsl(24.6 95% 53.1%) 0%, hsl(20.5 90.2% 48.2%) 100%); + --np-gradient-soft: linear-gradient(135deg, hsla(24.6, 95%, 53.1%, .18) 0%, hsla(20.5, 90.2%, 48.2%, .18) 100%); + --np-brand-ink: hsl(24.6 95% 53.1%); + --np-brand-soft: hsla(24.6, 95%, 53.1%, .14); +} + +:root[data-brand="novelpad"] { + --background: 37.5 50% 96.9%; + --foreground: 42.9 14.3% 9.6%; + --muted: 40 38.5% 92.4%; + --muted-foreground: 32.7 6.9% 31.2%; + --popover: 37.5 50% 96.9%; + --popover-foreground: 42.9 14.3% 9.6%; + --border: 40 34.3% 86.3%; + --input: 40 34.3% 86.3%; + /* Card is the raised ink: lighter than the paper --background (L 96.9%) so it + lifts in light mode, mirroring dark mode where --card (L 12.9%) sits above + --background (L 10.6%). A darker card here reads as a recessed panel. */ + --card: 38 60% 99%; + --card-foreground: 42.9 14.3% 9.6%; + --primary: 42.9 14.3% 9.6%; + --primary-foreground: 37.5 50% 96.9%; + --secondary: 40 38.5% 92.4%; + --secondary-foreground: 42.9 14.3% 9.6%; + --accent: 40 38.5% 92.4%; + --accent-foreground: 42.9 14.3% 9.6%; + --constructive: 160.1 84.1% 39.4%; + --constructive-foreground: 37.5 50% 96.9%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 37.5 50% 96.9%; + --ring: 188.7 94.5% 42.7%; + --highlight: 188.7 94.5% 42.7%; + --sidebar-background: 40 48.4% 93.9%; + --sidebar-foreground: 32.7 6.9% 31.2%; + --sidebar-primary: 188.7 94.5% 42.7%; + --sidebar-primary-foreground: 37.5 50% 96.9%; + --sidebar-accent: 40 38.5% 92.4%; + --sidebar-accent-foreground: 42.9 14.3% 9.6%; + --sidebar-border: 40 34.3% 86.3%; + --sidebar-ring: 188.7 94.5% 42.7%; + --column: 40 38.5% 92.4%; + --column-foreground: 42.9 14.3% 9.6%; + --column-border: 40 34.3% 86.3%; + /* Pro's status bar belongs to the board surface rather than reading as a + separate bordered panel. */ + --board-footer-background: hsl(var(--board-background)); + --board-footer-border: transparent; + --board-footer-border-width: 0px; + --canvas: 37.5 50% 96.9%; +} + +.dark[data-brand="novelpad"] { + --background: 225 44.4% 10.6%; + --foreground: 216 33.3% 97.1%; + --muted: 226.2 39.4% 12.9%; + --muted-foreground: 221.5 17.6% 71%; + /* Menus/popovers sit at ink-3 (raised above the ink-2 cards), matching the + prototype's scene/stack/board menus. */ + --popover: 224.1 36.2% 18.4%; + --popover-foreground: 216 33.3% 97.1%; + --border: 224.1 36.2% 18.4%; + --input: 223.9 32.8% 24.5%; + --card: 226.2 39.4% 12.9%; + --card-foreground: 216 33.3% 97.1%; + --primary: 216 33.3% 97.1%; + --primary-foreground: 225 44.4% 10.6%; + --secondary: 224.1 36.2% 18.4%; + --secondary-foreground: 216 33.3% 97.1%; + --accent: 224.1 36.2% 18.4%; + --accent-foreground: 216 33.3% 97.1%; + --constructive: 160.1 84.1% 39.4%; + --constructive-foreground: 216 33.3% 97.1%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 216 33.3% 97.1%; + --ring: 187.9 85.7% 53.3%; + --highlight: 187.9 85.7% 53.3%; + /* Sidebar = ink-1 (chrome level), matching the toolbar; the board canvas + (--canvas) is the darker ink-0 layer below it. */ + --sidebar-background: 225 44.4% 10.6%; + --sidebar-foreground: 225 21.7% 82%; + --sidebar-primary: 187.9 85.7% 53.3%; + --sidebar-primary-foreground: 225 44.4% 10.6%; + --sidebar-accent: 224.1 36.2% 18.4%; + --sidebar-accent-foreground: 216 33.3% 97.1%; + --sidebar-border: 224.1 36.2% 18.4%; + --sidebar-ring: 187.9 85.7% 53.3%; + --column: 224.1 36.2% 18.4%; + --column-foreground: 216 33.3% 97.1%; + --column-border: 223.9 32.8% 24.5%; + /* Recessed board canvas = ink-0 (#0B1020), darker than --background (ink-1). */ + --canvas: 225.7 48.8% 8.4%; + /* Raised card: ink-2 (--card, L 12.9%) lifted to L 15.5% so scene cards read + above the same-ink board (--muted). */ + --card-raised: 226.2 39.4% 15.5%; +} + +/* NovelPad brand fonts, gradient, tag ramp, motion + focus atmosphere (theme-agnostic) */ +:root[data-brand="novelpad"], +.dark[data-brand="novelpad"] { + --font-sans: "Bricolage Grotesque Variable", "Bricolage Grotesque", ui-sans-serif, -apple-system, "Segoe UI", system-ui, sans-serif; + --font-serif: "Source Serif 4 Variable", "Source Serif 4", "Source Serif Pro", "Iowan Old Style", Georgia, serif; + --font-reading: "Lora Variable", "Lora", "Iowan Old Style", Georgia, serif; + --font-mono: "Courier Prime", "Courier New", ui-monospace, "SF Mono", Menlo, monospace; + --font-display: "Fenwick", "DM Serif Display", "Source Serif 4 Variable", "Source Serif 4", Georgia, serif; + + --np-gradient: linear-gradient(135deg, #22D3EE 0%, #D946EF 55%, #EC4899 100%); + --np-gradient-soft: linear-gradient(135deg, rgba(34, 211, 238, .18) 0%, rgba(217, 70, 239, .18) 55%, rgba(236, 72, 153, .18) 100%); + + --tag-cyan: #22D3EE; + --tag-pink: #EC4899; + --tag-plum: #7C3AED; + --tag-amber: #F59E0B; + --tag-sage: #5B8F6B; + --tag-dusk: #64748B; + --tag-coral: #FB7185; + + --ease: cubic-bezier(0.32, 0.72, 0, 1); + --dur-fast: 150ms; + --dur-base: 220ms; + --dur-slow: 320ms; + + --np-focus-ring: 0 0 0 2px hsl(var(--background)), 0 0 0 4px #22D3EE; +} + +/* ── NovelPad Classic ────────────────────────────────────────────────────── + Visual theme for writers who prefer the previous NovelPad application. + Source palette: ../novelpad/src/novelpad/ux/theme.ts. The app's structural + layout stays current; semantic surfaces, accents, typography, and standard + radii reproduce the Classic character in both color modes. */ +:root[data-brand="novelpad"][data-novel-theme="novelpad-classic"] { + --background: 60 11.1% 94.7%; /* #F3F3F0 */ + --foreground: 247.1 11.4% 29.2%; /* #444253 */ + --muted: 0 0% 93.3%; /* #EEEEEE */ + --muted-foreground: 240 4% 34.7%; /* #55555C */ + --popover: 0 0% 100%; + --popover-foreground: 247.1 11.4% 29.2%; + --border: 236.7 16.7% 78.8%; /* #C0C1D2 */ + --input: 236.7 16.7% 78.8%; + --card: 0 0% 100%; + --card-foreground: 247.1 11.4% 29.2%; + --card-raised: 0 0% 100%; + --primary: 198.7 73.2% 49.8%; /* #22A2DC */ + --primary-foreground: 253.3 20.9% 8.4%; + --secondary: 334.1 74.7% 52%; /* #E02978 */ + --secondary-foreground: 0 0% 100%; + --accent: 236.7 16.7% 78.8%; + --accent-foreground: 244.6 11.5% 22.2%; + --constructive: 128.2 44.2% 44.3%; /* #3FA34D */ + --constructive-foreground: 0 0% 100%; + --destructive: 6.2 81.7% 62.7%; /* #EE6352 */ + --destructive-foreground: 253.3 20.9% 8.4%; + --ring: 198.7 73.2% 49.8%; + --highlight: 198.7 73.2% 49.8%; + /* Classic's navigation rail was dark in both modes. */ + --sidebar-background: 244.6 11.5% 22.2%; + --sidebar-foreground: 210 21.4% 94.5%; + --sidebar-primary: 198.7 73.2% 49.8%; + --sidebar-primary-foreground: 253.3 20.9% 8.4%; + --sidebar-accent: 240 11.7% 15.1%; + --sidebar-accent-foreground: 210 21.4% 94.5%; + --sidebar-border: 244.6 11.5% 22.2%; + --sidebar-ring: 198.7 73.2% 49.8%; + --app-header-background: hsl(var(--sidebar-background)); + --app-header-foreground: hsl(var(--sidebar-foreground)); + --app-header-border: hsl(var(--sidebar-border)); + --column: 0 0% 93.3%; + --column-foreground: 240 8.1% 21.8%; + --column-border: 236.7 16.7% 78.8%; + --board-background: 240 11.7% 15.1%; /* #22222B */ + --board-action-foreground: #EEF1F4; + --canvas: 60 11.1% 94.7%; + + --np-dialog-surface: #FFFFFF; + --np-dialog-line: rgba(68, 66, 83, 0.18); + --np-scrim: rgba(19, 17, 26, 0.38); + --np-dialog-shadow: 0 18px 48px rgba(34, 34, 43, 0.22), 0 2px 6px rgba(34, 34, 43, 0.10); + --np-popover-shadow: 0 8px 20px rgba(34, 34, 43, 0.18); +} + +.dark[data-brand="novelpad"][data-novel-theme="novelpad-classic"] { + --background: 253.3 20.9% 8.4%; /* #13111A */ + --foreground: 210 21.4% 94.5%; /* #EEF1F4 */ + --muted: 244.6 11.5% 22.2%; /* #33323F */ + --muted-foreground: 236.7 16.7% 78.8%; + --popover: 244.6 11.5% 22.2%; + --popover-foreground: 210 21.4% 94.5%; + --border: 247.1 11.4% 29.2%; /* #444253 */ + --input: 240 4% 34.7%; /* #55555C */ + --card: 240 11.7% 15.1%; /* #22222B */ + --card-foreground: 210 21.4% 94.5%; + --card-raised: 240 11.7% 15.1%; + --primary: 198.7 73.2% 49.8%; + --primary-foreground: 253.3 20.9% 8.4%; + --secondary: 334.1 74.7% 52%; + --secondary-foreground: 0 0% 100%; + --accent: 244.6 11.5% 22.2%; + --accent-foreground: 210 21.4% 94.5%; + --constructive: 128.2 44.2% 44.3%; + --constructive-foreground: 253.3 20.9% 8.4%; + --destructive: 6.2 81.7% 62.7%; + --destructive-foreground: 253.3 20.9% 8.4%; + --ring: 198.7 73.2% 49.8%; + --highlight: 198.7 73.2% 49.8%; + --sidebar-background: 244.6 11.5% 22.2%; + --sidebar-foreground: 210 21.4% 94.5%; + --sidebar-primary: 198.7 73.2% 49.8%; + --sidebar-primary-foreground: 253.3 20.9% 8.4%; + --sidebar-accent: 240 11.7% 15.1%; + --sidebar-accent-foreground: 210 21.4% 94.5%; + --sidebar-border: 240 11.7% 15.1%; + --sidebar-ring: 198.7 73.2% 49.8%; + --app-header-background: hsl(var(--sidebar-background)); + --app-header-foreground: hsl(var(--sidebar-foreground)); + --app-header-border: hsl(var(--sidebar-border)); + --column: 240 8.1% 21.8%; /* #33333C */ + --column-foreground: 0 0% 93.3%; + --column-border: 240 4% 34.7%; + --board-background: 240 11.7% 15.1%; /* #22222B */ + --board-action-foreground: #EEF1F4; + --canvas: 253.3 20.9% 8.4%; + + --np-dialog-surface: #22222B; + --np-dialog-line: rgba(192, 193, 210, 0.18); + --np-scrim: rgba(19, 17, 26, 0.68); + --np-dialog-shadow: 0 24px 64px rgba(0, 0, 0, 0.56), inset 0 0 0 1px rgba(238, 241, 244, 0.025); + --np-popover-shadow: 0 10px 26px rgba(0, 0, 0, 0.44); +} + +:root[data-brand="novelpad"][data-novel-theme="novelpad-classic"], +.dark[data-brand="novelpad"][data-novel-theme="novelpad-classic"] { + /* Outfit is already self-hosted and is the closest available geometric sans + to Classic's Montserrat without introducing a network-loaded font. */ + --font-sans: "Outfit Variable", "Outfit", "Montserrat", "Roboto", "Helvetica", "Arial", sans-serif; + --font-serif: "Outfit Variable", "Outfit", "Montserrat", "Roboto", "Helvetica", "Arial", sans-serif; + --font-reading: "Libre Baskerville", Georgia, serif; + --font-display: "Outfit Variable", "Outfit", "Montserrat", "Roboto", "Helvetica", "Arial", sans-serif; + + --np-gradient: linear-gradient(-45deg, #E02978 45%, #22A2DC 85%); + --np-gradient-soft: linear-gradient(-45deg, rgba(224, 41, 120, 0.18) 45%, rgba(34, 162, 220, 0.18) 85%); + --np-brand-ink: #22A2DC; + --np-brand-soft: rgba(34, 162, 220, 0.14); + --np-focus-ring: 0 0 0 2px hsl(var(--background)), 0 0 0 4px #22A2DC; + + --tag-cyan: #08B2E3; + --tag-pink: #EE6352; + --tag-plum: #B38CB4; + --tag-amber: #E8AE68; + --tag-sage: #3FA34D; + + --radius: 0.25rem; + --radius-sm: 0.25rem; + --radius-xs: 0.25rem; + --radius-base: 0.25rem; + --radius-md: 0.25rem; + --radius-lg: 0.25rem; + --radius-xl: 0.25rem; + --radius-2xl: 0.25rem; + --radius-compact: 0.25rem; + --radius-icon: 0.25rem; + --radius-control: 0.25rem; + --radius-tile: 0.25rem; + --radius-badge: 0.25rem; + --radius-panel: 0.25rem; + --radius-dialog: 0.25rem; + /* Classic used one seven-pixel grid unit between stacks and around the + board edges (../novelpad/src/novelpad/ux/theme.ts). */ + --board-column-gap: 0.4375rem; + --board-padding-inline: 0.4375rem; + --board-padding-block-start: 0.4375rem; + --board-padding-block-end: 0.4375rem; + --board-padding-block-end-stacked: 0.4375rem; + --board-header-background: hsl(var(--board-background)); + --board-header-foreground: #EEF1F4; + --board-header-muted-background: rgba(238, 241, 244, 0.08); + --board-header-muted-foreground: #C0C1D2; + --board-header-border: transparent; + --board-header-border-width: 0px; + --board-header-control-border: #444253; + --board-footer-background: hsl(var(--board-background)); + --board-footer-foreground: #EEF1F4; + --board-footer-muted-foreground: #C0C1D2; + --board-footer-hover-background: rgba(238, 241, 244, 0.08); + --board-footer-border: transparent; + --board-footer-border-width: 0px; + --section-column-background: hsl(var(--column)); + --section-column-radius: 0.25rem; +} + +.btn-gradient { + background: var(--np-gradient); + color: #fff; +} + +[data-slot="board-header"] [data-slot="board-type-chip"] { + background-color: var(--board-header-muted-background); + border-color: var(--board-header-control-border); + color: var(--board-header-muted-foreground); +} + +@layer base { + * { + @apply border-border; + } + body { + @apply font-sans antialiased bg-background text-foreground; + } +} + +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } +} + +/* + * --mention-ink: the link color for @-mention chips (and the active typeahead + * highlight). On dark surfaces the bright brand --highlight reads well, but on + * light surfaces the high-luminance accents (cyan/blue/orange) wash out — so we + * deepen them per brand for legible contrast. Resolved against the originating + * element, so it also feeds `::highlight(mention-active)` (mark.css). Kept out + * of @layer base so these win over the layered theme blocks. + */ +:root { + --mention-ink: var(--highlight); +} +:root:not(.dark) { + --mention-ink: 217 78% 48%; +} +:root:not(.dark)[data-brand="helmdocs"] { + --mention-ink: 24.6 88% 44%; +} +:root:not(.dark)[data-brand="novelpad"] { + --mention-ink: 192 80% 30%; +} +:root:not(.dark)[data-brand="novelpad"][data-novel-theme="novelpad-classic"] { + --mention-ink: 198.7 83% 34%; +} + +/* + * Loader + skeleton design tokens (see docs/features/loaders.md). These feed the + * brand loader system in packages/app/src/components/common/loaders/: + * --loader-gradient the brand sweep used by Spinner / Progress / skeleton stripe + * --np-skel-track resting "bone" fill of a skeleton block + * --np-skel-tint travelling highlight on the skeleton sweep + * --np-skel-line skeleton card hairline (defaults to the theme border) + * --np-skel-speed skeleton shimmer period (overridable inline per instance) + * The handoffs are dark-tuned, so the light values raise contrast. Kept out of + * @layer base so they win over the layered theme/brand blocks. NovelPad is the + * default brand; helmdocs overrides keep the system on-brand if a *shared* + * loader renders on a Helm surface (Helm skeletons themselves stay animate-pulse). + */ +:root { + --loader-gradient: linear-gradient(135deg, #22d3ee 0%, #d946ef 55%, #ec4899 100%); + --np-skel-track: rgba(20, 18, 15, 0.06); + --np-skel-tint: rgba(34, 211, 238, 0.18); + --np-skel-line: hsl(var(--border)); + --np-skel-speed: 1400ms; +} +.dark { + --np-skel-track: rgba(255, 255, 255, 0.07); + --np-skel-tint: rgba(150, 228, 247, 0.17); +} + +/* + * Full-screen loading surface + inks (LoadingScreen / desktop boot splash). + * Light = NovelPad paper (#FBF8F3 = the light --background) under a faint brand + * glow; dark = the deep ink navy from the loader handoff. The desktop splash + * (apps/desktop/src/renderer/src/splash.tsx) renders before this sheet is + * guaranteed, so it mirrors these values inline — keep the two in sync. + */ +:root { + --np-loading-surface: radial-gradient(120% 90% at 50% 42%, rgba(217, 70, 239, 0.10) 0%, rgba(34, 211, 238, 0.07) 32%, rgba(251, 248, 243, 0) 64%), #FBF8F3; + --np-loading-ink: #1C1A15; + --np-loading-muted: rgba(28, 26, 21, 0.60); + --np-loading-subtitle: rgba(28, 26, 21, 0.45); + --np-loading-line: rgba(28, 26, 21, 0.16); +} +.dark { + --np-loading-surface: radial-gradient(120% 90% at 50% 42%, rgba(217, 70, 239, 0.10) 0%, rgba(34, 211, 238, 0.06) 32%, rgba(15, 21, 39, 0) 64%), #0B0F1A; + --np-loading-ink: #F5F7FA; + --np-loading-muted: rgba(245, 247, 250, 0.55); + --np-loading-subtitle: rgba(245, 247, 250, 0.40); + --np-loading-line: rgba(245, 247, 250, 0.16); +} +/* HelmDocs: plain theme surfaces, no NovelPad glow. */ +:root[data-brand="helmdocs"] { + --np-loading-surface: hsl(0 0% 100%); + --np-loading-ink: hsl(20 14.3% 4.1%); +} +.dark[data-brand="helmdocs"] { + --np-loading-surface: hsl(20 14.3% 4.1%); + --np-loading-ink: hsl(60 9.1% 97.8%); +} +:root[data-brand="helmdocs"] { + --loader-gradient: linear-gradient(135deg, hsl(24.6 95% 53.1%) 0%, hsl(20.5 90.2% 48.2%) 100%); + --np-skel-tint: rgba(251, 146, 60, 0.16); + /* Spinner (loader #4) conic sweep — retinted to the HelmDocs orange ramp so + the shared Spinner / loaderToast reads on-brand instead of leaking the + NovelPad cyan→plum→pink sweep. The stops map to spinner.css's conic slots + (transparent → light 180deg → mid 280deg → deep 360deg). */ + --np-spinner-from: rgba(251, 146, 60, 0); + --np-spinner-cyan: #fb923c; + --np-spinner-plum: #f97316; + --np-spinner-pink: #ea580c; +} +.dark[data-brand="helmdocs"] { + --np-skel-tint: rgba(251, 146, 60, 0.14); + --np-spinner-from: rgba(251, 146, 60, 0); + --np-spinner-cyan: #fb923c; + --np-spinner-plum: #f97316; + --np-spinner-pink: #ea580c; +} + +:root[data-brand="novelpad"][data-novel-theme="novelpad-classic"] { + --loader-gradient: linear-gradient(-45deg, #E02978 45%, #22A2DC 85%); + --np-skel-track: rgba(68, 66, 83, 0.09); + --np-skel-tint: rgba(34, 162, 220, 0.18); + --np-loading-surface: #F3F3F0; + --np-loading-ink: #444253; + --np-loading-muted: rgba(68, 66, 83, 0.66); + --np-loading-subtitle: rgba(68, 66, 83, 0.48); + --np-loading-line: rgba(68, 66, 83, 0.20); +} +.dark[data-brand="novelpad"][data-novel-theme="novelpad-classic"] { + --np-skel-track: rgba(238, 241, 244, 0.08); + --np-skel-tint: rgba(34, 162, 220, 0.18); + --np-loading-surface: #13111A; + --np-loading-ink: #EEF1F4; + --np-loading-muted: rgba(238, 241, 244, 0.60); + --np-loading-subtitle: rgba(238, 241, 244, 0.44); + --np-loading-line: rgba(238, 241, 244, 0.18); +} diff --git a/packages/config/tailwind.config.ts b/packages/config/tailwind.config.ts new file mode 100644 index 0000000..4275d19 --- /dev/null +++ b/packages/config/tailwind.config.ts @@ -0,0 +1,125 @@ +import { type Config } from 'tailwindcss'; + +export default { + darkMode: 'class', + content: [ + '../core/src/**/*.{js,ts,jsx,tsx}', + '../app/src/**/*.{js,ts,jsx,tsx}', + ], + theme: { + extend: { + fontFamily: { + sans: ['var(--font-sans)'], + serif: ['var(--font-serif)'], + reading: ['var(--font-reading)'], + mono: ['var(--font-mono)'], + display: ['var(--font-display)'], + }, + zIndex: { + base: '100', + modal: '200', + overlay: '300', + popover: '400', + tooltip: '500', + }, + colors: { + border: 'hsl(var(--border))', + input: 'hsl(var(--input))', + ring: 'hsl(var(--ring))', + // Brand accent for highlighting/active states (NovelPad cyan, HelmDocs + // orange). The alpha-value placeholder lets utilities tint it, e.g. + // `text-highlight`, `bg-highlight/15`, `border-highlight/40`. + highlight: 'hsl(var(--highlight) / )', + background: 'hsl(var(--background))', + foreground: 'hsl(var(--foreground))', + brand: { + DEFAULT: '#22D3EE', + blue: '#22D3EE', + purple: '#D946EF', + pink: '#EC4899', + }, + primary: { + DEFAULT: 'hsl(var(--primary))', + foreground: 'hsl(var(--primary-foreground))', + }, + secondary: { + DEFAULT: 'hsl(var(--secondary))', + foreground: 'hsl(var(--secondary-foreground))', + }, + constructive: { + DEFAULT: 'hsl(var(--constructive))', + foreground: 'hsl(var(--constructive-foreground))', + }, + destructive: { + DEFAULT: 'hsl(var(--destructive))', + foreground: 'hsl(var(--destructive-foreground))', + }, + muted: { + DEFAULT: 'hsl(var(--muted))', + foreground: 'hsl(var(--muted-foreground))', + }, + column: { + DEFAULT: 'hsl(var(--column))', + foreground: 'hsl(var(--column-foreground))', + border: 'hsl(var(--column-border))', + }, + accent: { + DEFAULT: 'hsl(var(--accent))', + foreground: 'hsl(var(--accent-foreground))', + }, + popover: { + DEFAULT: 'hsl(var(--popover))', + foreground: 'hsl(var(--popover-foreground))', + }, + card: { + DEFAULT: 'hsl(var(--card))', + foreground: 'hsl(var(--card-foreground))', + }, + sidebar: { + DEFAULT: 'hsl(var(--sidebar-background))', + foreground: 'hsl(var(--sidebar-foreground))', + primary: 'hsl(var(--sidebar-primary))', + 'primary-foreground': 'hsl(var(--sidebar-primary-foreground))', + accent: 'hsl(var(--sidebar-accent))', + 'accent-foreground': 'hsl(var(--sidebar-accent-foreground))', + border: 'hsl(var(--sidebar-border))', + ring: 'hsl(var(--sidebar-ring))', + }, + }, + borderRadius: { + sm: 'var(--radius-sm)', + DEFAULT: 'var(--radius-base)', + md: 'var(--radius-md)', + lg: 'var(--radius-lg)', + xl: 'var(--radius-xl)', + '2xl': 'var(--radius-2xl)', + }, + aspectRatio: { + '3/2': '3 / 2', + }, + keyframes: { + 'accordion-down': { + from: { + height: '0', + }, + to: { + height: 'var(--radix-accordion-content-height)', + }, + }, + 'accordion-up': { + from: { + height: 'var(--radix-accordion-content-height)', + }, + to: { + height: '0', + }, + }, + }, + animation: { + 'accordion-down': 'accordion-down 0.2s ease-out', + 'accordion-up': 'accordion-up 0.2s ease-out', + }, + }, + }, + plugins: [require('tailwindcss-animate'), require('@tailwindcss/typography'), require('@tailwindcss/container-queries')], +} satisfies Config; diff --git a/packages/config/tsconfig.base.json b/packages/config/tsconfig.base.json new file mode 100644 index 0000000..5d20298 --- /dev/null +++ b/packages/config/tsconfig.base.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "esModuleInterop": true, + "skipLibCheck": true, + "target": "es2022", + "allowJs": true, + "resolveJsonModule": true, + "moduleDetection": "force", + "isolatedModules": true, + "strict": true, + "noUncheckedIndexedAccess": true, + "module": "NodeNext", + "jsx": "react-jsx", + "sourceMap": true, + "incremental": true + } +} diff --git a/packages/config/vite-plugins/react-virtualized.js b/packages/config/vite-plugins/react-virtualized.js new file mode 100644 index 0000000..1501a0d --- /dev/null +++ b/packages/config/vite-plugins/react-virtualized.js @@ -0,0 +1,32 @@ +import fs from 'node:fs/promises'; +import { createRequire } from 'node:module'; +import path from 'node:path'; +import url from 'node:url'; + +const WRONG_CODE = `import { bpfrpt_proptype_WindowScroller } from "../WindowScroller.js";`; + +export function reactVirtualized() { + return { + name: 'flat:react-virtualized', + // Note: we cannot use the `transform` hook here + // because libraries are pre-bundled in vite directly, + // plugins aren't able to hack that step currently. + // so instead we manually edit the file in node_modules. + // all we need is to find the timing before pre-bundling. + configResolved: async () => { + const require = createRequire(import.meta.url); + const reactVirtualizedPath = require.resolve('react-virtualized'); + const { pathname: reactVirtualizedFilePath } = new url.URL( + reactVirtualizedPath, + import.meta.url, + ); + const file = reactVirtualizedFilePath.replace( + path.join('dist', 'commonjs', 'index.js'), + path.join('dist', 'es', 'WindowScroller', 'utils', 'onScroll.js'), + ); + const code = await fs.readFile(file, 'utf-8'); + const modified = code.replace(WRONG_CODE, ''); + await fs.writeFile(file, modified); + }, + }; +} diff --git a/packages/outreach-ai/README.md b/packages/outreach-ai/README.md new file mode 100644 index 0000000..56ef810 --- /dev/null +++ b/packages/outreach-ai/README.md @@ -0,0 +1,54 @@ +# @novelpad/outreach-ai + +LLM access layer for the HelmDocs grant-match outreach engine. Modeled on +`novelpad-desktop`'s `packages/ai`, trimmed to what this engine needs: a +Vertex/Gemini client, an embeddings helper, model-tier constants, and two +structured-output agents (org profiler, mission-fit judge). + +## Provenance + +`src/gemini.ts` and `src/embeddings.ts` are copied near-verbatim from +`novelpad-desktop` at commit `62c56b87` (see the header comment in each file +for the exact source path). They are kept as close to the original as +compiles standalone in this workspace — do not "clean up" drift between them +and the source without checking whether the source has moved on too. + +`src/agents/*/run.ts` follow the structured-JSON-output pattern from +`novelpad-desktop`'s `packages/ai/src/agents/grant/section-drafter/run.ts` +(prompt builder + `generateContent` with `responseMimeType: 'application/json'` +and a hand-written `responseSchema`), but are new code for this repo's domain, +not copies — both are currently `NOT IMPLEMENTED` stubs pending the +source-fetching / hard-gate scoring infrastructure they depend on. Each stub's +doc comment shows the intended call shape. + +## Never mix embedding models + +`generateQueryEmbedding` / `generateDocumentEmbedding` / `generateDocumentEmbeddings` +are pinned to `gemini-embedding-001` at `outputDimensionality: 1536`. **Do not +change either value**, and do not add a second embedding model/dimensionality +into this package. Every vector this package produces has to remain +comparable (same model, same dimensionality) against every other vector +already stored in the HelmDocs RAG index — switching models or dimensions +silently corrupts similarity search for anything embedded before the switch, +with no error at write time. If a better embedding model becomes available, +that's a deliberate, full-reindex migration, not a constant change here. + +## Model tiering + +See `src/models.ts`: `BULK_MODEL` (`gemini-2.5-flash`) for high-volume +mechanical work (classification, effort estimates, first-pass org-profile +extraction); `JUDGE_MODEL` (`gemini-2.5-pro`) for anything a wrong answer +could put in front of a real prospect (mission-fit judge, email +personalization QA). + +## Layout + +- `src/gemini.ts` — `getAi()` Vertex client singleton + `CHAT_MODEL`. +- `src/embeddings.ts` — query/document embedding helpers. +- `src/models.ts` — `BULK_MODEL` / `JUDGE_MODEL` constants. +- `src/agents/org-profiler/` — `OrgProfileSchema` (per-field source URL + + confidence) and the profiler agent stub. +- `src/agents/mission-fit-judge/` — `MissionFitVerdictSchema` (`fit`, + `citedOrgProgram`, `citedGrantPriority`, `reasoning`) and the judge agent + stub. +- `src/index.ts` — barrel export. diff --git a/packages/outreach-ai/package.json b/packages/outreach-ai/package.json new file mode 100644 index 0000000..3bfbb3a --- /dev/null +++ b/packages/outreach-ai/package.json @@ -0,0 +1,32 @@ +{ + "name": "@novelpad/outreach-ai", + "version": "1.0.0", + "packageManager": "yarn@4.5.0", + "type": "module", + "sideEffects": false, + "imports": { + "#~/*": "./dist/*" + }, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + }, + "scripts": { + "build": "tsc", + "dev": "tsc --watch", + "typecheck": "tsc --noEmit", + "test": "vitest run" + }, + "dependencies": { + "@google/genai": "^1.41.0", + "zod": "^3.25.0" + }, + "devDependencies": { + "@novelpad/config": "workspace:^", + "@types/node": "^22", + "typescript": "^5.9.3", + "vitest": "^3.2.4" + } +} diff --git a/packages/outreach-ai/src/agents/mission-fit-judge/run.ts b/packages/outreach-ai/src/agents/mission-fit-judge/run.ts new file mode 100644 index 0000000..12c587e --- /dev/null +++ b/packages/outreach-ai/src/agents/mission-fit-judge/run.ts @@ -0,0 +1,64 @@ +import { MissionFitVerdictSchema, type MissionFitVerdict } from './schema.js'; +import type { OrgProfile } from '../org-profiler/schema.js'; + +export interface RunMissionFitJudgeInput { + /** Extracted profile of the candidate org (from the Org Profiler). */ + orgProfile: OrgProfile; + /** Grant program name / title, as scored against by the deterministic SQL gates. */ + grantProgramName: string; + /** Funding priorities / eligible-use language pulled from the grant's own source text. */ + grantPriorities: string[]; +} + +/** + * Build the judge prompt for one (org, grant) match candidate. Kept separate + * from `runMissionFitJudge` so it's independently unit-testable once wired up. + */ +export function buildMissionFitJudgePrompt(input: RunMissionFitJudgeInput): string { + return [ + 'You are the final mission-fit judge for a candidate (org, grant) match', + 'that has already passed deterministic SQL hard gates (eligibility,', + 'geography, award range). Decide whether the org\'s actual programs', + 'plausibly fit the grant\'s funding priorities. You MUST cite one', + 'concrete org program and one concrete grant priority your verdict is', + 'grounded in — a verdict without both citations is invalid. A wrong', + '"fit: true" here can put a real NH nonprofit in front of a funder that', + 'will never fund them, so when the fit is unclear, prefer `fit: false`.', + '', + `Grant program: ${input.grantProgramName}`, + `Grant priorities: ${input.grantPriorities.join('; ')}`, + `Org legal name: ${input.orgProfile.legalName.value}`, + `Org mission: ${input.orgProfile.mission.value}`, + `Org program areas: ${input.orgProfile.programAreas.value.join('; ')}`, + ].join('\n'); +} + +/** + * NOT IMPLEMENTED — this judge is the veto gate before a match can reach a + * human reviewer (and, downstream, a real prospect via Apollo), so it should + * not go live against real matches until the deterministic hard-gate scoring + * this package doesn't own is wired in ahead of it. Intended production call + * shape, mirroring novelpad-desktop's + * packages/ai/src/agents/grant/section-drafter/run.ts (invokeVertex + + * responseSchema-constrained structured JSON output) — note `JUDGE_MODEL`, + * not `BULK_MODEL`: a wrong verdict here reaches a prospect: + * + * import { getAi } from '../../gemini.js'; + * import { JUDGE_MODEL } from '../../models.js'; + * + * const result = await getAi().models.generateContent({ + * model: JUDGE_MODEL, + * contents: buildMissionFitJudgePrompt(input), + * config: { + * responseMimeType: 'application/json', + * // responseSchema: MISSION_FIT_VERDICT_RESPONSE_SCHEMA — a Type/Schema + * // literal from '@google/genai' hand-mirroring MissionFitVerdictSchema. + * temperature: 0.1, + * }, + * }); + * const raw = JSON.parse(result.text ?? '{}'); + * return MissionFitVerdictSchema.parse(raw); + */ +export async function runMissionFitJudge(_input: RunMissionFitJudgeInput): Promise { + throw new Error('not implemented'); +} diff --git a/packages/outreach-ai/src/agents/mission-fit-judge/schema.ts b/packages/outreach-ai/src/agents/mission-fit-judge/schema.ts new file mode 100644 index 0000000..0abe7d0 --- /dev/null +++ b/packages/outreach-ai/src/agents/mission-fit-judge/schema.ts @@ -0,0 +1,20 @@ +import { z } from 'zod'; + +/** + * Mission-fit judge verdict for one (org, grant) match candidate. This is + * the last human-facing gate before a match is surfaced for review — the + * judge must ground its verdict in something concrete from each side rather + * than a vibe, so `citedOrgProgram` / `citedGrantPriority` are required, not + * optional summary fields. + */ +export const MissionFitVerdictSchema = z.object({ + /** Whether the org's mission plausibly fits the grant's funding priorities. */ + fit: z.boolean(), + /** The specific org program/activity the verdict is grounded in (from the org profile). */ + citedOrgProgram: z.string().min(1), + /** The specific funding priority/eligibility line the verdict is grounded in (from the grant). */ + citedGrantPriority: z.string().min(1), + /** Short human-readable justification tying the two citations together. */ + reasoning: z.string().min(1), +}); +export type MissionFitVerdict = z.infer; diff --git a/packages/outreach-ai/src/agents/org-profiler/run.ts b/packages/outreach-ai/src/agents/org-profiler/run.ts new file mode 100644 index 0000000..ae43ddb --- /dev/null +++ b/packages/outreach-ai/src/agents/org-profiler/run.ts @@ -0,0 +1,52 @@ +import { OrgProfileSchema, type OrgProfile } from './schema.js'; + +export interface RunOrgProfilerInput { + /** NH nonprofit legal or DBA name, as known so far (e.g. from a 990-PF index). */ + orgName: string; + /** Candidate source URLs to ground extraction in — org website, 990-PF PDF, GuideStar/Charity Navigator page. */ + sourceUrls: string[]; +} + +/** + * Build the producer prompt for the Org Profiler. Kept separate from + * `runOrgProfiler` so it's independently unit-testable once wired up. + */ +export function buildOrgProfilerPrompt(input: RunOrgProfilerInput): string { + return [ + 'You are extracting a structured profile for an NH nonprofit organization', + 'from the source documents below. Every field must cite the exact source', + 'URL it was read from and a 0-1 confidence. Do not fabricate a value —', + 'omit or null a field you cannot ground in a source.', + '', + `Organization (working name): ${input.orgName}`, + `Sources: ${input.sourceUrls.join(', ')}`, + ].join('\n'); +} + +/** + * NOT IMPLEMENTED — the profiler needs a source-fetching harness (web fetch / + * PDF-parse for 990-PFs, per the ingestion pipeline this package doesn't own) + * wired in before it can safely call Vertex with real source text. Intended + * production call shape, mirroring novelpad-desktop's + * packages/ai/src/agents/grant/section-drafter/run.ts (invokeVertex + + * responseSchema-constrained structured JSON output): + * + * import { getAi } from '../../gemini.js'; + * import { BULK_MODEL } from '../../models.js'; + * + * const result = await getAi().models.generateContent({ + * model: BULK_MODEL, + * contents: buildOrgProfilerPrompt(input), + * config: { + * responseMimeType: 'application/json', + * // responseSchema: ORG_PROFILE_RESPONSE_SCHEMA — a Type/Schema literal + * // from '@google/genai' hand-mirroring OrgProfileSchema below. + * temperature: 0.1, + * }, + * }); + * const raw = JSON.parse(result.text ?? '{}'); + * return OrgProfileSchema.parse(raw); + */ +export async function runOrgProfiler(_input: RunOrgProfilerInput): Promise { + throw new Error('not implemented'); +} diff --git a/packages/outreach-ai/src/agents/org-profiler/schema.test.ts b/packages/outreach-ai/src/agents/org-profiler/schema.test.ts new file mode 100644 index 0000000..ea57d2d --- /dev/null +++ b/packages/outreach-ai/src/agents/org-profiler/schema.test.ts @@ -0,0 +1,56 @@ +import { describe, expect, it } from 'vitest'; +import { OrgProfileSchema } from './schema.js'; + +const validProfile = { + legalName: { value: 'NH Literacy Alliance', sourceUrl: 'https://nhliteracy.org', confidence: 0.95 }, + ein: { value: '02-1234567', sourceUrl: 'https://apps.irs.gov/pfft', confidence: 0.9 }, + mission: { value: 'Improve literacy outcomes for NH children.', sourceUrl: 'https://nhliteracy.org/about', confidence: 0.85 }, + programAreas: { + value: ['youth literacy tutoring', 'family reading nights'], + sourceUrl: 'https://nhliteracy.org/programs', + confidence: 0.75, + }, + geographicScope: { value: 'Hillsborough County, NH', sourceUrl: 'https://nhliteracy.org/about', confidence: 0.6 }, + annualRevenue: { value: 480_000, sourceUrl: 'https://apps.irs.gov/pfft', confidence: 0.8 }, + targetPopulations: { + value: ['K-5 students', 'low-income families'], + sourceUrl: 'https://nhliteracy.org/about', + confidence: 0.55, + }, +}; + +describe('OrgProfileSchema', () => { + it('accepts a fully-sourced valid profile fixture', () => { + const result = OrgProfileSchema.safeParse(validProfile); + expect(result.success).toBe(true); + }); + + it('accepts a null ein and a null annualRevenue (org not matched to a 990-PF filing)', () => { + const result = OrgProfileSchema.safeParse({ ...validProfile, ein: null, annualRevenue: null }); + expect(result.success).toBe(true); + }); + + it('rejects an ein that does not match the ##-####### federal EIN shape', () => { + const result = OrgProfileSchema.safeParse({ + ...validProfile, + ein: { value: 'not-an-ein', sourceUrl: null, confidence: 0.9 }, + }); + expect(result.success).toBe(false); + }); + + it('rejects a confidence outside the 0-1 range', () => { + const result = OrgProfileSchema.safeParse({ + ...validProfile, + mission: { value: 'Improve literacy outcomes.', sourceUrl: null, confidence: 1.4 }, + }); + expect(result.success).toBe(false); + }); + + it('rejects an empty programAreas array (sourcedField requires at least one)', () => { + const result = OrgProfileSchema.safeParse({ + ...validProfile, + programAreas: { value: [], sourceUrl: null, confidence: 0.5 }, + }); + expect(result.success).toBe(false); + }); +}); diff --git a/packages/outreach-ai/src/agents/org-profiler/schema.ts b/packages/outreach-ai/src/agents/org-profiler/schema.ts new file mode 100644 index 0000000..1413e38 --- /dev/null +++ b/packages/outreach-ai/src/agents/org-profiler/schema.ts @@ -0,0 +1,36 @@ +import { z } from 'zod'; + +/** + * A single extracted org-profile field, carrying its own provenance: the URL + * the value was pulled from (a 990-PF filing, the org's website, an NH + * Secretary of State record) and the model's confidence in the extraction. + * `sourceUrl: null` means the field was inferred rather than read verbatim + * from a single cited page (e.g. synthesized across multiple sources) — + * callers should treat a null-sourced field as lower-trust regardless of the + * reported confidence. + */ +function sourcedField(valueSchema: Value) { + return z.object({ + value: valueSchema, + sourceUrl: z.string().url().nullable(), + confidence: z.number().min(0).max(1), + }); +} + +export const OrgProfileSchema = z.object({ + /** EIN-qualified legal name, as it appears on the org's IRS filings. */ + legalName: sourcedField(z.string().min(1)), + /** `##-#######` federal EIN. Null when the org couldn't be matched to a filing. */ + ein: sourcedField(z.string().regex(/^\d{2}-?\d{7}$/)).nullable(), + /** Verbatim or lightly-condensed mission statement. */ + mission: sourcedField(z.string().min(1)), + /** Free-text program-area tags (e.g. "youth mentoring", "food security"). */ + programAreas: sourcedField(z.array(z.string().min(1)).min(1)), + /** Municipality/county/region the org primarily serves within NH. */ + geographicScope: sourcedField(z.string().min(1)), + /** Most recent total-revenue figure, in whole dollars, from a 990-PF. */ + annualRevenue: sourcedField(z.number().nonnegative()).nullable(), + /** Populations named as beneficiaries in the org's own materials. */ + targetPopulations: sourcedField(z.array(z.string().min(1))), +}); +export type OrgProfile = z.infer; diff --git a/packages/outreach-ai/src/embeddings.ts b/packages/outreach-ai/src/embeddings.ts new file mode 100644 index 0000000..54d5cfa --- /dev/null +++ b/packages/outreach-ai/src/embeddings.ts @@ -0,0 +1,61 @@ +// Copied/adapted from novelpad-desktop packages/ai/src/embeddings.ts @ 62c56b87 +// Kept verbatim. NEVER change EMBEDDING_MODEL/EMBEDDING_DIMENSIONS below — +// `gemini-embedding-001` @ 1536 must stay identical for HelmDocs RAG vector compat. + +import { getAi } from './gemini.js'; + +const EMBEDDING_MODEL = 'gemini-embedding-001'; +const EMBEDDING_DIMENSIONS = 1536; +const MAX_BATCH_SIZE = 100; + +/** + * Generate an embedding for a single text, using RETRIEVAL_QUERY task type + * (optimized for search queries). + */ +export async function generateQueryEmbedding( + text: string, +): Promise { + const result = await getAi().models.embedContent({ + model: EMBEDDING_MODEL, + contents: text, + config: { taskType: 'RETRIEVAL_QUERY', outputDimensionality: EMBEDDING_DIMENSIONS }, + }); + return result.embeddings![0].values!; +} + +/** + * Generate an embedding for a single text, using RETRIEVAL_DOCUMENT task type + * (optimized for document indexing). + */ +export async function generateDocumentEmbedding( + text: string, +): Promise { + const result = await getAi().models.embedContent({ + model: EMBEDDING_MODEL, + contents: text, + config: { taskType: 'RETRIEVAL_DOCUMENT', outputDimensionality: EMBEDDING_DIMENSIONS }, + }); + return result.embeddings![0].values!; +} + +/** + * Generate embeddings for multiple texts in batches. + * Uses RETRIEVAL_DOCUMENT task type. Batches requests to stay within API limits. + */ +export async function generateDocumentEmbeddings( + texts: string[], +): Promise { + const allEmbeddings: number[][] = []; + + for (let i = 0; i < texts.length; i += MAX_BATCH_SIZE) { + const batch = texts.slice(i, i + MAX_BATCH_SIZE); + const result = await getAi().models.embedContent({ + model: EMBEDDING_MODEL, + contents: batch, + config: { taskType: 'RETRIEVAL_DOCUMENT', outputDimensionality: EMBEDDING_DIMENSIONS }, + }); + allEmbeddings.push(...result.embeddings!.map((e) => e.values!)); + } + + return allEmbeddings; +} diff --git a/packages/outreach-ai/src/gemini.ts b/packages/outreach-ai/src/gemini.ts new file mode 100644 index 0000000..0c9dc3c --- /dev/null +++ b/packages/outreach-ai/src/gemini.ts @@ -0,0 +1,67 @@ +// Copied/adapted from novelpad-desktop packages/ai/src/gemini.ts @ 62c56b87 +// Kept verbatim, GCP service-account key resolution logic unchanged. + +import { GoogleGenAI } from '@google/genai'; +import fs from 'fs'; +import path from 'path'; + +function findMonorepoRoot(from: string): string { + let dir = from; + while (dir !== path.dirname(dir)) { + try { + const pkg = JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf8')); + if (pkg.workspaces) return dir; + } catch {} + dir = path.dirname(dir); + } + return from; +} + +function resolveKeyFile() { + const keyFilePath = process.env.GCP_SERVICE_ACCOUNT_KEY_PATH; + if (!keyFilePath) return undefined; + if (path.isAbsolute(keyFilePath)) { + return JSON.parse(fs.readFileSync(keyFilePath, 'utf8')); + } + // Try resolving relative to cwd first + const fromCwd = path.resolve(process.cwd(), keyFilePath); + if (fs.existsSync(fromCwd)) { + return JSON.parse(fs.readFileSync(fromCwd, 'utf8')); + } + // Fall back to resolving relative to monorepo root + const root = findMonorepoRoot(process.cwd()); + const fromRoot = path.resolve(root, keyFilePath); + if (fs.existsSync(fromRoot)) { + return JSON.parse(fs.readFileSync(fromRoot, 'utf8')); + } + // Last resort: try normalizing away excess ../ and resolve from root + const normalized = path.normalize(keyFilePath); + const basename = normalized.split(path.sep).filter(s => s !== '..').join(path.sep); + const fromRootNormalized = path.resolve(root, basename); + return JSON.parse(fs.readFileSync(fromRootNormalized, 'utf8')); +} + +let _ai: GoogleGenAI | null = null; + +export function getAi(): GoogleGenAI { + if (_ai == null) { + const credentials = resolveKeyFile(); + _ai = new GoogleGenAI({ + vertexai: true, + project: process.env.GCP_PROJECT_ID, + location: process.env.GCP_LOCATION ?? 'global', + googleAuthOptions: credentials ? { credentials } : undefined, + }); + } + return _ai; +} + +/** + * Default Vertex model for agent calls. Env-overridable via `CHAT_MODEL` so a + * working model (from `yarn workspace @novelpad/ai probe:vertex `) can be + * set per environment without a code change. Defaults to the GA `gemini-2.5-pro` + * — NOT a `-preview` name, which can be disabled in a given GCP project and then + * fails silently (404 the SDK swallows to empty output / 0 tokens). + * Per-call override: `invokeVertex({ model })`. + */ +export const CHAT_MODEL = process.env.CHAT_MODEL ?? 'gemini-2.5-pro'; diff --git a/packages/outreach-ai/src/index.ts b/packages/outreach-ai/src/index.ts new file mode 100644 index 0000000..a94db87 --- /dev/null +++ b/packages/outreach-ai/src/index.ts @@ -0,0 +1,7 @@ +export * from './gemini.js'; +export * from './embeddings.js'; +export * from './models.js'; +export * from './agents/org-profiler/schema.js'; +export * from './agents/org-profiler/run.js'; +export * from './agents/mission-fit-judge/schema.js'; +export * from './agents/mission-fit-judge/run.js'; diff --git a/packages/outreach-ai/src/models.ts b/packages/outreach-ai/src/models.ts new file mode 100644 index 0000000..b7787c4 --- /dev/null +++ b/packages/outreach-ai/src/models.ts @@ -0,0 +1,18 @@ +/** + * Model-tier constants for @novelpad/outreach-ai agent calls. + * + * Tiering rule: reach for `JUDGE_MODEL` — the frontier tier — wherever a + * wrong answer can reach a prospect. That means any call gating or shaping + * something a real NH nonprofit contact will read or that gates a match into + * outreach: the mission-fit judge's verdict (a wrong "fit: true" sends an + * off-mission email to a real org) and personalization QA on outbound Apollo + * sequence copy. + * + * Use `BULK_MODEL` for everything upstream and internal, where a wrong + * answer is still caught by a deterministic gate or a human reviewer before + * anything reaches a prospect: classification, effort estimates, and + * first-pass org-profile extraction (the profiler's output is reviewed by a + * human before a match is ever scored). + */ +export const BULK_MODEL = 'gemini-2.5-flash'; +export const JUDGE_MODEL = 'gemini-2.5-pro'; diff --git a/packages/outreach-ai/tsconfig.json b/packages/outreach-ai/tsconfig.json new file mode 100644 index 0000000..7fd6772 --- /dev/null +++ b/packages/outreach-ai/tsconfig.json @@ -0,0 +1,21 @@ +{ + "extends": "@novelpad/config/tsconfig.base.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./src", + "module": "ESNext", + "moduleResolution": "Bundler", + "target": "ES2022", + "lib": ["ES2022"], + "types": ["node"], + "declaration": true, + "sourceMap": true, + "skipLibCheck": true, + "noUncheckedIndexedAccess": false, + "paths": { + "#~/*": ["./src/*"] + } + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "src/**/*.test.ts"] +} diff --git a/packages/outreach-core/README.md b/packages/outreach-core/README.md new file mode 100644 index 0000000..a5fc9dc --- /dev/null +++ b/packages/outreach-core/README.md @@ -0,0 +1,54 @@ +# @novelpad/outreach-core + +Domain/data package for the HelmDocs grant-match outreach engine: the Drizzle +schema for grants, orgs, org profiles, contacts, matches, and pipeline +events, plus the actions/queries that read and write them. + +Modeled on `packages/core` in [novelpad-desktop](https://gitea.stephenmann.io/NovelPad/novelpad-desktop), +following the same [action/query pattern](https://gitea.stephenmann.io/NovelPad/novelpad-desktop/src/branch/master/docs/features/database-action-query-pattern.md): +every read lives in a domain's `queries/`, every write in its `actions/`, +both barrel-exported, and route handlers/workflows/hooks never touch +`db.select()`/`db.insert()` directly. + +## Domains + +- `grants/` — open grant opportunities (Grants.gov, NH state, 990-PF, PND RSS, Candid). Ingestion upserts on `sourceUrl`; an hourly sweep expires anything past its close date. +- `orgs/` — NH nonprofits, plus `org_profiles` (LLM/agent-researched enrichment: mission, programs, known funders, budget band, citation sources). +- `matches/` — a scored `(org, grant)` pair. `hard-gates.ts` is the deterministic, DB-free pass/fail predicate (entity eligibility, geography, ≥21-day runway, ≥$10k ceiling, application-form support) that runs *before* the weighted LLM subscoring pass — see its module doc for why it's kept pure. +- `pipeline/` — outreach lifecycle events (`enrolled` → `sent` → `opened`/`replied`/... → `demo_booked` → `converted`), synced from Apollo and recorded internally. + +## Deliberate deviations from `@novelpad/core` + +This package is **server-only** — there is no client app, so several things +`@novelpad/core` does for its PGlite/OPFS client don't apply here: + +- **No PGlite, no client schema, no sync.** There's exactly one database + handle type (`NpOutreachDatabase`/`NpOutreachTransaction` in `src/db/db.ts`), + not the client/server pair (`NpClientDatabase` vs `NpDatabase`) `@novelpad/core` + needs to support both a local and a remote copy of the same data. +- **No `novelpad_core_dev` export condition.** `@novelpad/core`'s `package.json` + uses a custom `novelpad_core_dev` condition so dev builds resolve straight + to `src/*.ts` without a build step. This package's `exports` map only has + plain `types`/`default` conditions — run `yarn build` (or `yarn dev` for a + watch build) before consuming it from a sibling workspace in dev. +- **No `build:migration-files` bundling step.** `@novelpad/core`'s + `create:migration` also runs a script that bundles generated SQL into a + TS module the client can import for its OPFS-based local migrator. There + is no local migrator here, so `bin/create-migration` only runs + `drizzle-kit generate` against the single server config. +- **Root `index.ts` is intentionally small.** It exports only the schema, + DB types, and the pure `hard-gates` predicate — not domain client barrels, + since every domain here is server-only. Everything else lives behind + `index.server.ts` / the `./server` export condition. + +## Scripts + +``` +yarn build # tsc +yarn dev # tsc --watch +yarn typecheck # tsc --noEmit +yarn test # vitest run +yarn create:migration # drizzle-kit generate against drizzle.config.ts +yarn migrate # drizzle-kit migrate +yarn studio # drizzle-kit studio +``` diff --git a/packages/outreach-core/bin/create-migration b/packages/outreach-core/bin/create-migration new file mode 100755 index 0000000..9942e39 --- /dev/null +++ b/packages/outreach-core/bin/create-migration @@ -0,0 +1,16 @@ +#!/bin/bash +# Copied/adapted from novelpad-desktop packages/core/bin/create-migration @ 62c56b87 +# Deviation: no client schema in this package, so only the single (server) +# drizzle config is generated against, and there is no separate +# build:migration-files bundling step to run afterward. + +if [ "$#" -ne 1 ]; then + echo "Usage: create-migration " + echo "Or: yarn create:migration " + echo "Or: npm run create:migration -- " + exit 9 +fi + +echo "Creating migration: ${1}" +yarn build +yarn drizzle-kit generate --config drizzle.config.ts --name "${1}" diff --git a/packages/outreach-core/drizzle.config.ts b/packages/outreach-core/drizzle.config.ts new file mode 100644 index 0000000..329f707 --- /dev/null +++ b/packages/outreach-core/drizzle.config.ts @@ -0,0 +1,14 @@ +// Copied/adapted from novelpad-desktop packages/core/drizzle.config.ts @ 62c56b87 +import { defineConfig } from 'drizzle-kit'; + +export default defineConfig({ + schema: './src/db/schema.ts', + out: './drizzle/server', + dialect: 'postgresql', + migrations: { + prefix: 'unix', + }, + dbCredentials: { + url: process.env.DATABASE_URL!, + }, +}); diff --git a/packages/outreach-core/drizzle/server/1784214387_initial-schema.sql b/packages/outreach-core/drizzle/server/1784214387_initial-schema.sql new file mode 100644 index 0000000..f0e7fe6 --- /dev/null +++ b/packages/outreach-core/drizzle/server/1784214387_initial-schema.sql @@ -0,0 +1,134 @@ +CREATE EXTENSION IF NOT EXISTS vector;--> statement-breakpoint +CREATE TYPE "public"."application_effort_estimate" AS ENUM('loi_only', 'short_form', 'full_federal', 'unknown');--> statement-breakpoint +CREATE TYPE "public"."contact_priority" AS ENUM('named', 'generic');--> statement-breakpoint +CREATE TYPE "public"."contact_source_provider" AS ENUM('apollo', 'irs_990', 'website', 'manual');--> statement-breakpoint +CREATE TYPE "public"."email_status" AS ENUM('unverified', 'valid', 'risky', 'invalid');--> statement-breakpoint +CREATE TYPE "public"."grant_source" AS ENUM('grants_gov', 'nh_state', 'irs_990pf', 'pnd_rss', 'candid', 'manual');--> statement-breakpoint +CREATE TYPE "public"."grant_status" AS ENUM('open', 'expired', 'closed');--> statement-breakpoint +CREATE TYPE "public"."icp_band" AS ENUM('below', 'primary', 'above', 'unknown');--> statement-breakpoint +CREATE TYPE "public"."match_reject_reason" AS ENUM('wrong_eligibility', 'wrong_geography', 'bad_capacity_fit', 'weak_mission_fit', 'stale_deadline', 'bad_contact', 'other');--> statement-breakpoint +CREATE TYPE "public"."match_review_status" AS ENUM('pending', 'approved', 'rejected', 'edited');--> statement-breakpoint +CREATE TYPE "public"."pipeline_event_type" AS ENUM('enrolled', 'sent', 'opened', 'replied', 'bounced', 'unsubscribed', 'brief_requested', 'brief_sent', 'demo_booked', 'demo_held', 'pilot_started', 'converted');--> statement-breakpoint +CREATE TYPE "public"."registration_status" AS ENUM('good_standing', 'lapsed', 'unknown');--> statement-breakpoint +CREATE TABLE "contacts" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "org_id" uuid NOT NULL, + "full_name" text, + "title" text, + "email" text, + "email_status" "email_status" DEFAULT 'unverified' NOT NULL, + "source_provider" "contact_source_provider", + "priority" "contact_priority" DEFAULT 'generic' NOT NULL, + "created_at" timestamp with time zone DEFAULT now() +); +--> statement-breakpoint +CREATE TABLE "grants" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "funder" text NOT NULL, + "title" text NOT NULL, + "synopsis" text, + "eligibility_entity_types" text[], + "geographic_scope" text, + "program_areas" text[], + "award_floor" integer, + "award_ceiling" integer, + "expected_awards_count" integer, + "open_date" timestamp with time zone, + "close_date" timestamp with time zone, + "match_requirement" boolean DEFAULT false NOT NULL, + "application_effort_estimate" "application_effort_estimate" DEFAULT 'unknown' NOT NULL, + "application_form_supported" boolean DEFAULT false NOT NULL, + "source_url" text NOT NULL, + "source" "grant_source" NOT NULL, + "status" "grant_status" DEFAULT 'open' NOT NULL, + "synopsis_embedding" vector(1536), + "last_verified_at" timestamp with time zone, + "created_at" timestamp with time zone DEFAULT now(), + "updated_at" timestamp with time zone DEFAULT now() +); +--> statement-breakpoint +CREATE TABLE "matches" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "org_id" uuid NOT NULL, + "grant_id" uuid NOT NULL, + "total_score" integer NOT NULL, + "subscores" jsonb, + "hard_gates_passed" boolean DEFAULT false NOT NULL, + "easy_win" boolean DEFAULT false NOT NULL, + "rationale" jsonb, + "review_status" "match_review_status" DEFAULT 'pending' NOT NULL, + "reject_reason" "match_reject_reason", + "is_hero" boolean DEFAULT false NOT NULL, + "created_at" timestamp with time zone DEFAULT now(), + "updated_at" timestamp with time zone DEFAULT now() +); +--> statement-breakpoint +CREATE TABLE "org_profiles" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "org_id" uuid NOT NULL, + "mission_statement" text, + "programs" jsonb, + "service_geography" text, + "recent_news" jsonb, + "known_funders" jsonb, + "staff" jsonb, + "budget_band" text, + "sources" jsonb, + "confidence" real, + "profile_embedding" vector(1536), + "created_at" timestamp with time zone DEFAULT now() +); +--> statement-breakpoint +CREATE TABLE "orgs" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "name" text NOT NULL, + "city" text, + "state" text DEFAULT 'NH' NOT NULL, + "ein" text, + "ntee_code" text, + "total_revenue" integer, + "fiscal_year_end" text, + "registration_status" "registration_status" DEFAULT 'unknown' NOT NULL, + "icp_band" "icp_band" DEFAULT 'unknown' NOT NULL, + "source_registry" text, + "created_at" timestamp with time zone DEFAULT now(), + "updated_at" timestamp with time zone DEFAULT now() +); +--> statement-breakpoint +CREATE TABLE "pipeline_events" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "org_id" uuid, + "contact_id" uuid, + "match_id" uuid, + "event_type" "pipeline_event_type" NOT NULL, + "payload" jsonb, + "occurred_at" timestamp with time zone DEFAULT now() NOT NULL, + "created_at" timestamp with time zone DEFAULT now() +); +--> statement-breakpoint +ALTER TABLE "contacts" ADD CONSTRAINT "contacts_org_id_orgs_id_fk" FOREIGN KEY ("org_id") REFERENCES "public"."orgs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "matches" ADD CONSTRAINT "matches_org_id_orgs_id_fk" FOREIGN KEY ("org_id") REFERENCES "public"."orgs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "matches" ADD CONSTRAINT "matches_grant_id_grants_id_fk" FOREIGN KEY ("grant_id") REFERENCES "public"."grants"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "org_profiles" ADD CONSTRAINT "org_profiles_org_id_orgs_id_fk" FOREIGN KEY ("org_id") REFERENCES "public"."orgs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pipeline_events" ADD CONSTRAINT "pipeline_events_org_id_orgs_id_fk" FOREIGN KEY ("org_id") REFERENCES "public"."orgs"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pipeline_events" ADD CONSTRAINT "pipeline_events_contact_id_contacts_id_fk" FOREIGN KEY ("contact_id") REFERENCES "public"."contacts"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "pipeline_events" ADD CONSTRAINT "pipeline_events_match_id_matches_id_fk" FOREIGN KEY ("match_id") REFERENCES "public"."matches"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +CREATE INDEX "idx_contacts_org" ON "contacts" USING btree ("org_id");--> statement-breakpoint +CREATE INDEX "idx_contacts_email" ON "contacts" USING btree ("email");--> statement-breakpoint +CREATE UNIQUE INDEX "idx_grants_source_url" ON "grants" USING btree ("source_url");--> statement-breakpoint +CREATE INDEX "idx_grants_status" ON "grants" USING btree ("status");--> statement-breakpoint +CREATE INDEX "idx_grants_close_date" ON "grants" USING btree ("close_date");--> statement-breakpoint +CREATE INDEX "idx_grants_source" ON "grants" USING btree ("source");--> statement-breakpoint +CREATE INDEX "grants_synopsis_embedding_idx" ON "grants" USING hnsw ("synopsis_embedding" vector_cosine_ops);--> statement-breakpoint +CREATE INDEX "idx_matches_org" ON "matches" USING btree ("org_id");--> statement-breakpoint +CREATE INDEX "idx_matches_grant" ON "matches" USING btree ("grant_id");--> statement-breakpoint +CREATE INDEX "idx_matches_review_status" ON "matches" USING btree ("review_status");--> statement-breakpoint +CREATE INDEX "idx_org_profiles_org" ON "org_profiles" USING btree ("org_id");--> statement-breakpoint +CREATE INDEX "org_profiles_embedding_idx" ON "org_profiles" USING hnsw ("profile_embedding" vector_cosine_ops);--> statement-breakpoint +CREATE UNIQUE INDEX "idx_orgs_ein" ON "orgs" USING btree ("ein") WHERE "orgs"."ein" IS NOT NULL;--> statement-breakpoint +CREATE INDEX "idx_orgs_icp_band" ON "orgs" USING btree ("icp_band");--> statement-breakpoint +CREATE INDEX "idx_orgs_state" ON "orgs" USING btree ("state");--> statement-breakpoint +CREATE INDEX "idx_pipeline_events_org" ON "pipeline_events" USING btree ("org_id");--> statement-breakpoint +CREATE INDEX "idx_pipeline_events_contact" ON "pipeline_events" USING btree ("contact_id");--> statement-breakpoint +CREATE INDEX "idx_pipeline_events_match" ON "pipeline_events" USING btree ("match_id");--> statement-breakpoint +CREATE INDEX "idx_pipeline_events_type_occurred" ON "pipeline_events" USING btree ("event_type","occurred_at"); \ No newline at end of file diff --git a/packages/outreach-core/drizzle/server/meta/1784214387_snapshot.json b/packages/outreach-core/drizzle/server/meta/1784214387_snapshot.json new file mode 100644 index 0000000..8efbd41 --- /dev/null +++ b/packages/outreach-core/drizzle/server/meta/1784214387_snapshot.json @@ -0,0 +1,1120 @@ +{ + "id": "ea850bc6-43a1-4ce1-88d4-dbac85993138", + "prevId": "00000000-0000-0000-0000-000000000000", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.contacts": { + "name": "contacts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "full_name": { + "name": "full_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email_status": { + "name": "email_status", + "type": "email_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'unverified'" + }, + "source_provider": { + "name": "source_provider", + "type": "contact_source_provider", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "priority": { + "name": "priority", + "type": "contact_priority", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'generic'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "idx_contacts_org": { + "name": "idx_contacts_org", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_contacts_email": { + "name": "idx_contacts_email", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "contacts_org_id_orgs_id_fk": { + "name": "contacts_org_id_orgs_id_fk", + "tableFrom": "contacts", + "tableTo": "orgs", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.grants": { + "name": "grants", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "funder": { + "name": "funder", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "synopsis": { + "name": "synopsis", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "eligibility_entity_types": { + "name": "eligibility_entity_types", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "geographic_scope": { + "name": "geographic_scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "program_areas": { + "name": "program_areas", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "award_floor": { + "name": "award_floor", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "award_ceiling": { + "name": "award_ceiling", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "expected_awards_count": { + "name": "expected_awards_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "open_date": { + "name": "open_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "close_date": { + "name": "close_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "match_requirement": { + "name": "match_requirement", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "application_effort_estimate": { + "name": "application_effort_estimate", + "type": "application_effort_estimate", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'unknown'" + }, + "application_form_supported": { + "name": "application_form_supported", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "source_url": { + "name": "source_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "grant_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "grant_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'open'" + }, + "synopsis_embedding": { + "name": "synopsis_embedding", + "type": "vector(1536)", + "primaryKey": false, + "notNull": false + }, + "last_verified_at": { + "name": "last_verified_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "idx_grants_source_url": { + "name": "idx_grants_source_url", + "columns": [ + { + "expression": "source_url", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_grants_status": { + "name": "idx_grants_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_grants_close_date": { + "name": "idx_grants_close_date", + "columns": [ + { + "expression": "close_date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_grants_source": { + "name": "idx_grants_source", + "columns": [ + { + "expression": "source", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "grants_synopsis_embedding_idx": { + "name": "grants_synopsis_embedding_idx", + "columns": [ + { + "expression": "synopsis_embedding", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "vector_cosine_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "hnsw", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.matches": { + "name": "matches", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "grant_id": { + "name": "grant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "total_score": { + "name": "total_score", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "subscores": { + "name": "subscores", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "hard_gates_passed": { + "name": "hard_gates_passed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "easy_win": { + "name": "easy_win", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "rationale": { + "name": "rationale", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "review_status": { + "name": "review_status", + "type": "match_review_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "reject_reason": { + "name": "reject_reason", + "type": "match_reject_reason", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "is_hero": { + "name": "is_hero", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "idx_matches_org": { + "name": "idx_matches_org", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_matches_grant": { + "name": "idx_matches_grant", + "columns": [ + { + "expression": "grant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_matches_review_status": { + "name": "idx_matches_review_status", + "columns": [ + { + "expression": "review_status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "matches_org_id_orgs_id_fk": { + "name": "matches_org_id_orgs_id_fk", + "tableFrom": "matches", + "tableTo": "orgs", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "matches_grant_id_grants_id_fk": { + "name": "matches_grant_id_grants_id_fk", + "tableFrom": "matches", + "tableTo": "grants", + "columnsFrom": [ + "grant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.org_profiles": { + "name": "org_profiles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "mission_statement": { + "name": "mission_statement", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "programs": { + "name": "programs", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "service_geography": { + "name": "service_geography", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "recent_news": { + "name": "recent_news", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "known_funders": { + "name": "known_funders", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "staff": { + "name": "staff", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "budget_band": { + "name": "budget_band", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sources": { + "name": "sources", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "confidence": { + "name": "confidence", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "profile_embedding": { + "name": "profile_embedding", + "type": "vector(1536)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "idx_org_profiles_org": { + "name": "idx_org_profiles_org", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "org_profiles_embedding_idx": { + "name": "org_profiles_embedding_idx", + "columns": [ + { + "expression": "profile_embedding", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "vector_cosine_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "hnsw", + "with": {} + } + }, + "foreignKeys": { + "org_profiles_org_id_orgs_id_fk": { + "name": "org_profiles_org_id_orgs_id_fk", + "tableFrom": "org_profiles", + "tableTo": "orgs", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.orgs": { + "name": "orgs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "city": { + "name": "city", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'NH'" + }, + "ein": { + "name": "ein", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ntee_code": { + "name": "ntee_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "total_revenue": { + "name": "total_revenue", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "fiscal_year_end": { + "name": "fiscal_year_end", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "registration_status": { + "name": "registration_status", + "type": "registration_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'unknown'" + }, + "icp_band": { + "name": "icp_band", + "type": "icp_band", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'unknown'" + }, + "source_registry": { + "name": "source_registry", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "idx_orgs_ein": { + "name": "idx_orgs_ein", + "columns": [ + { + "expression": "ein", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"orgs\".\"ein\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_orgs_icp_band": { + "name": "idx_orgs_icp_band", + "columns": [ + { + "expression": "icp_band", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_orgs_state": { + "name": "idx_orgs_state", + "columns": [ + { + "expression": "state", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pipeline_events": { + "name": "pipeline_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "org_id": { + "name": "org_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "contact_id": { + "name": "contact_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "match_id": { + "name": "match_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "event_type": { + "name": "event_type", + "type": "pipeline_event_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "occurred_at": { + "name": "occurred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "idx_pipeline_events_org": { + "name": "idx_pipeline_events_org", + "columns": [ + { + "expression": "org_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_pipeline_events_contact": { + "name": "idx_pipeline_events_contact", + "columns": [ + { + "expression": "contact_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_pipeline_events_match": { + "name": "idx_pipeline_events_match", + "columns": [ + { + "expression": "match_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_pipeline_events_type_occurred": { + "name": "idx_pipeline_events_type_occurred", + "columns": [ + { + "expression": "event_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "occurred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pipeline_events_org_id_orgs_id_fk": { + "name": "pipeline_events_org_id_orgs_id_fk", + "tableFrom": "pipeline_events", + "tableTo": "orgs", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "pipeline_events_contact_id_contacts_id_fk": { + "name": "pipeline_events_contact_id_contacts_id_fk", + "tableFrom": "pipeline_events", + "tableTo": "contacts", + "columnsFrom": [ + "contact_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "pipeline_events_match_id_matches_id_fk": { + "name": "pipeline_events_match_id_matches_id_fk", + "tableFrom": "pipeline_events", + "tableTo": "matches", + "columnsFrom": [ + "match_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.application_effort_estimate": { + "name": "application_effort_estimate", + "schema": "public", + "values": [ + "loi_only", + "short_form", + "full_federal", + "unknown" + ] + }, + "public.contact_priority": { + "name": "contact_priority", + "schema": "public", + "values": [ + "named", + "generic" + ] + }, + "public.contact_source_provider": { + "name": "contact_source_provider", + "schema": "public", + "values": [ + "apollo", + "irs_990", + "website", + "manual" + ] + }, + "public.email_status": { + "name": "email_status", + "schema": "public", + "values": [ + "unverified", + "valid", + "risky", + "invalid" + ] + }, + "public.grant_source": { + "name": "grant_source", + "schema": "public", + "values": [ + "grants_gov", + "nh_state", + "irs_990pf", + "pnd_rss", + "candid", + "manual" + ] + }, + "public.grant_status": { + "name": "grant_status", + "schema": "public", + "values": [ + "open", + "expired", + "closed" + ] + }, + "public.icp_band": { + "name": "icp_band", + "schema": "public", + "values": [ + "below", + "primary", + "above", + "unknown" + ] + }, + "public.match_reject_reason": { + "name": "match_reject_reason", + "schema": "public", + "values": [ + "wrong_eligibility", + "wrong_geography", + "bad_capacity_fit", + "weak_mission_fit", + "stale_deadline", + "bad_contact", + "other" + ] + }, + "public.match_review_status": { + "name": "match_review_status", + "schema": "public", + "values": [ + "pending", + "approved", + "rejected", + "edited" + ] + }, + "public.pipeline_event_type": { + "name": "pipeline_event_type", + "schema": "public", + "values": [ + "enrolled", + "sent", + "opened", + "replied", + "bounced", + "unsubscribed", + "brief_requested", + "brief_sent", + "demo_booked", + "demo_held", + "pilot_started", + "converted" + ] + }, + "public.registration_status": { + "name": "registration_status", + "schema": "public", + "values": [ + "good_standing", + "lapsed", + "unknown" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/packages/outreach-core/drizzle/server/meta/_journal.json b/packages/outreach-core/drizzle/server/meta/_journal.json new file mode 100644 index 0000000..667293d --- /dev/null +++ b/packages/outreach-core/drizzle/server/meta/_journal.json @@ -0,0 +1,13 @@ +{ + "version": "7", + "dialect": "postgresql", + "entries": [ + { + "idx": 0, + "version": "7", + "when": 1784214387212, + "tag": "1784214387_initial-schema", + "breakpoints": true + } + ] +} \ No newline at end of file diff --git a/packages/outreach-core/package.json b/packages/outreach-core/package.json new file mode 100644 index 0000000..fe0e9ed --- /dev/null +++ b/packages/outreach-core/package.json @@ -0,0 +1,46 @@ +{ + "name": "@novelpad/outreach-core", + "packageManager": "yarn@4.5.0", + "sideEffects": false, + "type": "module", + "version": "1.0.0", + "private": true, + "module": "dist/index.js", + "scripts": { + "build": "tsc", + "dev": "tsc --watch", + "typecheck": "tsc --noEmit", + "test": "vitest run", + "create:migration": "bin/create-migration", + "migrate": "drizzle-kit migrate", + "studio": "drizzle-kit studio" + }, + "imports": { + "#~/*": "./dist/*" + }, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + }, + "./server": { + "types": "./dist/index.server.d.ts", + "default": "./dist/index.server.js" + } + }, + "dependencies": { + "drizzle-orm": "0.44.6", + "pg": "8.20.0", + "zod": "^3.25.0" + }, + "devDependencies": { + "@novelpad/config": "workspace:^", + "@types/node": "^22", + "@types/pg": "8.20.0", + "drizzle-kit": "^0.31.5", + "npm-run-all": "^4.1.5", + "tsx": "^4.19.2", + "typescript": "^5.9.3", + "vitest": "^3.2.4" + } +} diff --git a/packages/outreach-core/src/db/db.ts b/packages/outreach-core/src/db/db.ts new file mode 100644 index 0000000..5e329d3 --- /dev/null +++ b/packages/outreach-core/src/db/db.ts @@ -0,0 +1,17 @@ +import type { NodePgDatabase } from 'drizzle-orm/node-postgres'; +import type { PgQueryResultHKT, PgTransaction } from 'drizzle-orm/pg-core'; + +import { schema } from './schema.js'; + +export { schema }; + +/** + * The outreach-core database handle. This package is server-only — there is + * no client/local database, so unlike @novelpad/core there is only ever one + * of these (no `NpClientDatabase` counterpart). + */ +export type NpOutreachDatabase = NodePgDatabase; + +export type NpOutreachTransaction< + TQueryResult extends PgQueryResultHKT = PgQueryResultHKT, +> = PgTransaction; diff --git a/packages/outreach-core/src/db/index.ts b/packages/outreach-core/src/db/index.ts new file mode 100644 index 0000000..3a37ae3 --- /dev/null +++ b/packages/outreach-core/src/db/index.ts @@ -0,0 +1,2 @@ +export * from './db.js'; +export * from './schema.js'; diff --git a/packages/outreach-core/src/db/schema.ts b/packages/outreach-core/src/db/schema.ts new file mode 100644 index 0000000..05c8180 --- /dev/null +++ b/packages/outreach-core/src/db/schema.ts @@ -0,0 +1,339 @@ +import { sql } from 'drizzle-orm'; +import { + boolean, + index, + integer, + jsonb, + pgEnum, + pgTable, + real, + text, + timestamp, + uniqueIndex, + uuid, + vector, +} from 'drizzle-orm/pg-core'; + +// --------------------------------------------------------------------------- +// Enums +// --------------------------------------------------------------------------- + +export const applicationEffortEnum = pgEnum('application_effort_estimate', [ + 'loi_only', + 'short_form', + 'full_federal', + 'unknown', +]); + +export const grantSourceEnum = pgEnum('grant_source', [ + 'grants_gov', + 'nh_state', + 'irs_990pf', + 'pnd_rss', + 'candid', + 'manual', +]); + +export const grantStatusEnum = pgEnum('grant_status', [ + 'open', + 'expired', + 'closed', +]); + +export const registrationStatusEnum = pgEnum('registration_status', [ + 'good_standing', + 'lapsed', + 'unknown', +]); + +export const icpBandEnum = pgEnum('icp_band', [ + 'below', + 'primary', + 'above', + 'unknown', +]); + +export const emailStatusEnum = pgEnum('email_status', [ + 'unverified', + 'valid', + 'risky', + 'invalid', +]); + +export const contactSourceEnum = pgEnum('contact_source_provider', [ + 'apollo', + 'irs_990', + 'website', + 'manual', +]); + +export const contactPriorityEnum = pgEnum('contact_priority', [ + 'named', + 'generic', +]); + +export const matchReviewStatusEnum = pgEnum('match_review_status', [ + 'pending', + 'approved', + 'rejected', + 'edited', +]); + +export const matchRejectReasonEnum = pgEnum('match_reject_reason', [ + 'wrong_eligibility', + 'wrong_geography', + 'bad_capacity_fit', + 'weak_mission_fit', + 'stale_deadline', + 'bad_contact', + 'other', +]); + +export const pipelineEventTypeEnum = pgEnum('pipeline_event_type', [ + 'enrolled', + 'sent', + 'opened', + 'replied', + 'bounced', + 'unsubscribed', + 'brief_requested', + 'brief_sent', + 'demo_booked', + 'demo_held', + 'pilot_started', + 'converted', +]); + +// --------------------------------------------------------------------------- +// grants — open grant opportunities ingested from Grants.gov, NH state +// sources, IRS 990-PF filings, Philanthropy News Digest RSS, and Candid. +// --------------------------------------------------------------------------- + +export const grants = pgTable( + 'grants', + { + id: uuid('id') + .primaryKey() + .default(sql`gen_random_uuid()`), + funder: text('funder').notNull(), + title: text('title').notNull(), + synopsis: text('synopsis'), + eligibilityEntityTypes: text('eligibility_entity_types').array(), + geographicScope: text('geographic_scope'), + // NTEE codes, e.g. ['A', 'B20', 'P'] + programAreas: text('program_areas').array(), + awardFloor: integer('award_floor'), + awardCeiling: integer('award_ceiling'), + expectedAwardsCount: integer('expected_awards_count'), + openDate: timestamp('open_date', { withTimezone: true }), + closeDate: timestamp('close_date', { withTimezone: true }), + matchRequirement: boolean('match_requirement').notNull().default(false), + applicationEffortEstimate: applicationEffortEnum( + 'application_effort_estimate', + ) + .notNull() + .default('unknown'), + applicationFormSupported: boolean('application_form_supported') + .notNull() + .default(false), + sourceUrl: text('source_url').notNull(), + source: grantSourceEnum('source').notNull(), + status: grantStatusEnum('status').notNull().default('open'), + synopsisEmbedding: vector('synopsis_embedding', { dimensions: 1536 }), + lastVerifiedAt: timestamp('last_verified_at', { withTimezone: true }), + createdAt: timestamp('created_at', { withTimezone: true }).defaultNow(), + updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow(), + }, + (t) => [ + // Upsert key: sources are re-crawled and re-ingested on a schedule. + uniqueIndex('idx_grants_source_url').on(t.sourceUrl), + index('idx_grants_status').on(t.status), + index('idx_grants_close_date').on(t.closeDate), + index('idx_grants_source').on(t.source), + index('grants_synopsis_embedding_idx').using( + 'hnsw', + t.synopsisEmbedding.op('vector_cosine_ops'), + ), + ], +); + +// --------------------------------------------------------------------------- +// orgs — New Hampshire nonprofits being profiled and matched against grants. +// --------------------------------------------------------------------------- + +export const orgs = pgTable( + 'orgs', + { + id: uuid('id') + .primaryKey() + .default(sql`gen_random_uuid()`), + name: text('name').notNull(), + city: text('city'), + state: text('state').notNull().default('NH'), + ein: text('ein'), + nteeCode: text('ntee_code'), + totalRevenue: integer('total_revenue'), + fiscalYearEnd: text('fiscal_year_end'), + registrationStatus: registrationStatusEnum('registration_status') + .notNull() + .default('unknown'), + icpBand: icpBandEnum('icp_band').notNull().default('unknown'), + sourceRegistry: text('source_registry'), + createdAt: timestamp('created_at', { withTimezone: true }).defaultNow(), + updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow(), + }, + (t) => [ + // Partial unique index: EIN is unique when present, but many small + // orgs in early ingestion won't have one resolved yet. + uniqueIndex('idx_orgs_ein').on(t.ein).where(sql`${t.ein} IS NOT NULL`), + index('idx_orgs_icp_band').on(t.icpBand), + index('idx_orgs_state').on(t.state), + ], +); + +// --------------------------------------------------------------------------- +// org_profiles — LLM/agent-researched enrichment for an org. +// --------------------------------------------------------------------------- + +export const orgProfiles = pgTable( + 'org_profiles', + { + id: uuid('id') + .primaryKey() + .default(sql`gen_random_uuid()`), + orgId: uuid('org_id') + .notNull() + .references(() => orgs.id, { onDelete: 'cascade' }), + missionStatement: text('mission_statement'), + programs: jsonb('programs'), + serviceGeography: text('service_geography'), + recentNews: jsonb('recent_news'), + knownFunders: jsonb('known_funders'), + staff: jsonb('staff'), + budgetBand: text('budget_band'), + // Citation URLs backing the researched fields above. + sources: jsonb('sources'), + confidence: real('confidence'), + profileEmbedding: vector('profile_embedding', { dimensions: 1536 }), + createdAt: timestamp('created_at', { withTimezone: true }).defaultNow(), + }, + (t) => [ + index('idx_org_profiles_org').on(t.orgId), + index('org_profiles_embedding_idx').using( + 'hnsw', + t.profileEmbedding.op('vector_cosine_ops'), + ), + ], +); + +// --------------------------------------------------------------------------- +// contacts — people at an org, sourced from Apollo, 990 filings, or the +// org's own website. +// --------------------------------------------------------------------------- + +export const contacts = pgTable( + 'contacts', + { + id: uuid('id') + .primaryKey() + .default(sql`gen_random_uuid()`), + orgId: uuid('org_id') + .notNull() + .references(() => orgs.id, { onDelete: 'cascade' }), + fullName: text('full_name'), + title: text('title'), + email: text('email'), + emailStatus: emailStatusEnum('email_status').notNull().default('unverified'), + sourceProvider: contactSourceEnum('source_provider'), + priority: contactPriorityEnum('priority').notNull().default('generic'), + createdAt: timestamp('created_at', { withTimezone: true }).defaultNow(), + }, + (t) => [ + index('idx_contacts_org').on(t.orgId), + index('idx_contacts_email').on(t.email), + ], +); + +// --------------------------------------------------------------------------- +// matches — a scored (org, grant) pair, gated deterministically and then +// weighted by LLM subscores, queued for human review. +// --------------------------------------------------------------------------- + +export const matches = pgTable( + 'matches', + { + id: uuid('id') + .primaryKey() + .default(sql`gen_random_uuid()`), + orgId: uuid('org_id') + .notNull() + .references(() => orgs.id, { onDelete: 'cascade' }), + grantId: uuid('grant_id') + .notNull() + .references(() => grants.id, { onDelete: 'cascade' }), + totalScore: integer('total_score').notNull(), + subscores: jsonb('subscores'), + hardGatesPassed: boolean('hard_gates_passed').notNull().default(false), + easyWin: boolean('easy_win').notNull().default(false), + // LLM-produced citations backing the score/subscores. + rationale: jsonb('rationale'), + reviewStatus: matchReviewStatusEnum('review_status') + .notNull() + .default('pending'), + rejectReason: matchRejectReasonEnum('reject_reason'), + isHero: boolean('is_hero').notNull().default(false), + createdAt: timestamp('created_at', { withTimezone: true }).defaultNow(), + updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow(), + }, + (t) => [ + index('idx_matches_org').on(t.orgId), + index('idx_matches_grant').on(t.grantId), + index('idx_matches_review_status').on(t.reviewStatus), + ], +); + +// --------------------------------------------------------------------------- +// pipeline_events — outreach lifecycle events synced back from Apollo (and +// recorded internally) for an org/contact/match. +// --------------------------------------------------------------------------- + +export const pipelineEvents = pgTable( + 'pipeline_events', + { + id: uuid('id') + .primaryKey() + .default(sql`gen_random_uuid()`), + orgId: uuid('org_id').references(() => orgs.id, { onDelete: 'set null' }), + contactId: uuid('contact_id').references(() => contacts.id, { + onDelete: 'set null', + }), + matchId: uuid('match_id').references(() => matches.id, { + onDelete: 'set null', + }), + eventType: pipelineEventTypeEnum('event_type').notNull(), + payload: jsonb('payload'), + occurredAt: timestamp('occurred_at', { withTimezone: true }) + .notNull() + .defaultNow(), + createdAt: timestamp('created_at', { withTimezone: true }).defaultNow(), + }, + (t) => [ + index('idx_pipeline_events_org').on(t.orgId), + index('idx_pipeline_events_contact').on(t.contactId), + index('idx_pipeline_events_match').on(t.matchId), + index('idx_pipeline_events_type_occurred').on(t.eventType, t.occurredAt), + ], +); + +// --------------------------------------------------------------------------- +// Schema barrel +// --------------------------------------------------------------------------- + +export const schema = { + grants, + orgs, + orgProfiles, + contacts, + matches, + pipelineEvents, +}; diff --git a/packages/outreach-core/src/grants/actions/expire-closed-grants.server.ts b/packages/outreach-core/src/grants/actions/expire-closed-grants.server.ts new file mode 100644 index 0000000..0eb452b --- /dev/null +++ b/packages/outreach-core/src/grants/actions/expire-closed-grants.server.ts @@ -0,0 +1,28 @@ +import { and, eq, lt, sql } from 'drizzle-orm'; + +import type { NpOutreachDatabase, NpOutreachTransaction } from '#~/db/db.js'; +import { schema } from '#~/db/db.js'; + +/** + * Marks every currently-`open` grant whose `closeDate` has passed as + * `expired`, so it drops out of the active match/scoring pool. Grants with + * no `closeDate` (rolling/LOI-only programs) are left untouched. + * + * Returns the ids that were flipped, for logging/observability. + */ +export async function serverExpireClosedGrants( + db: NpOutreachDatabase | NpOutreachTransaction, +): Promise { + const rows = await db + .update(schema.grants) + .set({ status: 'expired', updatedAt: sql`now()` }) + .where( + and( + eq(schema.grants.status, 'open'), + lt(schema.grants.closeDate, sql`now()`), + ), + ) + .returning({ id: schema.grants.id }); + + return rows.map((r) => r.id); +} diff --git a/packages/outreach-core/src/grants/actions/index.server.ts b/packages/outreach-core/src/grants/actions/index.server.ts new file mode 100644 index 0000000..f91e9ee --- /dev/null +++ b/packages/outreach-core/src/grants/actions/index.server.ts @@ -0,0 +1,2 @@ +export * from './expire-closed-grants.server.js'; +export * from './insert-grants.server.js'; diff --git a/packages/outreach-core/src/grants/actions/insert-grants.server.ts b/packages/outreach-core/src/grants/actions/insert-grants.server.ts new file mode 100644 index 0000000..dea9f45 --- /dev/null +++ b/packages/outreach-core/src/grants/actions/insert-grants.server.ts @@ -0,0 +1,55 @@ +import { sql } from 'drizzle-orm'; + +import type { NpOutreachDatabase, NpOutreachTransaction } from '#~/db/db.js'; +import { schema } from '#~/db/db.js'; + +/** + * Insertable shape for a single grant, as produced by a source-specific + * ingestion step (Grants.gov, NH state, 990-PF extracts, ...) after + * normalization to our schema's columns. + */ +export type NewGrantInput = Omit< + typeof schema.grants.$inferInsert, + 'id' | 'createdAt' | 'updatedAt' +>; + +/** + * Upserts a batch of grants, keyed on `sourceUrl` — the stable identifier + * across re-crawls of the same upstream listing. Existing rows are + * refreshed in place (title/dates/amounts can change between crawls); + * `status` is also re-set from the incoming payload so a grant that was + * re-opened upstream comes back out of `expired`/`closed`. + */ +export async function serverInsertGrants( + db: NpOutreachDatabase | NpOutreachTransaction, + grants: ReadonlyArray, +): Promise { + if (grants.length === 0) return; + + await db + .insert(schema.grants) + .values([...grants]) + .onConflictDoUpdate({ + target: schema.grants.sourceUrl, + set: { + funder: sql`excluded.funder`, + title: sql`excluded.title`, + synopsis: sql`excluded.synopsis`, + eligibilityEntityTypes: sql`excluded.eligibility_entity_types`, + geographicScope: sql`excluded.geographic_scope`, + programAreas: sql`excluded.program_areas`, + awardFloor: sql`excluded.award_floor`, + awardCeiling: sql`excluded.award_ceiling`, + expectedAwardsCount: sql`excluded.expected_awards_count`, + openDate: sql`excluded.open_date`, + closeDate: sql`excluded.close_date`, + matchRequirement: sql`excluded.match_requirement`, + applicationEffortEstimate: sql`excluded.application_effort_estimate`, + applicationFormSupported: sql`excluded.application_form_supported`, + source: sql`excluded.source`, + status: sql`excluded.status`, + lastVerifiedAt: sql`excluded.last_verified_at`, + updatedAt: sql`now()`, + }, + }); +} diff --git a/packages/outreach-core/src/grants/index.server.ts b/packages/outreach-core/src/grants/index.server.ts new file mode 100644 index 0000000..5ac0a75 --- /dev/null +++ b/packages/outreach-core/src/grants/index.server.ts @@ -0,0 +1,2 @@ +export * from './actions/index.server.js'; +export * from './queries/index.server.js'; diff --git a/packages/outreach-core/src/grants/queries/index.server.ts b/packages/outreach-core/src/grants/queries/index.server.ts new file mode 100644 index 0000000..598abac --- /dev/null +++ b/packages/outreach-core/src/grants/queries/index.server.ts @@ -0,0 +1 @@ +export * from './list-open-grants.server.js'; diff --git a/packages/outreach-core/src/grants/queries/list-open-grants.server.ts b/packages/outreach-core/src/grants/queries/list-open-grants.server.ts new file mode 100644 index 0000000..abd5fda --- /dev/null +++ b/packages/outreach-core/src/grants/queries/list-open-grants.server.ts @@ -0,0 +1,15 @@ +import { eq } from 'drizzle-orm'; + +import type { NpOutreachDatabase, NpOutreachTransaction } from '#~/db/db.js'; +import { schema } from '#~/db/db.js'; + +/** + * All grants currently in `open` status, most-recently-closing first isn't + * assumed here — callers needing a particular ordering (e.g. soonest + * deadline) should `.orderBy` on the returned query builder. + */ +export function serverListOpenGrants( + db: NpOutreachDatabase | NpOutreachTransaction, +) { + return db.select().from(schema.grants).where(eq(schema.grants.status, 'open')); +} diff --git a/packages/outreach-core/src/index.server.ts b/packages/outreach-core/src/index.server.ts new file mode 100644 index 0000000..7196244 --- /dev/null +++ b/packages/outreach-core/src/index.server.ts @@ -0,0 +1,6 @@ +export * from './index.js'; + +export * from './grants/index.server.js'; +export * from './matches/index.server.js'; +export * from './orgs/index.server.js'; +export * from './pipeline/index.server.js'; diff --git a/packages/outreach-core/src/index.ts b/packages/outreach-core/src/index.ts new file mode 100644 index 0000000..90bf15b --- /dev/null +++ b/packages/outreach-core/src/index.ts @@ -0,0 +1,9 @@ +// Client-safe barrel: schema, DB types, and pure domain logic only. This +// package is server-only (no client/local database), so unlike +// @novelpad/core there is no synced/client schema here — this export +// exists purely so consumers that only need the table definitions/types +// (e.g. a Drizzle `drizzle(pool, { schema })` construction site, or a +// worker sharing types with an action call) don't have to pull in every +// server-only action/query. +export * from './db/index.js'; +export * from './matches/hard-gates.js'; diff --git a/packages/outreach-core/src/matches/actions/index.server.ts b/packages/outreach-core/src/matches/actions/index.server.ts new file mode 100644 index 0000000..356c71b --- /dev/null +++ b/packages/outreach-core/src/matches/actions/index.server.ts @@ -0,0 +1,2 @@ +export * from './insert-match.server.js'; +export * from './set-match-review.server.js'; diff --git a/packages/outreach-core/src/matches/actions/insert-match.server.ts b/packages/outreach-core/src/matches/actions/insert-match.server.ts new file mode 100644 index 0000000..ce4578e --- /dev/null +++ b/packages/outreach-core/src/matches/actions/insert-match.server.ts @@ -0,0 +1,28 @@ +import type { NpOutreachDatabase, NpOutreachTransaction } from '#~/db/db.js'; +import { schema } from '#~/db/db.js'; + +export type NewMatchInput = Omit< + typeof schema.matches.$inferInsert, + 'id' | 'createdAt' | 'updatedAt' | 'reviewStatus' +>; + +/** + * Records a scored (org, grant) match. Always lands in `reviewStatus: + * 'pending'` — the review decision is a separate step via + * `serverSetMatchReview`. + */ +export async function serverInsertMatch( + db: NpOutreachDatabase | NpOutreachTransaction, + match: NewMatchInput, +): Promise { + const [row] = await db + .insert(schema.matches) + .values({ ...match, reviewStatus: 'pending' }) + .returning({ id: schema.matches.id }); + + if (row == null) { + throw new Error('serverInsertMatch: insert returned no row'); + } + + return row.id; +} diff --git a/packages/outreach-core/src/matches/actions/set-match-review.server.ts b/packages/outreach-core/src/matches/actions/set-match-review.server.ts new file mode 100644 index 0000000..8ac4b67 --- /dev/null +++ b/packages/outreach-core/src/matches/actions/set-match-review.server.ts @@ -0,0 +1,35 @@ +import { eq, sql } from 'drizzle-orm'; + +import type { NpOutreachDatabase, NpOutreachTransaction } from '#~/db/db.js'; +import { schema } from '#~/db/db.js'; + +type ReviewStatus = (typeof schema.matches.$inferSelect)['reviewStatus']; +type RejectReason = (typeof schema.matches.$inferSelect)['rejectReason']; + +/** + * Records a human reviewer's decision on a match. `rejectReason` is only + * meaningful (and only ever set) when `reviewStatus` is `'rejected'` — any + * other status clears it, so a previously-rejected match doesn't carry a + * stale reason if it's later re-approved after editing. + */ +export async function serverSetMatchReview( + db: NpOutreachDatabase | NpOutreachTransaction, + { + matchId, + reviewStatus, + rejectReason, + }: { + matchId: string; + reviewStatus: ReviewStatus; + rejectReason?: RejectReason; + }, +): Promise { + await db + .update(schema.matches) + .set({ + reviewStatus, + rejectReason: reviewStatus === 'rejected' ? (rejectReason ?? null) : null, + updatedAt: sql`now()`, + }) + .where(eq(schema.matches.id, matchId)); +} diff --git a/packages/outreach-core/src/matches/hard-gates.test.ts b/packages/outreach-core/src/matches/hard-gates.test.ts new file mode 100644 index 0000000..2f71d58 --- /dev/null +++ b/packages/outreach-core/src/matches/hard-gates.test.ts @@ -0,0 +1,153 @@ +import { describe, expect, it } from 'vitest'; + +import { + evaluateHardGates, + type HardGateGrantInput, + type HardGateOrgInput, + MIN_AWARD_CEILING, +} from './hard-gates.js'; + +const NOW = new Date('2026-07-16T00:00:00Z'); + +function daysFromNow(days: number): Date { + return new Date(NOW.getTime() + days * 24 * 60 * 60 * 1000); +} + +const org: HardGateOrgInput = { + entityType: '501c3', + state: 'NH', +}; + +const grant: HardGateGrantInput = { + eligibilityEntityTypes: ['501c3', 'municipality'], + geographicScope: 'New Hampshire', + closeDate: daysFromNow(30), + awardCeiling: 50_000, + applicationFormSupported: true, +}; + +describe('evaluateHardGates', () => { + it('passes a fully-eligible match with no failures', () => { + const result = evaluateHardGates(org, grant, { now: NOW }); + expect(result).toEqual({ passed: true, failures: [] }); + }); + + it('treats an unrestricted entity-type list as eligible', () => { + const result = evaluateHardGates( + org, + { ...grant, eligibilityEntityTypes: null }, + { now: NOW }, + ); + expect(result.failures).not.toContain('ineligible_entity_type'); + }); + + it('fails when the org entity type is not in the eligibility list', () => { + const result = evaluateHardGates( + { ...org, entityType: 'llc' }, + grant, + { now: NOW }, + ); + expect(result.passed).toBe(false); + expect(result.failures).toContain('ineligible_entity_type'); + }); + + it('passes a national-scope grant regardless of org state', () => { + const result = evaluateHardGates( + org, + { ...grant, geographicScope: 'National' }, + { now: NOW }, + ); + expect(result.failures).not.toContain('geography_mismatch'); + }); + + it('fails when the geographic scope excludes the org state', () => { + const result = evaluateHardGates( + org, + { ...grant, geographicScope: 'California' }, + { now: NOW }, + ); + expect(result.passed).toBe(false); + expect(result.failures).toContain('geography_mismatch'); + }); + + it('fails when the deadline is fewer than 21 days out', () => { + const result = evaluateHardGates( + org, + { ...grant, closeDate: daysFromNow(20) }, + { now: NOW }, + ); + expect(result.passed).toBe(false); + expect(result.failures).toContain('deadline_too_soon'); + }); + + it('passes when the deadline is exactly 21 days out', () => { + const result = evaluateHardGates( + org, + { ...grant, closeDate: daysFromNow(21) }, + { now: NOW }, + ); + expect(result.failures).not.toContain('deadline_too_soon'); + }); + + it('fails when there is no close date at all', () => { + const result = evaluateHardGates( + org, + { ...grant, closeDate: null }, + { now: NOW }, + ); + expect(result.passed).toBe(false); + expect(result.failures).toContain('deadline_too_soon'); + }); + + it('fails when the award ceiling is below the minimum', () => { + const result = evaluateHardGates( + org, + { ...grant, awardCeiling: MIN_AWARD_CEILING - 1 }, + { now: NOW }, + ); + expect(result.passed).toBe(false); + expect(result.failures).toContain('award_ceiling_too_low'); + }); + + it('fails when the award ceiling is unknown', () => { + const result = evaluateHardGates( + org, + { ...grant, awardCeiling: null }, + { now: NOW }, + ); + expect(result.passed).toBe(false); + expect(result.failures).toContain('award_ceiling_too_low'); + }); + + it('fails when the application form is not supported', () => { + const result = evaluateHardGates( + org, + { ...grant, applicationFormSupported: false }, + { now: NOW }, + ); + expect(result.passed).toBe(false); + expect(result.failures).toContain('application_form_unsupported'); + }); + + it('accumulates every failing gate, not just the first', () => { + const result = evaluateHardGates( + { entityType: 'llc', state: 'NH' }, + { + eligibilityEntityTypes: ['501c3'], + geographicScope: 'California', + closeDate: null, + awardCeiling: null, + applicationFormSupported: false, + }, + { now: NOW }, + ); + expect(result.passed).toBe(false); + expect(result.failures).toEqual([ + 'ineligible_entity_type', + 'geography_mismatch', + 'deadline_too_soon', + 'award_ceiling_too_low', + 'application_form_unsupported', + ]); + }); +}); diff --git a/packages/outreach-core/src/matches/hard-gates.ts b/packages/outreach-core/src/matches/hard-gates.ts new file mode 100644 index 0000000..ada1d43 --- /dev/null +++ b/packages/outreach-core/src/matches/hard-gates.ts @@ -0,0 +1,173 @@ +/** + * Deterministic SQL-equivalent hard gates for an (org, grant) match. + * + * These are cheap, unambiguous pass/fail checks evaluated BEFORE the + * weighted LLM subscoring pass — a match that fails any hard gate is never + * worth spending an LLM call on. Kept as a pure function over plain data + * (no DB dependency) so it can be unit tested directly and reused from a + * SQL-backed batch job, a single-match rescoring step, or a preview in the + * review UI without threading a database handle through it. + */ + +export interface HardGateOrgInput { + /** e.g. '501c3', 'municipality', 'school_district' */ + readonly entityType: string; + /** Two-letter state code, e.g. 'NH'. */ + readonly state: string; +} + +export interface HardGateGrantInput { + /** Entity types the funder will accept; null/empty means unrestricted. */ + readonly eligibilityEntityTypes: readonly string[] | null; + /** Free-text geographic scope, e.g. 'NH', 'New England', 'National'. */ + readonly geographicScope: string | null; + readonly closeDate: Date | null; + readonly awardCeiling: number | null; + readonly applicationFormSupported: boolean; +} + +export type HardGateFailureReason = + | 'ineligible_entity_type' + | 'geography_mismatch' + | 'deadline_too_soon' + | 'award_ceiling_too_low' + | 'application_form_unsupported'; + +export interface HardGateResult { + readonly passed: boolean; + readonly failures: readonly HardGateFailureReason[]; +} + +/** A match's runway must clear this many days before the close date. */ +export const MIN_DAYS_TO_DEADLINE = 21; + +/** Grants below this ceiling aren't worth the outreach effort. */ +export const MIN_AWARD_CEILING = 10_000; + +const NATIONAL_SCOPE_KEYWORDS = [ + 'national', + 'nationwide', + 'united states', + 'usa', +]; + +/** Full state names, for scopes that spell the state out ('New Hampshire'). */ +const STATE_NAMES: Record = { + AL: 'alabama', + AK: 'alaska', + AZ: 'arizona', + AR: 'arkansas', + CA: 'california', + CO: 'colorado', + CT: 'connecticut', + DE: 'delaware', + FL: 'florida', + GA: 'georgia', + HI: 'hawaii', + ID: 'idaho', + IL: 'illinois', + IN: 'indiana', + IA: 'iowa', + KS: 'kansas', + KY: 'kentucky', + LA: 'louisiana', + ME: 'maine', + MD: 'maryland', + MA: 'massachusetts', + MI: 'michigan', + MN: 'minnesota', + MS: 'mississippi', + MO: 'missouri', + MT: 'montana', + NE: 'nebraska', + NV: 'nevada', + NH: 'new hampshire', + NJ: 'new jersey', + NM: 'new mexico', + NY: 'new york', + NC: 'north carolina', + ND: 'north dakota', + OH: 'ohio', + OK: 'oklahoma', + OR: 'oregon', + PA: 'pennsylvania', + RI: 'rhode island', + SC: 'south carolina', + SD: 'south dakota', + TN: 'tennessee', + TX: 'texas', + UT: 'utah', + VT: 'vermont', + VA: 'virginia', + WA: 'washington', + WV: 'west virginia', + WI: 'wisconsin', + WY: 'wyoming', + DC: 'district of columbia', +}; + +/** + * Runs every hard gate against an (org, grant) pair and returns which, if + * any, failed. `now` is injectable for deterministic testing. + */ +export function evaluateHardGates( + org: HardGateOrgInput, + grant: HardGateGrantInput, + { now = new Date() }: { now?: Date } = {}, +): HardGateResult { + const failures: HardGateFailureReason[] = []; + + if (!isEntityEligible(org, grant)) failures.push('ineligible_entity_type'); + if (!isGeographyEligible(org, grant)) failures.push('geography_mismatch'); + if (!hasSufficientRunway(grant, now)) failures.push('deadline_too_soon'); + if (!meetsAwardCeiling(grant)) failures.push('award_ceiling_too_low'); + if (!grant.applicationFormSupported) { + failures.push('application_form_unsupported'); + } + + return { passed: failures.length === 0, failures }; +} + +function isEntityEligible( + org: HardGateOrgInput, + grant: HardGateGrantInput, +): boolean { + if ( + grant.eligibilityEntityTypes == null || + grant.eligibilityEntityTypes.length === 0 + ) { + return true; + } + return grant.eligibilityEntityTypes.some( + (entityType) => entityType.toLowerCase() === org.entityType.toLowerCase(), + ); +} + +function isGeographyEligible( + org: HardGateOrgInput, + grant: HardGateGrantInput, +): boolean { + const scope = grant.geographicScope?.trim().toLowerCase(); + if (!scope) return true; + if (NATIONAL_SCOPE_KEYWORDS.some((keyword) => scope.includes(keyword))) { + return true; + } + // Match the two-letter code only on word boundaries ('NH', 'NH-only') so + // it can't fire on letters embedded inside another word, and also accept + // the spelled-out state name ('New Hampshire'). + const code = org.state.trim().toLowerCase(); + if (new RegExp(`\\b${code}\\b`, 'i').test(scope)) return true; + const fullName = STATE_NAMES[org.state.trim().toUpperCase()]; + return fullName != null && scope.includes(fullName); +} + +function hasSufficientRunway(grant: HardGateGrantInput, now: Date): boolean { + if (grant.closeDate == null) return false; + const msPerDay = 24 * 60 * 60 * 1000; + const daysRemaining = (grant.closeDate.getTime() - now.getTime()) / msPerDay; + return daysRemaining >= MIN_DAYS_TO_DEADLINE; +} + +function meetsAwardCeiling(grant: HardGateGrantInput): boolean { + return grant.awardCeiling != null && grant.awardCeiling >= MIN_AWARD_CEILING; +} diff --git a/packages/outreach-core/src/matches/index.server.ts b/packages/outreach-core/src/matches/index.server.ts new file mode 100644 index 0000000..0549e20 --- /dev/null +++ b/packages/outreach-core/src/matches/index.server.ts @@ -0,0 +1,6 @@ +// `hard-gates.js` is client-safe (pure, no DB) and is exported from the +// package's root `index.js` instead — not re-exported here to avoid an +// ambiguous `export *` collision in `index.server.ts` (which re-exports +// both `./index.js` and this file). +export * from './actions/index.server.js'; +export * from './queries/index.server.js'; diff --git a/packages/outreach-core/src/matches/queries/index.server.ts b/packages/outreach-core/src/matches/queries/index.server.ts new file mode 100644 index 0000000..691ea67 --- /dev/null +++ b/packages/outreach-core/src/matches/queries/index.server.ts @@ -0,0 +1 @@ +export * from './list-pending-review-matches.server.js'; diff --git a/packages/outreach-core/src/matches/queries/list-pending-review-matches.server.ts b/packages/outreach-core/src/matches/queries/list-pending-review-matches.server.ts new file mode 100644 index 0000000..38df478 --- /dev/null +++ b/packages/outreach-core/src/matches/queries/list-pending-review-matches.server.ts @@ -0,0 +1,41 @@ +import { eq } from 'drizzle-orm'; + +import type { NpOutreachDatabase, NpOutreachTransaction } from '#~/db/db.js'; +import { schema } from '#~/db/db.js'; + +/** + * Pending matches joined to their org and grant — the shape the review + * queue renders. One row per pending (org, grant) match, most useful + * columns only; the full match row (subscores, rationale) can be fetched + * by id when a detail view lands. + */ +export interface PendingReviewMatch { + id: string; + orgName: string; + grantTitle: string; + funder: string; + totalScore: number; + easyWin: boolean; + isHero: boolean; + closeDate: Date | null; +} + +export async function serverListPendingReviewMatches( + db: NpOutreachDatabase | NpOutreachTransaction, +): Promise { + return db + .select({ + id: schema.matches.id, + orgName: schema.orgs.name, + grantTitle: schema.grants.title, + funder: schema.grants.funder, + totalScore: schema.matches.totalScore, + easyWin: schema.matches.easyWin, + isHero: schema.matches.isHero, + closeDate: schema.grants.closeDate, + }) + .from(schema.matches) + .innerJoin(schema.orgs, eq(schema.matches.orgId, schema.orgs.id)) + .innerJoin(schema.grants, eq(schema.matches.grantId, schema.grants.id)) + .where(eq(schema.matches.reviewStatus, 'pending')); +} diff --git a/packages/outreach-core/src/orgs/actions/index.server.ts b/packages/outreach-core/src/orgs/actions/index.server.ts new file mode 100644 index 0000000..67c9161 --- /dev/null +++ b/packages/outreach-core/src/orgs/actions/index.server.ts @@ -0,0 +1 @@ +export * from './insert-org.server.js'; diff --git a/packages/outreach-core/src/orgs/actions/insert-org.server.ts b/packages/outreach-core/src/orgs/actions/insert-org.server.ts new file mode 100644 index 0000000..d8326bb --- /dev/null +++ b/packages/outreach-core/src/orgs/actions/insert-org.server.ts @@ -0,0 +1,30 @@ +import type { NpOutreachDatabase, NpOutreachTransaction } from '#~/db/db.js'; +import { schema } from '#~/db/db.js'; + +export type NewOrgInput = Omit< + typeof schema.orgs.$inferInsert, + 'id' | 'createdAt' | 'updatedAt' +>; + +/** + * Inserts a single NH nonprofit org. Callers that discover an org from a + * registry re-scan should query for an existing `ein` match first — this + * function does not upsert (EIN is only unique when present, which makes a + * blind upsert-on-conflict ambiguous for the common case of an org sourced + * without one). + */ +export async function serverInsertOrg( + db: NpOutreachDatabase | NpOutreachTransaction, + org: NewOrgInput, +): Promise { + const [row] = await db + .insert(schema.orgs) + .values(org) + .returning({ id: schema.orgs.id }); + + if (row == null) { + throw new Error('serverInsertOrg: insert returned no row'); + } + + return row.id; +} diff --git a/packages/outreach-core/src/orgs/index.server.ts b/packages/outreach-core/src/orgs/index.server.ts new file mode 100644 index 0000000..5ac0a75 --- /dev/null +++ b/packages/outreach-core/src/orgs/index.server.ts @@ -0,0 +1,2 @@ +export * from './actions/index.server.js'; +export * from './queries/index.server.js'; diff --git a/packages/outreach-core/src/orgs/queries/index.server.ts b/packages/outreach-core/src/orgs/queries/index.server.ts new file mode 100644 index 0000000..60e5214 --- /dev/null +++ b/packages/outreach-core/src/orgs/queries/index.server.ts @@ -0,0 +1 @@ +export * from './list-orgs-in-icp-band.server.js'; diff --git a/packages/outreach-core/src/orgs/queries/list-orgs-in-icp-band.server.ts b/packages/outreach-core/src/orgs/queries/list-orgs-in-icp-band.server.ts new file mode 100644 index 0000000..a1a98b2 --- /dev/null +++ b/packages/outreach-core/src/orgs/queries/list-orgs-in-icp-band.server.ts @@ -0,0 +1,14 @@ +import { eq } from 'drizzle-orm'; + +import type { NpOutreachDatabase, NpOutreachTransaction } from '#~/db/db.js'; +import { schema } from '#~/db/db.js'; + +type IcpBand = (typeof schema.orgs.$inferSelect)['icpBand']; + +/** All orgs currently classified into the given ICP band. */ +export function serverListOrgsInIcpBand( + db: NpOutreachDatabase | NpOutreachTransaction, + { icpBand }: { icpBand: IcpBand }, +) { + return db.select().from(schema.orgs).where(eq(schema.orgs.icpBand, icpBand)); +} diff --git a/packages/outreach-core/src/pipeline/actions/index.server.ts b/packages/outreach-core/src/pipeline/actions/index.server.ts new file mode 100644 index 0000000..4ddff5c --- /dev/null +++ b/packages/outreach-core/src/pipeline/actions/index.server.ts @@ -0,0 +1 @@ +export * from './record-pipeline-event.server.js'; diff --git a/packages/outreach-core/src/pipeline/actions/record-pipeline-event.server.ts b/packages/outreach-core/src/pipeline/actions/record-pipeline-event.server.ts new file mode 100644 index 0000000..f8c089d --- /dev/null +++ b/packages/outreach-core/src/pipeline/actions/record-pipeline-event.server.ts @@ -0,0 +1,29 @@ +import type { NpOutreachDatabase, NpOutreachTransaction } from '#~/db/db.js'; +import { schema } from '#~/db/db.js'; + +export type NewPipelineEventInput = Omit< + typeof schema.pipelineEvents.$inferInsert, + 'id' | 'createdAt' +>; + +/** + * Records an outreach lifecycle event (Apollo webhook, or an internally + * generated milestone like `brief_requested`). `occurredAt` defaults to now + * when the caller doesn't have an authoritative upstream timestamp (e.g. a + * webhook payload that only carries the event, not the original send time). + */ +export async function serverRecordPipelineEvent( + db: NpOutreachDatabase | NpOutreachTransaction, + event: NewPipelineEventInput, +): Promise { + const [row] = await db + .insert(schema.pipelineEvents) + .values({ occurredAt: new Date(), ...event }) + .returning({ id: schema.pipelineEvents.id }); + + if (row == null) { + throw new Error('serverRecordPipelineEvent: insert returned no row'); + } + + return row.id; +} diff --git a/packages/outreach-core/src/pipeline/index.server.ts b/packages/outreach-core/src/pipeline/index.server.ts new file mode 100644 index 0000000..5ac0a75 --- /dev/null +++ b/packages/outreach-core/src/pipeline/index.server.ts @@ -0,0 +1,2 @@ +export * from './actions/index.server.js'; +export * from './queries/index.server.js'; diff --git a/packages/outreach-core/src/pipeline/queries/index.server.ts b/packages/outreach-core/src/pipeline/queries/index.server.ts new file mode 100644 index 0000000..d7a41c1 --- /dev/null +++ b/packages/outreach-core/src/pipeline/queries/index.server.ts @@ -0,0 +1 @@ +export * from './list-pipeline-events-for-org.server.js'; diff --git a/packages/outreach-core/src/pipeline/queries/list-pipeline-events-for-org.server.ts b/packages/outreach-core/src/pipeline/queries/list-pipeline-events-for-org.server.ts new file mode 100644 index 0000000..2c0c139 --- /dev/null +++ b/packages/outreach-core/src/pipeline/queries/list-pipeline-events-for-org.server.ts @@ -0,0 +1,16 @@ +import { desc, eq } from 'drizzle-orm'; + +import type { NpOutreachDatabase, NpOutreachTransaction } from '#~/db/db.js'; +import { schema } from '#~/db/db.js'; + +/** An org's outreach event history, most recent first — for the org detail/timeline view. */ +export function serverListPipelineEventsForOrg( + db: NpOutreachDatabase | NpOutreachTransaction, + { orgId }: { orgId: string }, +) { + return db + .select() + .from(schema.pipelineEvents) + .where(eq(schema.pipelineEvents.orgId, orgId)) + .orderBy(desc(schema.pipelineEvents.occurredAt)); +} diff --git a/packages/outreach-core/tsconfig.json b/packages/outreach-core/tsconfig.json new file mode 100644 index 0000000..7ae02eb --- /dev/null +++ b/packages/outreach-core/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "@novelpad/config/tsconfig.base.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "declaration": true, + "types": ["node"], + "tsBuildInfoFile": "./dist/.tsbuildinfo", + "paths": { + "#~/*": ["./src/*"] + } + }, + "include": ["src/**/*.ts"], + "exclude": ["node_modules", "dist"] +} diff --git a/turbo.json b/turbo.json new file mode 100644 index 0000000..e1dcbc7 --- /dev/null +++ b/turbo.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://turbo.build/schema.json", + "tasks": { + "build": { + "dependsOn": ["^build"], + "env": ["VITE_*"], + "outputs": ["dist/**", "build/**", ".react-router/**", "*.tsbuildinfo"] + }, + "typecheck": { + "dependsOn": ["^typecheck", "^build"] + }, + "test": { + "dependsOn": ["^build"] + }, + "dev": { + "persistent": true, + "cache": false + } + } +} diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..5075f43 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,6687 @@ +# This file is generated by running "yarn install" inside your project. +# Manual changes might be lost - proceed with caution! + +__metadata: + version: 8 + cacheKey: 10c0 + +"@alloc/quick-lru@npm:^5.2.0": + version: 5.2.0 + resolution: "@alloc/quick-lru@npm:5.2.0" + checksum: 10c0/7b878c48b9d25277d0e1a9b8b2f2312a314af806b4129dc902f2bc29ab09b58236e53964689feec187b28c80d2203aff03829754773a707a8a5987f1b7682d92 + languageName: node + linkType: hard + +"@babel/code-frame@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/code-frame@npm:7.29.7" + dependencies: + "@babel/helper-validator-identifier": "npm:^7.29.7" + js-tokens: "npm:^4.0.0" + picocolors: "npm:^1.1.1" + checksum: 10c0/169fc2080169a40c1760155eaaaf739bcb882df0bec76a83adbda5493645bc17270a3434b8848c494b1933e96fe1d147370001e3cda09a39f43ae30f08ef2069 + languageName: node + linkType: hard + +"@babel/compat-data@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/compat-data@npm:7.29.7" + checksum: 10c0/47913f05e08a45a1c9df38c02b4b49e391005085b489432647a1abe112e5d9c75e3be8ea5972b7f6da4ec5d1339922ceb9ea02b8a25d4ed1cb8636e5261f344e + languageName: node + linkType: hard + +"@babel/core@npm:^7.23.7, @babel/core@npm:^7.27.7": + version: 7.29.7 + resolution: "@babel/core@npm:7.29.7" + dependencies: + "@babel/code-frame": "npm:^7.29.7" + "@babel/generator": "npm:^7.29.7" + "@babel/helper-compilation-targets": "npm:^7.29.7" + "@babel/helper-module-transforms": "npm:^7.29.7" + "@babel/helpers": "npm:^7.29.7" + "@babel/parser": "npm:^7.29.7" + "@babel/template": "npm:^7.29.7" + "@babel/traverse": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + "@jridgewell/remapping": "npm:^2.3.5" + convert-source-map: "npm:^2.0.0" + debug: "npm:^4.1.0" + gensync: "npm:^1.0.0-beta.2" + json5: "npm:^2.2.3" + semver: "npm:^6.3.1" + checksum: 10c0/112fb09c24de7a1de64d1de2c31fe65c4e6af4cb2fb6e6d99ea5373e6fc51e75b88581c0efae4c4c68f119a02a988c7106e95011a41530a2fb8ed793c7eaa07b + languageName: node + linkType: hard + +"@babel/generator@npm:^7.27.5, @babel/generator@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/generator@npm:7.29.7" + dependencies: + "@babel/parser": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + "@jridgewell/gen-mapping": "npm:^0.3.12" + "@jridgewell/trace-mapping": "npm:^0.3.28" + jsesc: "npm:^3.0.2" + checksum: 10c0/9bf72b01b5bd0ea5b1288a0e37dbd360bff2f2b1ce73342c0d40fb3db2ec3dc004ada5ffa925c5e12939a416eed59e600d562b8ecd938ce0d27dfd0eb6c6c2b7 + languageName: node + linkType: hard + +"@babel/helper-annotate-as-pure@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-annotate-as-pure@npm:7.29.7" + dependencies: + "@babel/types": "npm:^7.29.7" + checksum: 10c0/c56536b52d17632d89d49db2063ed6102f0e3bbadf6a0ccb74e6599d6a77173b644c7fe8c3ef17c7a162709d55b75ee5145ef6db917d16ba7f375fbffcf2e942 + languageName: node + linkType: hard + +"@babel/helper-compilation-targets@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-compilation-targets@npm:7.29.7" + dependencies: + "@babel/compat-data": "npm:^7.29.7" + "@babel/helper-validator-option": "npm:^7.29.7" + browserslist: "npm:^4.24.0" + lru-cache: "npm:^5.1.1" + semver: "npm:^6.3.1" + checksum: 10c0/4c15fd4c69a0a7047799a28a88460c19cede0a0ee8af994ea169114986f4af48b92c7393a4a3fee0456c11a656eece3448a6ed06354453d6c27cccf17195453b + languageName: node + linkType: hard + +"@babel/helper-create-class-features-plugin@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-create-class-features-plugin@npm:7.29.7" + dependencies: + "@babel/helper-annotate-as-pure": "npm:^7.29.7" + "@babel/helper-member-expression-to-functions": "npm:^7.29.7" + "@babel/helper-optimise-call-expression": "npm:^7.29.7" + "@babel/helper-replace-supers": "npm:^7.29.7" + "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.29.7" + "@babel/traverse": "npm:^7.29.7" + semver: "npm:^6.3.1" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 10c0/75f34905b5e708b473f1e9b33e07b2fcc8f4c60676df8bc74541bb91c77f387c32a948dd04d5071e469ba454d72d0a872e3ace40fbb1d1e7aaa8569efcf09ed4 + languageName: node + linkType: hard + +"@babel/helper-globals@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-globals@npm:7.29.7" + checksum: 10c0/f38417c40b1129a1b2b519ca961b9040c8827d1444fd74068702286b91b77089431dc76b6b9d5c1496e5da2a4f3ad329c6946e688ba3fa0d1d0b3d2b4f34f36a + languageName: node + linkType: hard + +"@babel/helper-member-expression-to-functions@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-member-expression-to-functions@npm:7.29.7" + dependencies: + "@babel/traverse": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + checksum: 10c0/eef7940ce0797208854a5af1049a98fee9abbffb5c619640c69ff5a555f8e3552295bb18756490b02bc6af7df8c1babcb83f12203aac2deb9dfecfc78846e12d + languageName: node + linkType: hard + +"@babel/helper-module-imports@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-module-imports@npm:7.29.7" + dependencies: + "@babel/traverse": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + checksum: 10c0/6adf60d97356027413342a092f818d9678c4f5caff716a33e3284b5ae14e47a9e88059d421dde4ee4894691260039a12602c0e7becadc175602194b40dfa345d + languageName: node + linkType: hard + +"@babel/helper-module-transforms@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-module-transforms@npm:7.29.7" + dependencies: + "@babel/helper-module-imports": "npm:^7.29.7" + "@babel/helper-validator-identifier": "npm:^7.29.7" + "@babel/traverse": "npm:^7.29.7" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 10c0/ee5a2172c24a42be696836f4b0d947489c9729d8adf5821885cf77d1ad5333e3c447368e9a71f67df1099570490553dccf9f888ef0a92a48aa63cb086bd8c7e1 + languageName: node + linkType: hard + +"@babel/helper-optimise-call-expression@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-optimise-call-expression@npm:7.29.7" + dependencies: + "@babel/types": "npm:^7.29.7" + checksum: 10c0/fd0244b9bfbb487db02d59aa2703c6991d654ea5f3f39d912682842bdca2e87b5ae8643b0ce8069bf5fbee39d1aa9db7abefeb5e6ba1aa650dca12777cf5b7e2 + languageName: node + linkType: hard + +"@babel/helper-plugin-utils@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-plugin-utils@npm:7.29.7" + checksum: 10c0/380477a06133274a2759f9355929cb60a95e8b8fee624a1ae1fa349e1d1645b89daca456f72833f6d1062bffa12ee4271c5bf0cc5a61c0166cdc24c7591e2408 + languageName: node + linkType: hard + +"@babel/helper-replace-supers@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-replace-supers@npm:7.29.7" + dependencies: + "@babel/helper-member-expression-to-functions": "npm:^7.29.7" + "@babel/helper-optimise-call-expression": "npm:^7.29.7" + "@babel/traverse": "npm:^7.29.7" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 10c0/1c7ae37797f226e965ab85f6affa53d25a10c169c604a4daeb36f9df09e673471e6522f631c13761cf9fbafeca2ea14c241dea8d723a51039d561beb01d86ac4 + languageName: node + linkType: hard + +"@babel/helper-skip-transparent-expression-wrappers@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-skip-transparent-expression-wrappers@npm:7.29.7" + dependencies: + "@babel/traverse": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + checksum: 10c0/8c59493621487fc491f27adfc200af82a6aca3b9a5511e4e6050f8716593b4b243472cb56c8d2016e828b7ae12d605a819205aa8600ca08ee291dcd58d65c832 + languageName: node + linkType: hard + +"@babel/helper-string-parser@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-string-parser@npm:7.29.7" + checksum: 10c0/194bc0f1716e396d5ffde56ad6119745fb9557662c98611590e5e454906783a4ccb21ce93056b8eb69a4909044834e45d96e50ac695bbe9e3221648fe033c06c + languageName: node + linkType: hard + +"@babel/helper-validator-identifier@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-validator-identifier@npm:7.29.7" + checksum: 10c0/4795354e7ae0dcafa72de1cd04ec51252dc1498517170beaf019e03effc5b7bf13c6b21a3949a77e07b8125be7f106ed1131350d8ebd4566ae874094a726d62b + languageName: node + linkType: hard + +"@babel/helper-validator-option@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-validator-option@npm:7.29.7" + checksum: 10c0/d2a06c6d0ac40ba4a2f219fc2cab249c7a94bacdb2686273b7f9598571c908809b48468ff588915a346e6cc7296f60b581023d1d498b747fed06f779d335c2cc + languageName: node + linkType: hard + +"@babel/helpers@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helpers@npm:7.29.7" + dependencies: + "@babel/template": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + checksum: 10c0/218e8d10953647c9f44775f5a022b227a182674853b5ea8631889deb7e1a3e4bc870388aaecf59bb8bd92a87f9a96220ed3f70a35bffec6bcf9169ecb67891ac + languageName: node + linkType: hard + +"@babel/parser@npm:^7.23.6, @babel/parser@npm:^7.27.7, @babel/parser@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/parser@npm:7.29.7" + dependencies: + "@babel/types": "npm:^7.29.7" + bin: + parser: ./bin/babel-parser.js + checksum: 10c0/65133038f80b54a714d6027cb77cee3f9a6b5c4c6842ce674301e13947cbcbfa8055e63acaf1b84c085d34226a14425b2c2b97b829e0e226d2e8f1299942a51d + languageName: node + linkType: hard + +"@babel/plugin-syntax-jsx@npm:^7.27.1, @babel/plugin-syntax-jsx@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/plugin-syntax-jsx@npm:7.29.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.29.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/1736000de183538ba8eef34520105508860e48b0c763254ba9158af5e814ed8bbceeedbb4281fbda33de787ae5b3870e92f60c6ae7131e7d322e451d57387896 + languageName: node + linkType: hard + +"@babel/plugin-syntax-typescript@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/plugin-syntax-typescript@npm:7.29.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.29.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/c49883b0327e8683b770dc823205af5c697da216e590dcf5bf53f3f031e7e381de450b164f8f99853f0837a3de5cb793298e2be6697a0f6e452bb9dd34b5165e + languageName: node + linkType: hard + +"@babel/plugin-transform-modules-commonjs@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/plugin-transform-modules-commonjs@npm:7.29.7" + dependencies: + "@babel/helper-module-transforms": "npm:^7.29.7" + "@babel/helper-plugin-utils": "npm:^7.29.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/9791cb524438b2a8ba6cb8715788fa1e202fbecd4e76b3ccab0af0819fd69212b40ae30d72ac377012f7149889f7792ed8bf91e97bbe9113ca9641f8ad3bf332 + languageName: node + linkType: hard + +"@babel/plugin-transform-typescript@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/plugin-transform-typescript@npm:7.29.7" + dependencies: + "@babel/helper-annotate-as-pure": "npm:^7.29.7" + "@babel/helper-create-class-features-plugin": "npm:^7.29.7" + "@babel/helper-plugin-utils": "npm:^7.29.7" + "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.29.7" + "@babel/plugin-syntax-typescript": "npm:^7.29.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/8bf6a89c6827af6f11d4b189f1f97a64b8d754cc4caa5cbae16a6a8b113294d7f310dd40efd82e87ebcff2a1c683584b872d6d8960abe88d48f441d42f94c4d1 + languageName: node + linkType: hard + +"@babel/preset-typescript@npm:^7.27.1": + version: 7.29.7 + resolution: "@babel/preset-typescript@npm:7.29.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.29.7" + "@babel/helper-validator-option": "npm:^7.29.7" + "@babel/plugin-syntax-jsx": "npm:^7.29.7" + "@babel/plugin-transform-modules-commonjs": "npm:^7.29.7" + "@babel/plugin-transform-typescript": "npm:^7.29.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/40746a23a7ab46c0beb1c02d69883d9ffbe3043685cb3ae5363644391376b9261189fdad317191349e322c2c9bc550b031daa42470a1f8987362e4c56e492194 + languageName: node + linkType: hard + +"@babel/template@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/template@npm:7.29.7" + dependencies: + "@babel/code-frame": "npm:^7.29.7" + "@babel/parser": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + checksum: 10c0/8bb7f900dcab0e9e1c5ffbc33ca10e0d26b7b2e2ca804becb73ee771b9c4ed6e2908a4ae4a14c08560febb45d2b6b9a173955e42ad404d05f8b04840a14d9c58 + languageName: node + linkType: hard + +"@babel/traverse@npm:^7.23.7, @babel/traverse@npm:^7.27.7, @babel/traverse@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/traverse@npm:7.29.7" + dependencies: + "@babel/code-frame": "npm:^7.29.7" + "@babel/generator": "npm:^7.29.7" + "@babel/helper-globals": "npm:^7.29.7" + "@babel/parser": "npm:^7.29.7" + "@babel/template": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + debug: "npm:^4.3.1" + checksum: 10c0/e256a1fbdb956555b76f3c285b1e453f6bedec8b3afb61751d99d933efd11c7d79caf5ddf2493570058a9f7deaa1b48324380d7c1aa1443fd9508becbf56331a + languageName: node + linkType: hard + +"@babel/types@npm:^7.23.6, @babel/types@npm:^7.27.7, @babel/types@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/types@npm:7.29.7" + dependencies: + "@babel/helper-string-parser": "npm:^7.29.7" + "@babel/helper-validator-identifier": "npm:^7.29.7" + checksum: 10c0/b6623994c69717fa27294f5fa46d59140338e2d86c6c1c13085c84ef7d53086ee357fbf4fe9abe3dd3da75734dc77c4c0df2f90fb29e667558bb3b3fb705e88f + languageName: node + linkType: hard + +"@dbos-inc/dbos-sdk@npm:4.17.6": + version: 4.17.6 + resolution: "@dbos-inc/dbos-sdk@npm:4.17.6" + dependencies: + commander: "npm:12.0.0" + pg: "npm:8.11.3" + serialize-error: "npm:8.1.0" + superjson: "npm:^1.13.3" + ws: "npm:^8.18.1" + yaml: "npm:^2.8.3" + bin: + dbos: dist/src/cli/cli.js + dbos-sdk: dist/src/cli/cli.js + checksum: 10c0/7b8a1cf73c94336a9eb8c04d11212a1e8862302554650c06134e49a7b9dd5cef9fe6e6e0c60e3c8cac72b4ed3b5961622cf7aa132b0384bdc2fbd43e2fd74395 + languageName: node + linkType: hard + +"@dbos-inc/drizzle-datasource@npm:4.17.6": + version: 4.17.6 + resolution: "@dbos-inc/drizzle-datasource@npm:4.17.6" + dependencies: + pg: "npm:^8.11.3" + superjson: "npm:^1.13" + peerDependencies: + "@dbos-inc/dbos-sdk": "*" + drizzle-orm: "*" + checksum: 10c0/6a06eedb689dc1ebb13250ffdd89eb72dd86f9fcfd28c36c05103e1efb6ed494add0e046f9f782ab8ad7c1a98428b22d13469f70e2c467d3ede79be0630c6424 + languageName: node + linkType: hard + +"@drizzle-team/brocli@npm:^0.10.2": + version: 0.10.2 + resolution: "@drizzle-team/brocli@npm:0.10.2" + checksum: 10c0/3d8b99d680f0b14fea32b45c59b938b6665e0840cc67f04801b1aa3c6747da3c7d01c00e321645034fa100abdba7e0c20ce07cf46fc2ca769ee4cafd97562484 + languageName: node + linkType: hard + +"@esbuild-kit/core-utils@npm:^3.3.2": + version: 3.3.2 + resolution: "@esbuild-kit/core-utils@npm:3.3.2" + dependencies: + esbuild: "npm:~0.18.20" + source-map-support: "npm:^0.5.21" + checksum: 10c0/d856f5bd720814593f911d781ed7558a3f8ec1a39802f3831d0eea0d1306e0e2dc11b7b2443af621c413ec6557f1f3034a9a4f1472a4cb40e52cd6e3b356aa05 + languageName: node + linkType: hard + +"@esbuild-kit/esm-loader@npm:^2.5.5": + version: 2.6.5 + resolution: "@esbuild-kit/esm-loader@npm:2.6.5" + dependencies: + "@esbuild-kit/core-utils": "npm:^3.3.2" + get-tsconfig: "npm:^4.7.0" + checksum: 10c0/6894b29176eda62bdce0d458d57f32daed5cb8fcff14cb3ddfbc995cfe3e2fa8599f3b0b1af66db446903b30167f57069f27e9cf79a69cf9b41f557115811cde + languageName: node + linkType: hard + +"@esbuild/aix-ppc64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/aix-ppc64@npm:0.21.5" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/aix-ppc64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/aix-ppc64@npm:0.24.2" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/aix-ppc64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/aix-ppc64@npm:0.25.12" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/aix-ppc64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/aix-ppc64@npm:0.28.1" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/android-arm64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/android-arm64@npm:0.18.20" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/android-arm64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/android-arm64@npm:0.21.5" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/android-arm64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/android-arm64@npm:0.24.2" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/android-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/android-arm64@npm:0.25.12" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/android-arm64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/android-arm64@npm:0.28.1" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/android-arm@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/android-arm@npm:0.18.20" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@esbuild/android-arm@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/android-arm@npm:0.21.5" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@esbuild/android-arm@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/android-arm@npm:0.24.2" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@esbuild/android-arm@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/android-arm@npm:0.25.12" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@esbuild/android-arm@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/android-arm@npm:0.28.1" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@esbuild/android-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/android-x64@npm:0.18.20" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/android-x64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/android-x64@npm:0.21.5" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/android-x64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/android-x64@npm:0.24.2" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/android-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/android-x64@npm:0.25.12" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/android-x64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/android-x64@npm:0.28.1" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/darwin-arm64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/darwin-arm64@npm:0.18.20" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/darwin-arm64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/darwin-arm64@npm:0.21.5" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/darwin-arm64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/darwin-arm64@npm:0.24.2" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/darwin-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/darwin-arm64@npm:0.25.12" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/darwin-arm64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/darwin-arm64@npm:0.28.1" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/darwin-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/darwin-x64@npm:0.18.20" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/darwin-x64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/darwin-x64@npm:0.21.5" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/darwin-x64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/darwin-x64@npm:0.24.2" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/darwin-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/darwin-x64@npm:0.25.12" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/darwin-x64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/darwin-x64@npm:0.28.1" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/freebsd-arm64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/freebsd-arm64@npm:0.18.20" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/freebsd-arm64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/freebsd-arm64@npm:0.21.5" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/freebsd-arm64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/freebsd-arm64@npm:0.24.2" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/freebsd-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/freebsd-arm64@npm:0.25.12" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/freebsd-arm64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/freebsd-arm64@npm:0.28.1" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/freebsd-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/freebsd-x64@npm:0.18.20" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/freebsd-x64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/freebsd-x64@npm:0.21.5" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/freebsd-x64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/freebsd-x64@npm:0.24.2" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/freebsd-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/freebsd-x64@npm:0.25.12" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/freebsd-x64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/freebsd-x64@npm:0.28.1" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/linux-arm64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-arm64@npm:0.18.20" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/linux-arm64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/linux-arm64@npm:0.21.5" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/linux-arm64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/linux-arm64@npm:0.24.2" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/linux-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-arm64@npm:0.25.12" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/linux-arm64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/linux-arm64@npm:0.28.1" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/linux-arm@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-arm@npm:0.18.20" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@esbuild/linux-arm@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/linux-arm@npm:0.21.5" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@esbuild/linux-arm@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/linux-arm@npm:0.24.2" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@esbuild/linux-arm@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-arm@npm:0.25.12" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@esbuild/linux-arm@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/linux-arm@npm:0.28.1" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@esbuild/linux-ia32@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-ia32@npm:0.18.20" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/linux-ia32@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/linux-ia32@npm:0.21.5" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/linux-ia32@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/linux-ia32@npm:0.24.2" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/linux-ia32@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-ia32@npm:0.25.12" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/linux-ia32@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/linux-ia32@npm:0.28.1" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/linux-loong64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-loong64@npm:0.18.20" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + +"@esbuild/linux-loong64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/linux-loong64@npm:0.21.5" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + +"@esbuild/linux-loong64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/linux-loong64@npm:0.24.2" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + +"@esbuild/linux-loong64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-loong64@npm:0.25.12" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + +"@esbuild/linux-loong64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/linux-loong64@npm:0.28.1" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + +"@esbuild/linux-mips64el@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-mips64el@npm:0.18.20" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + +"@esbuild/linux-mips64el@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/linux-mips64el@npm:0.21.5" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + +"@esbuild/linux-mips64el@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/linux-mips64el@npm:0.24.2" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + +"@esbuild/linux-mips64el@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-mips64el@npm:0.25.12" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + +"@esbuild/linux-mips64el@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/linux-mips64el@npm:0.28.1" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + +"@esbuild/linux-ppc64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-ppc64@npm:0.18.20" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/linux-ppc64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/linux-ppc64@npm:0.21.5" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/linux-ppc64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/linux-ppc64@npm:0.24.2" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/linux-ppc64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-ppc64@npm:0.25.12" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/linux-ppc64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/linux-ppc64@npm:0.28.1" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/linux-riscv64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-riscv64@npm:0.18.20" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + +"@esbuild/linux-riscv64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/linux-riscv64@npm:0.21.5" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + +"@esbuild/linux-riscv64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/linux-riscv64@npm:0.24.2" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + +"@esbuild/linux-riscv64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-riscv64@npm:0.25.12" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + +"@esbuild/linux-riscv64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/linux-riscv64@npm:0.28.1" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + +"@esbuild/linux-s390x@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-s390x@npm:0.18.20" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + +"@esbuild/linux-s390x@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/linux-s390x@npm:0.21.5" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + +"@esbuild/linux-s390x@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/linux-s390x@npm:0.24.2" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + +"@esbuild/linux-s390x@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-s390x@npm:0.25.12" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + +"@esbuild/linux-s390x@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/linux-s390x@npm:0.28.1" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + +"@esbuild/linux-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-x64@npm:0.18.20" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/linux-x64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/linux-x64@npm:0.21.5" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/linux-x64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/linux-x64@npm:0.24.2" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/linux-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/linux-x64@npm:0.25.12" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/linux-x64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/linux-x64@npm:0.28.1" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/netbsd-arm64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/netbsd-arm64@npm:0.24.2" + conditions: os=netbsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/netbsd-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/netbsd-arm64@npm:0.25.12" + conditions: os=netbsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/netbsd-arm64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/netbsd-arm64@npm:0.28.1" + conditions: os=netbsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/netbsd-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/netbsd-x64@npm:0.18.20" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/netbsd-x64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/netbsd-x64@npm:0.21.5" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/netbsd-x64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/netbsd-x64@npm:0.24.2" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/netbsd-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/netbsd-x64@npm:0.25.12" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/netbsd-x64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/netbsd-x64@npm:0.28.1" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openbsd-arm64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/openbsd-arm64@npm:0.24.2" + conditions: os=openbsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/openbsd-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/openbsd-arm64@npm:0.25.12" + conditions: os=openbsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/openbsd-arm64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/openbsd-arm64@npm:0.28.1" + conditions: os=openbsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/openbsd-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/openbsd-x64@npm:0.18.20" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openbsd-x64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/openbsd-x64@npm:0.21.5" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openbsd-x64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/openbsd-x64@npm:0.24.2" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openbsd-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/openbsd-x64@npm:0.25.12" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openbsd-x64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/openbsd-x64@npm:0.28.1" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openharmony-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/openharmony-arm64@npm:0.25.12" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/openharmony-arm64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/openharmony-arm64@npm:0.28.1" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/sunos-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/sunos-x64@npm:0.18.20" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/sunos-x64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/sunos-x64@npm:0.21.5" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/sunos-x64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/sunos-x64@npm:0.24.2" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/sunos-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/sunos-x64@npm:0.25.12" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/sunos-x64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/sunos-x64@npm:0.28.1" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/win32-arm64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/win32-arm64@npm:0.18.20" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/win32-arm64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/win32-arm64@npm:0.21.5" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/win32-arm64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/win32-arm64@npm:0.24.2" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/win32-arm64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/win32-arm64@npm:0.25.12" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/win32-arm64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/win32-arm64@npm:0.28.1" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/win32-ia32@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/win32-ia32@npm:0.18.20" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/win32-ia32@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/win32-ia32@npm:0.21.5" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/win32-ia32@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/win32-ia32@npm:0.24.2" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/win32-ia32@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/win32-ia32@npm:0.25.12" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/win32-ia32@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/win32-ia32@npm:0.28.1" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/win32-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/win32-x64@npm:0.18.20" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/win32-x64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/win32-x64@npm:0.21.5" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/win32-x64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/win32-x64@npm:0.24.2" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/win32-x64@npm:0.25.12": + version: 0.25.12 + resolution: "@esbuild/win32-x64@npm:0.25.12" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/win32-x64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/win32-x64@npm:0.28.1" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@fontsource-variable/bricolage-grotesque@npm:^5.2.10": + version: 5.2.10 + resolution: "@fontsource-variable/bricolage-grotesque@npm:5.2.10" + checksum: 10c0/5963126874a66170093b2639f534b502cba78315fe50bfbdceb0ce124beb922071a69fdb6d66a7ab0983a67435ca3b466fe0722e5bd4bbcd525e352d9a03c499 + languageName: node + linkType: hard + +"@fontsource-variable/lora@npm:^5.2.8": + version: 5.2.8 + resolution: "@fontsource-variable/lora@npm:5.2.8" + checksum: 10c0/fa501a0c3e023b67d001793b578947d6068f81ec9e407c4ceb6bd1ee651e5cfc3da962134210d326d7208b8eb68c99b0f48e6c82de46b31c4163f6220f7bfaeb + languageName: node + linkType: hard + +"@fontsource-variable/outfit@npm:^5.2.8": + version: 5.2.8 + resolution: "@fontsource-variable/outfit@npm:5.2.8" + checksum: 10c0/0d8e3cf0153780d794c28bde93384ed22691a95c5e743489c3c43d4e4ec88d1c6e6a0684bc890bbbb04fb292e1505785636c60b3748e835375617e2af2e6c230 + languageName: node + linkType: hard + +"@fontsource-variable/source-serif-4@npm:^5.2.9": + version: 5.2.9 + resolution: "@fontsource-variable/source-serif-4@npm:5.2.9" + checksum: 10c0/95bf11dbdf1b9948d6f8c01ed194c0dea41c277ef65f1f43d24ad2690b4dc57ec0ccae0adbdd9648604a436e1c7b02441a85474605d53dce2f4619e9df162df6 + languageName: node + linkType: hard + +"@fontsource/courier-prime@npm:^5.2.8": + version: 5.2.8 + resolution: "@fontsource/courier-prime@npm:5.2.8" + checksum: 10c0/80ee2f38b54b496b0bcadc88986cb7f5acb0335b5d3f2788472fe19f1ebc1624bccbae1400c5adc2978e8881c3bb87c298ce89db20725f8c7352896966973783 + languageName: node + linkType: hard + +"@fontsource/dm-serif-display@npm:^5.2.8": + version: 5.2.8 + resolution: "@fontsource/dm-serif-display@npm:5.2.8" + checksum: 10c0/358375bad0c203cb5d9a55a248a48c9fc145b3e194ca13c5b0bc80fbc42bf4858c8d2409f84d7190daeeb898fa20b75ce0957642db71b852e7db3927ddeff950 + languageName: node + linkType: hard + +"@fontsource/inter@npm:^5.2.8": + version: 5.2.8 + resolution: "@fontsource/inter@npm:5.2.8" + checksum: 10c0/f737dd50005e4809887ba55ae0c9b7174216d6d14875d17a4fbb9a0ad75dec4265928b805a43fe16a23f14a878f1974a398bbfc84ad65c79fc4d4b9c3ea154e1 + languageName: node + linkType: hard + +"@fontsource/libre-baskerville@npm:^5.2.8": + version: 5.2.10 + resolution: "@fontsource/libre-baskerville@npm:5.2.10" + checksum: 10c0/e03c9abf9dc16bec0709eb27a02c32fb9e4966cec921df6442fbedb17b1db776360b7dc316430688e87909fd3ef296ce52c592f1389a9b84dc5e0bfe83c2dbb8 + languageName: node + linkType: hard + +"@google/genai@npm:^1.41.0": + version: 1.52.0 + resolution: "@google/genai@npm:1.52.0" + dependencies: + google-auth-library: "npm:^10.3.0" + p-retry: "npm:^4.6.2" + protobufjs: "npm:^7.5.4" + ws: "npm:^8.18.0" + peerDependencies: + "@modelcontextprotocol/sdk": ^1.25.2 + peerDependenciesMeta: + "@modelcontextprotocol/sdk": + optional: true + checksum: 10c0/2d22f6bd4baa915a402289dae4797410acbd5c5add1ed0c8d1bedef3ac7871fe1f04640d0612aa793fe7055f086719487491f55474a5b21597addfdea8631fa6 + languageName: node + linkType: hard + +"@isaacs/fs-minipass@npm:^4.0.0": + version: 4.0.1 + resolution: "@isaacs/fs-minipass@npm:4.0.1" + dependencies: + minipass: "npm:^7.0.4" + checksum: 10c0/c25b6dc1598790d5b55c0947a9b7d111cfa92594db5296c3b907e2f533c033666f692a3939eadac17b1c7c40d362d0b0635dc874cbfe3e70db7c2b07cc97a5d2 + languageName: node + linkType: hard + +"@jridgewell/gen-mapping@npm:^0.3.12, @jridgewell/gen-mapping@npm:^0.3.2, @jridgewell/gen-mapping@npm:^0.3.5": + version: 0.3.13 + resolution: "@jridgewell/gen-mapping@npm:0.3.13" + dependencies: + "@jridgewell/sourcemap-codec": "npm:^1.5.0" + "@jridgewell/trace-mapping": "npm:^0.3.24" + checksum: 10c0/9a7d65fb13bd9aec1fbab74cda08496839b7e2ceb31f5ab922b323e94d7c481ce0fc4fd7e12e2610915ed8af51178bdc61e168e92a8c8b8303b030b03489b13b + languageName: node + linkType: hard + +"@jridgewell/remapping@npm:^2.3.5": + version: 2.3.5 + resolution: "@jridgewell/remapping@npm:2.3.5" + dependencies: + "@jridgewell/gen-mapping": "npm:^0.3.5" + "@jridgewell/trace-mapping": "npm:^0.3.24" + checksum: 10c0/3de494219ffeb2c5c38711d0d7bb128097edf91893090a2dbc8ee0b55d092bb7347b1fd0f478486c5eab010e855c73927b1666f2107516d472d24a73017d1194 + languageName: node + linkType: hard + +"@jridgewell/resolve-uri@npm:^3.1.0": + version: 3.1.2 + resolution: "@jridgewell/resolve-uri@npm:3.1.2" + checksum: 10c0/d502e6fb516b35032331406d4e962c21fe77cdf1cbdb49c6142bcbd9e30507094b18972778a6e27cbad756209cfe34b1a27729e6fa08a2eb92b33943f680cf1e + languageName: node + linkType: hard + +"@jridgewell/sourcemap-codec@npm:^1.4.14, @jridgewell/sourcemap-codec@npm:^1.5.0, @jridgewell/sourcemap-codec@npm:^1.5.5": + version: 1.5.5 + resolution: "@jridgewell/sourcemap-codec@npm:1.5.5" + checksum: 10c0/f9e538f302b63c0ebc06eecb1dd9918dd4289ed36147a0ddce35d6ea4d7ebbda243cda7b2213b6a5e1d8087a298d5cf630fb2bd39329cdecb82017023f6081a0 + languageName: node + linkType: hard + +"@jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.28": + version: 0.3.31 + resolution: "@jridgewell/trace-mapping@npm:0.3.31" + dependencies: + "@jridgewell/resolve-uri": "npm:^3.1.0" + "@jridgewell/sourcemap-codec": "npm:^1.4.14" + checksum: 10c0/4b30ec8cd56c5fd9a661f088230af01e0c1a3888d11ffb6b47639700f71225be21d1f7e168048d6d4f9449207b978a235c07c8f15c07705685d16dc06280e9d9 + languageName: node + linkType: hard + +"@mjackson/node-fetch-server@npm:^0.2.0": + version: 0.2.0 + resolution: "@mjackson/node-fetch-server@npm:0.2.0" + checksum: 10c0/f22eb4cd50801f07eb187df666d0987a7bd5662caaf005faf62e035e00d7b92d6236852f69e9331dabe7737c0f69eabe967ef2d695f3d2e844d439f4489d6f36 + languageName: node + linkType: hard + +"@nodelib/fs.scandir@npm:2.1.5": + version: 2.1.5 + resolution: "@nodelib/fs.scandir@npm:2.1.5" + dependencies: + "@nodelib/fs.stat": "npm:2.0.5" + run-parallel: "npm:^1.1.9" + checksum: 10c0/732c3b6d1b1e967440e65f284bd06e5821fedf10a1bea9ed2bb75956ea1f30e08c44d3def9d6a230666574edbaf136f8cfd319c14fd1f87c66e6a44449afb2eb + languageName: node + linkType: hard + +"@nodelib/fs.stat@npm:2.0.5, @nodelib/fs.stat@npm:^2.0.2": + version: 2.0.5 + resolution: "@nodelib/fs.stat@npm:2.0.5" + checksum: 10c0/88dafe5e3e29a388b07264680dc996c17f4bda48d163a9d4f5c1112979f0ce8ec72aa7116122c350b4e7976bc5566dc3ddb579be1ceaacc727872eb4ed93926d + languageName: node + linkType: hard + +"@nodelib/fs.walk@npm:^1.2.3": + version: 1.2.8 + resolution: "@nodelib/fs.walk@npm:1.2.8" + dependencies: + "@nodelib/fs.scandir": "npm:2.1.5" + fastq: "npm:^1.6.0" + checksum: 10c0/db9de047c3bb9b51f9335a7bb46f4fcfb6829fb628318c12115fbaf7d369bfce71c15b103d1fc3b464812d936220ee9bc1c8f762d032c9f6be9acc99249095b1 + languageName: node + linkType: hard + +"@novelpad/config@workspace:^, @novelpad/config@workspace:packages/config": + version: 0.0.0-use.local + resolution: "@novelpad/config@workspace:packages/config" + dependencies: + "@fontsource-variable/bricolage-grotesque": "npm:^5.2.10" + "@fontsource-variable/lora": "npm:^5.2.8" + "@fontsource-variable/outfit": "npm:^5.2.8" + "@fontsource-variable/source-serif-4": "npm:^5.2.9" + "@fontsource/courier-prime": "npm:^5.2.8" + "@fontsource/dm-serif-display": "npm:^5.2.8" + "@fontsource/inter": "npm:^5.2.8" + "@fontsource/libre-baskerville": "npm:^5.2.8" + "@tailwindcss/container-queries": "npm:^0.1.1" + "@tailwindcss/typography": "npm:^0.5.19" + tailwindcss: "npm:^3.4.13" + tailwindcss-animate: "npm:^1.0.7" + vite: "npm:^5.4.11" + languageName: unknown + linkType: soft + +"@novelpad/outreach-ai@workspace:packages/outreach-ai": + version: 0.0.0-use.local + resolution: "@novelpad/outreach-ai@workspace:packages/outreach-ai" + dependencies: + "@google/genai": "npm:^1.41.0" + "@novelpad/config": "workspace:^" + "@types/node": "npm:^22" + typescript: "npm:^5.9.3" + vitest: "npm:^3.2.4" + zod: "npm:^3.25.0" + languageName: unknown + linkType: soft + +"@novelpad/outreach-core@workspace:^, @novelpad/outreach-core@workspace:packages/outreach-core": + version: 0.0.0-use.local + resolution: "@novelpad/outreach-core@workspace:packages/outreach-core" + dependencies: + "@novelpad/config": "workspace:^" + "@types/node": "npm:^22" + "@types/pg": "npm:8.20.0" + drizzle-kit: "npm:^0.31.5" + drizzle-orm: "npm:0.44.6" + npm-run-all: "npm:^4.1.5" + pg: "npm:8.20.0" + tsx: "npm:^4.19.2" + typescript: "npm:^5.9.3" + vitest: "npm:^3.2.4" + zod: "npm:^3.25.0" + languageName: unknown + linkType: soft + +"@novelpad/outreach-review@workspace:apps/outreach-review": + version: 0.0.0-use.local + resolution: "@novelpad/outreach-review@workspace:apps/outreach-review" + dependencies: + "@novelpad/config": "workspace:^" + "@novelpad/outreach-core": "workspace:^" + "@react-router/dev": "npm:^7.9.1" + "@react-router/node": "npm:^7.9.1" + "@react-router/serve": "npm:^7.9.1" + "@types/node": "npm:^22" + "@types/pg": "npm:8.20.0" + "@types/react": "npm:^19.0.9" + "@types/react-dom": "npm:^19.0.3" + autoprefixer: "npm:^10.4.20" + drizzle-orm: "npm:0.44.6" + isbot: "npm:4" + pg: "npm:8.20.0" + postcss: "npm:^8.4.47" + react: "npm:^19.0.0" + react-dom: "npm:^19.0.0" + react-router: "npm:^7.9.1" + tailwindcss: "npm:^3.4.13" + typescript: "npm:^5.9.3" + vite: "npm:^5.4.9" + languageName: unknown + linkType: soft + +"@novelpad/outreach-worker@workspace:apps/outreach-worker": + version: 0.0.0-use.local + resolution: "@novelpad/outreach-worker@workspace:apps/outreach-worker" + dependencies: + "@dbos-inc/dbos-sdk": "npm:4.17.6" + "@dbos-inc/drizzle-datasource": "npm:4.17.6" + "@novelpad/config": "workspace:^" + "@novelpad/outreach-core": "workspace:^" + "@types/node": "npm:^22" + "@types/pg": "npm:8.20.0" + drizzle-orm: "npm:0.44.6" + esbuild: "npm:^0.24.0" + pg: "npm:8.20.0" + tsx: "npm:^4.19.2" + typescript: "npm:^5.9.3" + languageName: unknown + linkType: soft + +"@protobufjs/aspromise@npm:^1.1.1, @protobufjs/aspromise@npm:^1.1.2": + version: 1.1.2 + resolution: "@protobufjs/aspromise@npm:1.1.2" + checksum: 10c0/a83343a468ff5b5ec6bff36fd788a64c839e48a07ff9f4f813564f58caf44d011cd6504ed2147bf34835bd7a7dd2107052af755961c6b098fd8902b4f6500d0f + languageName: node + linkType: hard + +"@protobufjs/base64@npm:^1.1.2": + version: 1.1.2 + resolution: "@protobufjs/base64@npm:1.1.2" + checksum: 10c0/eec925e681081af190b8ee231f9bad3101e189abbc182ff279da6b531e7dbd2a56f1f306f37a80b1be9e00aa2d271690d08dcc5f326f71c9eed8546675c8caf6 + languageName: node + linkType: hard + +"@protobufjs/codegen@npm:^2.0.5": + version: 2.0.5 + resolution: "@protobufjs/codegen@npm:2.0.5" + checksum: 10c0/1b8a2ae56ee60a56e9d205cd4b6072a1503c5069b8ebb905710f974ff0098a0d0700641c137e0a8d98dedf14423156a106a9433695cbf52574810f55000fdcab + languageName: node + linkType: hard + +"@protobufjs/eventemitter@npm:^1.1.1": + version: 1.1.1 + resolution: "@protobufjs/eventemitter@npm:1.1.1" + checksum: 10c0/8e06193d4629c5e7c09d4f8c2ddba8fc4dfa739f0149f33a1d901568d35bb7b8b5277a4e8452baf3bdd0b302fd599cf255d193267aa93a0a4747e23cd073c4ac + languageName: node + linkType: hard + +"@protobufjs/fetch@npm:^1.1.1": + version: 1.1.1 + resolution: "@protobufjs/fetch@npm:1.1.1" + dependencies: + "@protobufjs/aspromise": "npm:^1.1.1" + checksum: 10c0/a497ff5433854e8577f0427983ea39b9113b49a8120f94515291d763327061d2c3013e60e24ea436d091dafae01a0f6eb1867e3b1616045d96a31d8b3c646ed4 + languageName: node + linkType: hard + +"@protobufjs/float@npm:^1.0.2": + version: 1.0.2 + resolution: "@protobufjs/float@npm:1.0.2" + checksum: 10c0/18f2bdede76ffcf0170708af15c9c9db6259b771e6b84c51b06df34a9c339dbbeec267d14ce0bddd20acc142b1d980d983d31434398df7f98eb0c94a0eb79069 + languageName: node + linkType: hard + +"@protobufjs/path@npm:^1.1.2": + version: 1.1.2 + resolution: "@protobufjs/path@npm:1.1.2" + checksum: 10c0/cece0a938e7f5dfd2fa03f8c14f2f1cf8b0d6e13ac7326ff4c96ea311effd5fb7ae0bba754fbf505312af2e38500250c90e68506b97c02360a43793d88a0d8b4 + languageName: node + linkType: hard + +"@protobufjs/pool@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/pool@npm:1.1.0" + checksum: 10c0/eda2718b7f222ac6e6ad36f758a92ef90d26526026a19f4f17f668f45e0306a5bd734def3f48f51f8134ae0978b6262a5c517c08b115a551756d1a3aadfcf038 + languageName: node + linkType: hard + +"@protobufjs/utf8@npm:^1.1.1": + version: 1.1.2 + resolution: "@protobufjs/utf8@npm:1.1.2" + checksum: 10c0/975c6e2c0319bd50c17531d186b537a88cb4d3205e3ca9297cd842b631d341da0ee3ff16be8656cd2660756dc753b29c8a6a14c0ad8ed602c1f91ddf22e7b3f3 + languageName: node + linkType: hard + +"@react-router/dev@npm:^7.9.1": + version: 7.18.1 + resolution: "@react-router/dev@npm:7.18.1" + dependencies: + "@babel/core": "npm:^7.27.7" + "@babel/generator": "npm:^7.27.5" + "@babel/parser": "npm:^7.27.7" + "@babel/plugin-syntax-jsx": "npm:^7.27.1" + "@babel/preset-typescript": "npm:^7.27.1" + "@babel/traverse": "npm:^7.27.7" + "@babel/types": "npm:^7.27.7" + "@react-router/node": "npm:7.18.1" + "@remix-run/node-fetch-server": "npm:^0.13.0" + arg: "npm:^5.0.1" + babel-dead-code-elimination: "npm:^1.0.6" + chokidar: "npm:^4.0.0" + dedent: "npm:^1.5.3" + es-module-lexer: "npm:^1.3.1" + exit-hook: "npm:2.2.1" + isbot: "npm:^5.1.11" + jsesc: "npm:3.0.2" + lodash: "npm:^4.17.21" + p-map: "npm:^7.0.3" + pathe: "npm:^1.1.2" + picocolors: "npm:^1.1.1" + pkg-types: "npm:^2.3.0" + prettier: "npm:^3.6.2" + react-refresh: "npm:^0.14.0" + semver: "npm:^7.3.7" + tinyglobby: "npm:^0.2.14" + valibot: "npm:^1.2.0" + vite-node: "npm:^3.2.2" + peerDependencies: + "@react-router/serve": ^7.18.1 + "@vitejs/plugin-rsc": ~0.5.21 + react-router: ^7.18.1 + react-server-dom-webpack: ^19.2.3 + typescript: ^5.1.0 || ^6.0.0 + vite: ^5.1.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + wrangler: ^3.28.2 || ^4.0.0 + peerDependenciesMeta: + "@react-router/serve": + optional: true + "@vitejs/plugin-rsc": + optional: true + react-server-dom-webpack: + optional: true + typescript: + optional: true + wrangler: + optional: true + bin: + react-router: bin.js + checksum: 10c0/a36552e7e5083333af231bf2069ef94ef8da168a732551f68cba0e149f15023a9e8b1ad81b018d303805c56338443c8f963cd6854f1f3055d9d0d2d36e98bf04 + languageName: node + linkType: hard + +"@react-router/express@npm:7.18.1": + version: 7.18.1 + resolution: "@react-router/express@npm:7.18.1" + dependencies: + "@react-router/node": "npm:7.18.1" + peerDependencies: + express: ^4.17.1 || ^5 + react-router: 7.18.1 + typescript: ^5.1.0 || ^6.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/eea26f7e540ea6761c7d7b1d98f05694e238931628cc7d36965cb99837fb3ff091a230d38f59d66580851401db1d83a2692218fbe913854596e0c05a4fbd4fb1 + languageName: node + linkType: hard + +"@react-router/node@npm:7.18.1, @react-router/node@npm:^7.9.1": + version: 7.18.1 + resolution: "@react-router/node@npm:7.18.1" + dependencies: + "@mjackson/node-fetch-server": "npm:^0.2.0" + peerDependencies: + react-router: 7.18.1 + typescript: ^5.1.0 || ^6.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/af5e908b57f81ea179868ce0534bbde3613bb9ecdc208e6f727a838eb4f7b8bd5339f2f232668c62108c7709c005e252f6dacbf886aabaa3d18a0e8e006c4537 + languageName: node + linkType: hard + +"@react-router/serve@npm:^7.9.1": + version: 7.18.1 + resolution: "@react-router/serve@npm:7.18.1" + dependencies: + "@mjackson/node-fetch-server": "npm:^0.2.0" + "@react-router/express": "npm:7.18.1" + "@react-router/node": "npm:7.18.1" + compression: "npm:^1.8.1" + express: "npm:^4.19.2" + get-port: "npm:5.1.1" + morgan: "npm:^1.10.1" + source-map-support: "npm:^0.5.21" + peerDependencies: + react-router: 7.18.1 + bin: + react-router-serve: bin.js + checksum: 10c0/245eba66ddce5805e4f19ed7cd1c7abb542a163e48ca2862b3966030d6ecdf8d86bf5a02ec05eccd37f3362b4b9e13f65eca9922939a1c2c25962e10b475c444 + languageName: node + linkType: hard + +"@remix-run/node-fetch-server@npm:^0.13.0": + version: 0.13.3 + resolution: "@remix-run/node-fetch-server@npm:0.13.3" + checksum: 10c0/a3a9bc35e18e76883c7f40a23e8321d6627e2fea10e399524c2a53d12a6bf62f4ba301c5ad8414018f1a75f25183f3fe1f8bf13283c13a004a8ed47fd65d2139 + languageName: node + linkType: hard + +"@rollup/rollup-android-arm-eabi@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.62.2" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@rollup/rollup-android-arm64@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-android-arm64@npm:4.62.2" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-darwin-arm64@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-darwin-arm64@npm:4.62.2" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-darwin-x64@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-darwin-x64@npm:4.62.2" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-freebsd-arm64@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-freebsd-arm64@npm:4.62.2" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-freebsd-x64@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-freebsd-x64@npm:4.62.2" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm-gnueabihf@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.62.2" + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm-musleabihf@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.62.2" + conditions: os=linux & cpu=arm & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm64-gnu@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.62.2" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm64-musl@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.62.2" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-loong64-gnu@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-linux-loong64-gnu@npm:4.62.2" + conditions: os=linux & cpu=loong64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-loong64-musl@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-linux-loong64-musl@npm:4.62.2" + conditions: os=linux & cpu=loong64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-ppc64-gnu@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-linux-ppc64-gnu@npm:4.62.2" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-ppc64-musl@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-linux-ppc64-musl@npm:4.62.2" + conditions: os=linux & cpu=ppc64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-riscv64-gnu@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.62.2" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-riscv64-musl@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-linux-riscv64-musl@npm:4.62.2" + conditions: os=linux & cpu=riscv64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-s390x-gnu@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.62.2" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-x64-gnu@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.62.2" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-x64-musl@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.62.2" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-openbsd-x64@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-openbsd-x64@npm:4.62.2" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-openharmony-arm64@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-openharmony-arm64@npm:4.62.2" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-win32-arm64-msvc@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.62.2" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-win32-ia32-msvc@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.62.2" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@rollup/rollup-win32-x64-gnu@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-win32-x64-gnu@npm:4.62.2" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-win32-x64-msvc@npm:4.62.2": + version: 4.62.2 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.62.2" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@tailwindcss/container-queries@npm:^0.1.1": + version: 0.1.1 + resolution: "@tailwindcss/container-queries@npm:0.1.1" + peerDependencies: + tailwindcss: ">=3.2.0" + checksum: 10c0/336546ddcc60280723f2a92e311cac0acb8b05624c6519675d4b11ae13ed01dba5e622705a9be69cd66ca2c8019032551176546e6920d9634750165ac4c15d8e + languageName: node + linkType: hard + +"@tailwindcss/typography@npm:^0.5.19": + version: 0.5.20 + resolution: "@tailwindcss/typography@npm:0.5.20" + dependencies: + postcss-selector-parser: "npm:6.0.10" + peerDependencies: + tailwindcss: "*" + checksum: 10c0/e42f9347a7802d620c56ea3379dc7986c0eece9af2d0ae170d530a8c8ef9815f6fdb6b23b2ae76c9832314dcca125e6a166bfec1c88326495896afeab64c1c2e + languageName: node + linkType: hard + +"@turbo/darwin-64@npm:2.10.5": + version: 2.10.5 + resolution: "@turbo/darwin-64@npm:2.10.5" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@turbo/darwin-arm64@npm:2.10.5": + version: 2.10.5 + resolution: "@turbo/darwin-arm64@npm:2.10.5" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@turbo/linux-64@npm:2.10.5": + version: 2.10.5 + resolution: "@turbo/linux-64@npm:2.10.5" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@turbo/linux-arm64@npm:2.10.5": + version: 2.10.5 + resolution: "@turbo/linux-arm64@npm:2.10.5" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@turbo/windows-64@npm:2.10.5": + version: 2.10.5 + resolution: "@turbo/windows-64@npm:2.10.5" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@turbo/windows-arm64@npm:2.10.5": + version: 2.10.5 + resolution: "@turbo/windows-arm64@npm:2.10.5" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@types/chai@npm:^5.2.2": + version: 5.2.3 + resolution: "@types/chai@npm:5.2.3" + dependencies: + "@types/deep-eql": "npm:*" + assertion-error: "npm:^2.0.1" + checksum: 10c0/e0ef1de3b6f8045a5e473e867c8565788c444271409d155588504840ad1a53611011f85072188c2833941189400228c1745d78323dac13fcede9c2b28bacfb2f + languageName: node + linkType: hard + +"@types/deep-eql@npm:*": + version: 4.0.2 + resolution: "@types/deep-eql@npm:4.0.2" + checksum: 10c0/bf3f811843117900d7084b9d0c852da9a044d12eb40e6de73b552598a6843c21291a8a381b0532644574beecd5e3491c5ff3a0365ab86b15d59862c025384844 + languageName: node + linkType: hard + +"@types/estree@npm:1.0.9, @types/estree@npm:^1.0.0": + version: 1.0.9 + resolution: "@types/estree@npm:1.0.9" + checksum: 10c0/3ad3286ca2988cd550dafb8f2ad599c8474868e954fa601a36655bdfefd8039f7c714b8c1c7f2ae219ffbd58bd4660e66fa7479a0120fc02d4777057d4865387 + languageName: node + linkType: hard + +"@types/node@npm:*, @types/node@npm:>=13.7.0": + version: 26.1.1 + resolution: "@types/node@npm:26.1.1" + dependencies: + undici-types: "npm:~8.3.0" + checksum: 10c0/25ac5093195736dd074d5027adbba85617bc951d5a41506ebd3b9073048acb7233613f3822d5f9b9447f9c0841c91f6387c46283916767e3ef79f23917452c46 + languageName: node + linkType: hard + +"@types/node@npm:^22": + version: 22.20.1 + resolution: "@types/node@npm:22.20.1" + dependencies: + undici-types: "npm:~6.21.0" + checksum: 10c0/f2ba54d3d1fb92e1c57c78d32c3a17655b1e87363b707136f55c422b4838d4054901ce5d27f75bb0e5ecb7ebfee3804e0987822d22b473473008091857a09353 + languageName: node + linkType: hard + +"@types/pg@npm:8.20.0": + version: 8.20.0 + resolution: "@types/pg@npm:8.20.0" + dependencies: + "@types/node": "npm:*" + pg-protocol: "npm:*" + pg-types: "npm:^2.2.0" + checksum: 10c0/c8b5aa794ea074aa20d0c1ef6c721ce0fe16f2c084d0ccc32b7f12909a08ec969e6b01a094ce8e7019cc425381c4b59f261bd0133daf0c6d4aca5c6c492e8312 + languageName: node + linkType: hard + +"@types/react-dom@npm:^19.0.3": + version: 19.2.3 + resolution: "@types/react-dom@npm:19.2.3" + peerDependencies: + "@types/react": ^19.2.0 + checksum: 10c0/b486ebe0f4e2fb35e2e108df1d8fc0927ca5d6002d5771e8a739de11239fe62d0e207c50886185253c99eb9dedfeeb956ea7429e5ba17f6693c7acb4c02f8cd1 + languageName: node + linkType: hard + +"@types/react@npm:^19.0.9": + version: 19.2.17 + resolution: "@types/react@npm:19.2.17" + dependencies: + csstype: "npm:^3.2.2" + checksum: 10c0/bc2c4af96b3e480604424de70d5ebda90c5f4b485df471858c0bc2d7d70364b606ec3c4d8579f94f01aa0c6c0591f56bcf14cba5689f5eea4b74250ccdc3a232 + languageName: node + linkType: hard + +"@types/retry@npm:0.12.0": + version: 0.12.0 + resolution: "@types/retry@npm:0.12.0" + checksum: 10c0/7c5c9086369826f569b83a4683661557cab1361bac0897a1cefa1a915ff739acd10ca0d62b01071046fe3f5a3f7f2aec80785fe283b75602dc6726781ea3e328 + languageName: node + linkType: hard + +"@vitest/expect@npm:3.2.7": + version: 3.2.7 + resolution: "@vitest/expect@npm:3.2.7" + dependencies: + "@types/chai": "npm:^5.2.2" + "@vitest/spy": "npm:3.2.7" + "@vitest/utils": "npm:3.2.7" + chai: "npm:^5.2.0" + tinyrainbow: "npm:^2.0.0" + checksum: 10c0/5a13fee261d1020d47a3517f4d9a2cb209d7475399c815f6ebe22be116ddb0c6c401592f07d24f9a8b1c192155d11f651397ee28061f886ea61f1f9181a8fd48 + languageName: node + linkType: hard + +"@vitest/mocker@npm:3.2.7": + version: 3.2.7 + resolution: "@vitest/mocker@npm:3.2.7" + dependencies: + "@vitest/spy": "npm:3.2.7" + estree-walker: "npm:^3.0.3" + magic-string: "npm:^0.30.17" + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + checksum: 10c0/e8d9a155723e77b3e14c5a1eba35d8966b94420ebc733ffd7119b7bede22006580c468eec2aec6612fb77852cbcdfa94c6f49218f3ca1427aa9363d545c624fd + languageName: node + linkType: hard + +"@vitest/pretty-format@npm:3.2.7, @vitest/pretty-format@npm:^3.2.7": + version: 3.2.7 + resolution: "@vitest/pretty-format@npm:3.2.7" + dependencies: + tinyrainbow: "npm:^2.0.0" + checksum: 10c0/f556dd5f9e5240c7ab054d795622292c1b23f6aad3332d1e250f33d801783efbb7883387640b76d22cc91b81f30cb735b40371ec3e4f5293e735495f45d624df + languageName: node + linkType: hard + +"@vitest/runner@npm:3.2.7": + version: 3.2.7 + resolution: "@vitest/runner@npm:3.2.7" + dependencies: + "@vitest/utils": "npm:3.2.7" + pathe: "npm:^2.0.3" + strip-literal: "npm:^3.0.0" + checksum: 10c0/d73ba6df95175ea2b545d37fde3e743d113ecd608693b444af1d4dc27956abe4d5b500e25e09ffe46ce720de6b7cec68c264fca20366fc6da0d9ce75c52047cc + languageName: node + linkType: hard + +"@vitest/snapshot@npm:3.2.7": + version: 3.2.7 + resolution: "@vitest/snapshot@npm:3.2.7" + dependencies: + "@vitest/pretty-format": "npm:3.2.7" + magic-string: "npm:^0.30.17" + pathe: "npm:^2.0.3" + checksum: 10c0/c58a17a2c98788e77d30d39837c024852eca91618efe9b53ec399cf76332365b8cc592a69617e0f7d696df716dcc341dac61c6cbea86ae98cdb49a4a84f48d19 + languageName: node + linkType: hard + +"@vitest/spy@npm:3.2.7": + version: 3.2.7 + resolution: "@vitest/spy@npm:3.2.7" + dependencies: + tinyspy: "npm:^4.0.3" + checksum: 10c0/cf2728b4ddc6137e0ca749215c7714845e8221a5f799e933bc623216a9958fd8b032cdd7f033ed8375df9e6ed84a18b64687ca2530f50e8a76bc3b1d16d88c42 + languageName: node + linkType: hard + +"@vitest/utils@npm:3.2.7": + version: 3.2.7 + resolution: "@vitest/utils@npm:3.2.7" + dependencies: + "@vitest/pretty-format": "npm:3.2.7" + loupe: "npm:^3.1.4" + tinyrainbow: "npm:^2.0.0" + checksum: 10c0/fea57d6cf66853926f54ea6e9bd249b707120b0abba565d96a3880c7f04d5c5cd4778546879b9074cc73c0b81b475c1873791d4b3b3e5a324115b5bcfd3a46bb + languageName: node + linkType: hard + +"abbrev@npm:^5.0.0": + version: 5.0.0 + resolution: "abbrev@npm:5.0.0" + checksum: 10c0/8e88f5c798ea4562d28c5a3e9ad69e3879890bc5d695d8f2dffb8609be4c890aacc8f80ef4553fdd2c6a62d70c2ce8bc57b38074e383beb7487bdafa9ed42ea5 + languageName: node + linkType: hard + +"accepts@npm:~1.3.8": + version: 1.3.8 + resolution: "accepts@npm:1.3.8" + dependencies: + mime-types: "npm:~2.1.34" + negotiator: "npm:0.6.3" + checksum: 10c0/3a35c5f5586cfb9a21163ca47a5f77ac34fa8ceb5d17d2fa2c0d81f41cbd7f8c6fa52c77e2c039acc0f4d09e71abdc51144246900f6bef5e3c4b333f77d89362 + languageName: node + linkType: hard + +"agent-base@npm:^7.1.2": + version: 7.1.4 + resolution: "agent-base@npm:7.1.4" + checksum: 10c0/c2c9ab7599692d594b6a161559ada307b7a624fa4c7b03e3afdb5a5e31cd0e53269115b620fcab024c5ac6a6f37fa5eb2e004f076ad30f5f7e6b8b671f7b35fe + languageName: node + linkType: hard + +"ansi-styles@npm:^3.2.1": + version: 3.2.1 + resolution: "ansi-styles@npm:3.2.1" + dependencies: + color-convert: "npm:^1.9.0" + checksum: 10c0/ece5a8ef069fcc5298f67e3f4771a663129abd174ea2dfa87923a2be2abf6cd367ef72ac87942da00ce85bd1d651d4cd8595aebdb1b385889b89b205860e977b + languageName: node + linkType: hard + +"any-promise@npm:^1.0.0": + version: 1.3.0 + resolution: "any-promise@npm:1.3.0" + checksum: 10c0/60f0298ed34c74fef50daab88e8dab786036ed5a7fad02e012ab57e376e0a0b4b29e83b95ea9b5e7d89df762f5f25119b83e00706ecaccb22cfbacee98d74889 + languageName: node + linkType: hard + +"anymatch@npm:~3.1.2": + version: 3.1.3 + resolution: "anymatch@npm:3.1.3" + dependencies: + normalize-path: "npm:^3.0.0" + picomatch: "npm:^2.0.4" + checksum: 10c0/57b06ae984bc32a0d22592c87384cd88fe4511b1dd7581497831c56d41939c8a001b28e7b853e1450f2bf61992dfcaa8ae2d0d161a0a90c4fb631ef07098fbac + languageName: node + linkType: hard + +"arg@npm:^5.0.1, arg@npm:^5.0.2": + version: 5.0.2 + resolution: "arg@npm:5.0.2" + checksum: 10c0/ccaf86f4e05d342af6666c569f844bec426595c567d32a8289715087825c2ca7edd8a3d204e4d2fb2aa4602e09a57d0c13ea8c9eea75aac3dbb4af5514e6800e + languageName: node + linkType: hard + +"array-buffer-byte-length@npm:^1.0.1, array-buffer-byte-length@npm:^1.0.2": + version: 1.0.2 + resolution: "array-buffer-byte-length@npm:1.0.2" + dependencies: + call-bound: "npm:^1.0.3" + is-array-buffer: "npm:^3.0.5" + checksum: 10c0/74e1d2d996941c7a1badda9cabb7caab8c449db9086407cad8a1b71d2604cc8abf105db8ca4e02c04579ec58b7be40279ddb09aea4784832984485499f48432d + languageName: node + linkType: hard + +"array-flatten@npm:1.1.1": + version: 1.1.1 + resolution: "array-flatten@npm:1.1.1" + checksum: 10c0/806966c8abb2f858b08f5324d9d18d7737480610f3bd5d3498aaae6eb5efdc501a884ba019c9b4a8f02ff67002058749d05548fd42fa8643f02c9c7f22198b91 + languageName: node + linkType: hard + +"arraybuffer.prototype.slice@npm:^1.0.4": + version: 1.0.4 + resolution: "arraybuffer.prototype.slice@npm:1.0.4" + dependencies: + array-buffer-byte-length: "npm:^1.0.1" + call-bind: "npm:^1.0.8" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.5" + es-errors: "npm:^1.3.0" + get-intrinsic: "npm:^1.2.6" + is-array-buffer: "npm:^3.0.4" + checksum: 10c0/2f2459caa06ae0f7f615003f9104b01f6435cc803e11bd2a655107d52a1781dc040532dc44d93026b694cc18793993246237423e13a5337e86b43ed604932c06 + languageName: node + linkType: hard + +"assertion-error@npm:^2.0.1": + version: 2.0.1 + resolution: "assertion-error@npm:2.0.1" + checksum: 10c0/bbbcb117ac6480138f8c93cf7f535614282dea9dc828f540cdece85e3c665e8f78958b96afac52f29ff883c72638e6a87d469ecc9fe5bc902df03ed24a55dba8 + languageName: node + linkType: hard + +"async-function@npm:^1.0.0": + version: 1.0.0 + resolution: "async-function@npm:1.0.0" + checksum: 10c0/669a32c2cb7e45091330c680e92eaeb791bc1d4132d827591e499cd1f776ff5a873e77e5f92d0ce795a8d60f10761dec9ddfe7225a5de680f5d357f67b1aac73 + languageName: node + linkType: hard + +"async-generator-function@npm:^1.0.0": + version: 1.0.0 + resolution: "async-generator-function@npm:1.0.0" + checksum: 10c0/2c50ef856c543ad500d8d8777d347e3c1ba623b93e99c9263ecc5f965c1b12d2a140e2ab6e43c3d0b85366110696f28114649411cbcd10b452a92a2318394186 + languageName: node + linkType: hard + +"autoprefixer@npm:^10.4.20": + version: 10.5.4 + resolution: "autoprefixer@npm:10.5.4" + dependencies: + browserslist: "npm:^4.28.6" + caniuse-lite: "npm:^1.0.30001806" + fraction.js: "npm:^5.3.4" + picocolors: "npm:^1.1.1" + postcss-value-parser: "npm:^4.2.0" + peerDependencies: + postcss: ^8.1.0 + bin: + autoprefixer: bin/autoprefixer + checksum: 10c0/274ebc9fa50ac0d7a5aa9bc29baa502f2d2006f47a899dac2de4ba86c989defaa62b096e68692e15d53c9ed2b62c57dd047ceba61d344908d8b6e61f220a10e8 + languageName: node + linkType: hard + +"available-typed-arrays@npm:^1.0.7": + version: 1.0.7 + resolution: "available-typed-arrays@npm:1.0.7" + dependencies: + possible-typed-array-names: "npm:^1.0.0" + checksum: 10c0/d07226ef4f87daa01bd0fe80f8f310982e345f372926da2e5296aecc25c41cab440916bbaa4c5e1034b453af3392f67df5961124e4b586df1e99793a1374bdb2 + languageName: node + linkType: hard + +"babel-dead-code-elimination@npm:^1.0.6": + version: 1.0.12 + resolution: "babel-dead-code-elimination@npm:1.0.12" + dependencies: + "@babel/core": "npm:^7.23.7" + "@babel/parser": "npm:^7.23.6" + "@babel/traverse": "npm:^7.23.7" + "@babel/types": "npm:^7.23.6" + checksum: 10c0/9289b66ce202a5f2b8c160f6a0ed16b97c42a9e06e92ea997df4e34d6a8309a01cf641b21467ee6a2713efd7e158b4b770e04a07f3f9d30eb05d428d186f7a60 + languageName: node + linkType: hard + +"balanced-match@npm:^1.0.0": + version: 1.0.2 + resolution: "balanced-match@npm:1.0.2" + checksum: 10c0/9308baf0a7e4838a82bbfd11e01b1cb0f0cf2893bc1676c27c2a8c0e70cbae1c59120c3268517a8ae7fb6376b4639ef81ca22582611dbee4ed28df945134aaee + languageName: node + linkType: hard + +"base64-js@npm:^1.3.0": + version: 1.5.1 + resolution: "base64-js@npm:1.5.1" + checksum: 10c0/f23823513b63173a001030fae4f2dabe283b99a9d324ade3ad3d148e218134676f1ee8568c877cd79ec1c53158dcf2d2ba527a97c606618928ba99dd930102bf + languageName: node + linkType: hard + +"baseline-browser-mapping@npm:^2.10.42": + version: 2.10.43 + resolution: "baseline-browser-mapping@npm:2.10.43" + bin: + baseline-browser-mapping: dist/cli.cjs + checksum: 10c0/08a9e585fe0b1672a0a41026579d41debbaff2c1ceffc9efe510b47713b9d78296846d7a86f0d74d8f64af0088fdebd715f5ea6cb88d6fe1d57f8fafa686b3b5 + languageName: node + linkType: hard + +"basic-auth@npm:~2.0.1": + version: 2.0.1 + resolution: "basic-auth@npm:2.0.1" + dependencies: + safe-buffer: "npm:5.1.2" + checksum: 10c0/05f56db3a0fc31c89c86b605231e32ee143fb6ae38dc60616bc0970ae6a0f034172def99e69d3aed0e2c9e7cac84e2d63bc51a0b5ff6ab5fc8808cc8b29923c1 + languageName: node + linkType: hard + +"bignumber.js@npm:^9.0.0": + version: 9.3.1 + resolution: "bignumber.js@npm:9.3.1" + checksum: 10c0/61342ba5fe1c10887f0ecf5be02ff6709271481aff48631f86b4d37d55a99b87ce441cfd54df3d16d10ee07ceab7e272fc0be430c657ffafbbbf7b7d631efb75 + languageName: node + linkType: hard + +"binary-extensions@npm:^2.0.0": + version: 2.3.0 + resolution: "binary-extensions@npm:2.3.0" + checksum: 10c0/75a59cafc10fb12a11d510e77110c6c7ae3f4ca22463d52487709ca7f18f69d886aa387557cc9864fbdb10153d0bdb4caacabf11541f55e89ed6e18d12ece2b5 + languageName: node + linkType: hard + +"body-parser@npm:~1.20.5": + version: 1.20.6 + resolution: "body-parser@npm:1.20.6" + dependencies: + bytes: "npm:~3.1.2" + content-type: "npm:~1.0.5" + debug: "npm:2.6.9" + depd: "npm:2.0.0" + destroy: "npm:~1.2.0" + http-errors: "npm:~2.0.1" + iconv-lite: "npm:~0.4.24" + on-finished: "npm:~2.4.1" + qs: "npm:~6.15.1" + raw-body: "npm:~2.5.3" + type-is: "npm:~1.6.18" + unpipe: "npm:~1.0.0" + checksum: 10c0/ad477209f1e714c41fa892a7d2fe22e10b23262103cc25cc6492dd3e6f7869cd2d8b00928b543a1a14d3c3663432452a192efd00422fad6f3687b0e38f7e3b07 + languageName: node + linkType: hard + +"brace-expansion@npm:^1.1.7": + version: 1.1.16 + resolution: "brace-expansion@npm:1.1.16" + dependencies: + balanced-match: "npm:^1.0.0" + concat-map: "npm:0.0.1" + checksum: 10c0/b2a915bbedbf4e45840d1fb9a4d391bbf26a79475bd134714d3cee34f1f0edb0ce982738028843be5fbaf8039429f71fa487df8c915b6065ced542c83e58fae6 + languageName: node + linkType: hard + +"braces@npm:^3.0.3, braces@npm:~3.0.2": + version: 3.0.3 + resolution: "braces@npm:3.0.3" + dependencies: + fill-range: "npm:^7.1.1" + checksum: 10c0/7c6dfd30c338d2997ba77500539227b9d1f85e388a5f43220865201e407e076783d0881f2d297b9f80951b4c957fcf0b51c1d2d24227631643c3f7c284b0aa04 + languageName: node + linkType: hard + +"browserslist@npm:^4.24.0, browserslist@npm:^4.28.6": + version: 4.28.6 + resolution: "browserslist@npm:4.28.6" + dependencies: + baseline-browser-mapping: "npm:^2.10.42" + caniuse-lite: "npm:^1.0.30001803" + electron-to-chromium: "npm:^1.5.389" + node-releases: "npm:^2.0.51" + update-browserslist-db: "npm:^1.2.3" + bin: + browserslist: cli.js + checksum: 10c0/259e3c2e0e59daf1fab0889303d397b35505c1c910e503a329990c4f6c0e589c0959fcd2627487311e4a94d7b9e2a9b1fa02875592f5c98b8e9e03e0ccd1e3ea + languageName: node + linkType: hard + +"buffer-equal-constant-time@npm:^1.0.1": + version: 1.0.1 + resolution: "buffer-equal-constant-time@npm:1.0.1" + checksum: 10c0/fb2294e64d23c573d0dd1f1e7a466c3e978fe94a4e0f8183937912ca374619773bef8e2aceb854129d2efecbbc515bbd0cc78d2734a3e3031edb0888531bbc8e + languageName: node + linkType: hard + +"buffer-from@npm:^1.0.0": + version: 1.1.2 + resolution: "buffer-from@npm:1.1.2" + checksum: 10c0/124fff9d66d691a86d3b062eff4663fe437a9d9ee4b47b1b9e97f5a5d14f6d5399345db80f796827be7c95e70a8e765dd404b7c3ff3b3324f98e9b0c8826cc34 + languageName: node + linkType: hard + +"bytes@npm:3.1.2, bytes@npm:~3.1.2": + version: 3.1.2 + resolution: "bytes@npm:3.1.2" + checksum: 10c0/76d1c43cbd602794ad8ad2ae94095cddeb1de78c5dddaa7005c51af10b0176c69971a6d88e805a90c2b6550d76636e43c40d8427a808b8645ede885de4a0358e + languageName: node + linkType: hard + +"cac@npm:^6.7.14": + version: 6.7.14 + resolution: "cac@npm:6.7.14" + checksum: 10c0/4ee06aaa7bab8981f0d54e5f5f9d4adcd64058e9697563ce336d8a3878ed018ee18ebe5359b2430eceae87e0758e62ea2019c3f52ae6e211b1bd2e133856cd10 + languageName: node + linkType: hard + +"call-bind-apply-helpers@npm:^1.0.1, call-bind-apply-helpers@npm:^1.0.2": + version: 1.0.2 + resolution: "call-bind-apply-helpers@npm:1.0.2" + dependencies: + es-errors: "npm:^1.3.0" + function-bind: "npm:^1.1.2" + checksum: 10c0/47bd9901d57b857590431243fea704ff18078b16890a6b3e021e12d279bbf211d039155e27d7566b374d49ee1f8189344bac9833dec7a20cdec370506361c938 + languageName: node + linkType: hard + +"call-bind@npm:^1.0.7, call-bind@npm:^1.0.8, call-bind@npm:^1.0.9": + version: 1.0.9 + resolution: "call-bind@npm:1.0.9" + dependencies: + call-bind-apply-helpers: "npm:^1.0.2" + es-define-property: "npm:^1.0.1" + get-intrinsic: "npm:^1.3.0" + set-function-length: "npm:^1.2.2" + checksum: 10c0/a6621f6da1444481919ce3b4983dff725691e0754d3507ae483ce56e54985f2da7d6f1df512c56dbf28660745cf1ca52553f1fc9aef5557f3ce353ef14fab714 + languageName: node + linkType: hard + +"call-bound@npm:^1.0.2, call-bound@npm:^1.0.3, call-bound@npm:^1.0.4": + version: 1.0.4 + resolution: "call-bound@npm:1.0.4" + dependencies: + call-bind-apply-helpers: "npm:^1.0.2" + get-intrinsic: "npm:^1.3.0" + checksum: 10c0/f4796a6a0941e71c766aea672f63b72bc61234c4f4964dc6d7606e3664c307e7d77845328a8f3359ce39ddb377fed67318f9ee203dea1d47e46165dcf2917644 + languageName: node + linkType: hard + +"camelcase-css@npm:^2.0.1": + version: 2.0.1 + resolution: "camelcase-css@npm:2.0.1" + checksum: 10c0/1a1a3137e8a781e6cbeaeab75634c60ffd8e27850de410c162cce222ea331cd1ba5364e8fb21c95e5ca76f52ac34b81a090925ca00a87221355746d049c6e273 + languageName: node + linkType: hard + +"caniuse-lite@npm:^1.0.30001803, caniuse-lite@npm:^1.0.30001806": + version: 1.0.30001806 + resolution: "caniuse-lite@npm:1.0.30001806" + checksum: 10c0/9442c8afff0968e9b2ce0104a7340c1ce4a5c09f469ccb9eef10d25712ea0afe542486e5318c697c70dd502f988cfc04eaa1c9438c92ac3bcf4d708a2a17bee1 + languageName: node + linkType: hard + +"chai@npm:^5.2.0": + version: 5.3.3 + resolution: "chai@npm:5.3.3" + dependencies: + assertion-error: "npm:^2.0.1" + check-error: "npm:^2.1.1" + deep-eql: "npm:^5.0.1" + loupe: "npm:^3.1.0" + pathval: "npm:^2.0.0" + checksum: 10c0/b360fd4d38861622e5010c2f709736988b05c7f31042305fa3f4e9911f6adb80ccfb4e302068bf8ed10e835c2e2520cba0f5edc13d878b886987e5aa62483f53 + languageName: node + linkType: hard + +"chalk@npm:^2.4.1": + version: 2.4.2 + resolution: "chalk@npm:2.4.2" + dependencies: + ansi-styles: "npm:^3.2.1" + escape-string-regexp: "npm:^1.0.5" + supports-color: "npm:^5.3.0" + checksum: 10c0/e6543f02ec877732e3a2d1c3c3323ddb4d39fbab687c23f526e25bd4c6a9bf3b83a696e8c769d078e04e5754921648f7821b2a2acfd16c550435fd630026e073 + languageName: node + linkType: hard + +"check-error@npm:^2.1.1": + version: 2.1.3 + resolution: "check-error@npm:2.1.3" + checksum: 10c0/878e99038fb6476316b74668cd6a498c7e66df3efe48158fa40db80a06ba4258742ac3ee2229c4a2a98c5e73f5dff84eb3e50ceb6b65bbd8f831eafc8338607d + languageName: node + linkType: hard + +"chokidar@npm:^3.6.0": + version: 3.6.0 + resolution: "chokidar@npm:3.6.0" + dependencies: + anymatch: "npm:~3.1.2" + braces: "npm:~3.0.2" + fsevents: "npm:~2.3.2" + glob-parent: "npm:~5.1.2" + is-binary-path: "npm:~2.1.0" + is-glob: "npm:~4.0.1" + normalize-path: "npm:~3.0.0" + readdirp: "npm:~3.6.0" + dependenciesMeta: + fsevents: + optional: true + checksum: 10c0/8361dcd013f2ddbe260eacb1f3cb2f2c6f2b0ad118708a343a5ed8158941a39cb8fb1d272e0f389712e74ee90ce8ba864eece9e0e62b9705cb468a2f6d917462 + languageName: node + linkType: hard + +"chokidar@npm:^4.0.0": + version: 4.0.3 + resolution: "chokidar@npm:4.0.3" + dependencies: + readdirp: "npm:^4.0.1" + checksum: 10c0/a58b9df05bb452f7d105d9e7229ac82fa873741c0c40ddcc7bb82f8a909fbe3f7814c9ebe9bc9a2bef9b737c0ec6e2d699d179048ef06ad3ec46315df0ebe6ad + languageName: node + linkType: hard + +"chownr@npm:^3.0.0": + version: 3.0.0 + resolution: "chownr@npm:3.0.0" + checksum: 10c0/43925b87700f7e3893296c8e9c56cc58f926411cce3a6e5898136daaf08f08b9a8eb76d37d3267e707d0dcc17aed2e2ebdf5848c0c3ce95cf910a919935c1b10 + languageName: node + linkType: hard + +"color-convert@npm:^1.9.0": + version: 1.9.3 + resolution: "color-convert@npm:1.9.3" + dependencies: + color-name: "npm:1.1.3" + checksum: 10c0/5ad3c534949a8c68fca8fbc6f09068f435f0ad290ab8b2f76841b9e6af7e0bb57b98cb05b0e19fe33f5d91e5a8611ad457e5f69e0a484caad1f7487fd0e8253c + languageName: node + linkType: hard + +"color-name@npm:1.1.3": + version: 1.1.3 + resolution: "color-name@npm:1.1.3" + checksum: 10c0/566a3d42cca25b9b3cd5528cd7754b8e89c0eb646b7f214e8e2eaddb69994ac5f0557d9c175eb5d8f0ad73531140d9c47525085ee752a91a2ab15ab459caf6d6 + languageName: node + linkType: hard + +"commander@npm:12.0.0": + version: 12.0.0 + resolution: "commander@npm:12.0.0" + checksum: 10c0/e51cac1d1d0aa1f76581981d2256a9249497e08f5a370bf63b0dfc7e76a647fc8cbc3ddd507928f2bdca6c514c83834e87e2687ace2fe2fc7cc7e631bf80f83d + languageName: node + linkType: hard + +"commander@npm:^4.0.0": + version: 4.1.1 + resolution: "commander@npm:4.1.1" + checksum: 10c0/84a76c08fe6cc08c9c93f62ac573d2907d8e79138999312c92d4155bc2325d487d64d13f669b2000c9f8caf70493c1be2dac74fec3c51d5a04f8bc3ae1830bab + languageName: node + linkType: hard + +"compressible@npm:~2.0.18": + version: 2.0.18 + resolution: "compressible@npm:2.0.18" + dependencies: + mime-db: "npm:>= 1.43.0 < 2" + checksum: 10c0/8a03712bc9f5b9fe530cc5a79e164e665550d5171a64575d7dcf3e0395d7b4afa2d79ab176c61b5b596e28228b350dd07c1a2a6ead12fd81d1b6cd632af2fef7 + languageName: node + linkType: hard + +"compression@npm:^1.8.1": + version: 1.8.1 + resolution: "compression@npm:1.8.1" + dependencies: + bytes: "npm:3.1.2" + compressible: "npm:~2.0.18" + debug: "npm:2.6.9" + negotiator: "npm:~0.6.4" + on-headers: "npm:~1.1.0" + safe-buffer: "npm:5.2.1" + vary: "npm:~1.1.2" + checksum: 10c0/85114b0b91c16594dc8c671cd9b05ef5e465066a60e5a4ed8b4551661303559a896ed17bb72c4234c04064e078f6ca86a34b8690349499a43f6fc4b844475da4 + languageName: node + linkType: hard + +"concat-map@npm:0.0.1": + version: 0.0.1 + resolution: "concat-map@npm:0.0.1" + checksum: 10c0/c996b1cfdf95b6c90fee4dae37e332c8b6eb7d106430c17d538034c0ad9a1630cb194d2ab37293b1bdd4d779494beee7786d586a50bd9376fd6f7bcc2bd4c98f + languageName: node + linkType: hard + +"confbox@npm:^0.2.4": + version: 0.2.4 + resolution: "confbox@npm:0.2.4" + checksum: 10c0/4c36af33d9df7034300c452f7b289179264493bd0671fa81b995a0d70dc897b1d37f1af10d3ffb187f178d17ba1ed2ba167ed0f599ba3a139c271205dd553f73 + languageName: node + linkType: hard + +"content-disposition@npm:~0.5.4": + version: 0.5.4 + resolution: "content-disposition@npm:0.5.4" + dependencies: + safe-buffer: "npm:5.2.1" + checksum: 10c0/bac0316ebfeacb8f381b38285dc691c9939bf0a78b0b7c2d5758acadad242d04783cee5337ba7d12a565a19075af1b3c11c728e1e4946de73c6ff7ce45f3f1bb + languageName: node + linkType: hard + +"content-type@npm:~1.0.4, content-type@npm:~1.0.5": + version: 1.0.5 + resolution: "content-type@npm:1.0.5" + checksum: 10c0/b76ebed15c000aee4678c3707e0860cb6abd4e680a598c0a26e17f0bfae723ec9cc2802f0ff1bc6e4d80603719010431d2231018373d4dde10f9ccff9dadf5af + languageName: node + linkType: hard + +"convert-source-map@npm:^2.0.0": + version: 2.0.0 + resolution: "convert-source-map@npm:2.0.0" + checksum: 10c0/8f2f7a27a1a011cc6cc88cc4da2d7d0cfa5ee0369508baae3d98c260bb3ac520691464e5bbe4ae7cdf09860c1d69ecc6f70c63c6e7c7f7e3f18ec08484dc7d9b + languageName: node + linkType: hard + +"cookie-signature@npm:~1.0.6": + version: 1.0.7 + resolution: "cookie-signature@npm:1.0.7" + checksum: 10c0/e7731ad2995ae2efeed6435ec1e22cdd21afef29d300c27281438b1eab2bae04ef0d1a203928c0afec2cee72aa36540b8747406ebe308ad23c8e8cc3c26c9c51 + languageName: node + linkType: hard + +"cookie@npm:^1.0.1": + version: 1.1.1 + resolution: "cookie@npm:1.1.1" + checksum: 10c0/79c4ddc0fcad9c4f045f826f42edf54bcc921a29586a4558b0898277fa89fb47be95bc384c2253f493af7b29500c830da28341274527328f18eba9f58afa112c + languageName: node + linkType: hard + +"cookie@npm:~0.7.1": + version: 0.7.2 + resolution: "cookie@npm:0.7.2" + checksum: 10c0/9596e8ccdbf1a3a88ae02cf5ee80c1c50959423e1022e4e60b91dd87c622af1da309253d8abdb258fb5e3eacb4f08e579dc58b4897b8087574eee0fd35dfa5d2 + languageName: node + linkType: hard + +"copy-anything@npm:^3.0.2": + version: 3.0.5 + resolution: "copy-anything@npm:3.0.5" + dependencies: + is-what: "npm:^4.1.8" + checksum: 10c0/01eadd500c7e1db71d32d95a3bfaaedcb839ef891c741f6305ab0461398056133de08f2d1bf4c392b364e7bdb7ce498513896e137a7a183ac2516b065c28a4fe + languageName: node + linkType: hard + +"cross-spawn@npm:^6.0.5": + version: 6.0.6 + resolution: "cross-spawn@npm:6.0.6" + dependencies: + nice-try: "npm:^1.0.4" + path-key: "npm:^2.0.1" + semver: "npm:^5.5.0" + shebang-command: "npm:^1.2.0" + which: "npm:^1.2.9" + checksum: 10c0/bf61fb890e8635102ea9bce050515cf915ff6a50ccaa0b37a17dc82fded0fb3ed7af5478b9367b86baee19127ad86af4be51d209f64fd6638c0862dca185fe1d + languageName: node + linkType: hard + +"cssesc@npm:^3.0.0": + version: 3.0.0 + resolution: "cssesc@npm:3.0.0" + bin: + cssesc: bin/cssesc + checksum: 10c0/6bcfd898662671be15ae7827120472c5667afb3d7429f1f917737f3bf84c4176003228131b643ae74543f17a394446247df090c597bb9a728cce298606ed0aa7 + languageName: node + linkType: hard + +"csstype@npm:^3.2.2": + version: 3.2.3 + resolution: "csstype@npm:3.2.3" + checksum: 10c0/cd29c51e70fa822f1cecd8641a1445bed7063697469d35633b516e60fe8c1bde04b08f6c5b6022136bb669b64c63d4173af54864510fbb4ee23281801841a3ce + languageName: node + linkType: hard + +"data-uri-to-buffer@npm:^4.0.0": + version: 4.0.1 + resolution: "data-uri-to-buffer@npm:4.0.1" + checksum: 10c0/20a6b93107597530d71d4cb285acee17f66bcdfc03fd81040921a81252f19db27588d87fc8fc69e1950c55cfb0bf8ae40d0e5e21d907230813eb5d5a7f9eb45b + languageName: node + linkType: hard + +"data-view-buffer@npm:^1.0.2": + version: 1.0.2 + resolution: "data-view-buffer@npm:1.0.2" + dependencies: + call-bound: "npm:^1.0.3" + es-errors: "npm:^1.3.0" + is-data-view: "npm:^1.0.2" + checksum: 10c0/7986d40fc7979e9e6241f85db8d17060dd9a71bd53c894fa29d126061715e322a4cd47a00b0b8c710394854183d4120462b980b8554012acc1c0fa49df7ad38c + languageName: node + linkType: hard + +"data-view-byte-length@npm:^1.0.2": + version: 1.0.2 + resolution: "data-view-byte-length@npm:1.0.2" + dependencies: + call-bound: "npm:^1.0.3" + es-errors: "npm:^1.3.0" + is-data-view: "npm:^1.0.2" + checksum: 10c0/f8a4534b5c69384d95ac18137d381f18a5cfae1f0fc1df0ef6feef51ef0d568606d970b69e02ea186c6c0f0eac77fe4e6ad96fec2569cc86c3afcc7475068c55 + languageName: node + linkType: hard + +"data-view-byte-offset@npm:^1.0.1": + version: 1.0.1 + resolution: "data-view-byte-offset@npm:1.0.1" + dependencies: + call-bound: "npm:^1.0.2" + es-errors: "npm:^1.3.0" + is-data-view: "npm:^1.0.1" + checksum: 10c0/fa7aa40078025b7810dcffc16df02c480573b7b53ef1205aa6a61533011005c1890e5ba17018c692ce7c900212b547262d33279fde801ad9843edc0863bf78c4 + languageName: node + linkType: hard + +"debug@npm:2.6.9": + version: 2.6.9 + resolution: "debug@npm:2.6.9" + dependencies: + ms: "npm:2.0.0" + checksum: 10c0/121908fb839f7801180b69a7e218a40b5a0b718813b886b7d6bdb82001b931c938e2941d1e4450f33a1b1df1da653f5f7a0440c197f29fbf8a6e9d45ff6ef589 + languageName: node + linkType: hard + +"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.3.1, debug@npm:^4.4.1": + version: 4.4.3 + resolution: "debug@npm:4.4.3" + dependencies: + ms: "npm:^2.1.3" + peerDependenciesMeta: + supports-color: + optional: true + checksum: 10c0/d79136ec6c83ecbefd0f6a5593da6a9c91ec4d7ddc4b54c883d6e71ec9accb5f67a1a5e96d00a328196b5b5c86d365e98d8a3a70856aaf16b4e7b1985e67f5a6 + languageName: node + linkType: hard + +"dedent@npm:^1.5.3": + version: 1.7.2 + resolution: "dedent@npm:1.7.2" + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + checksum: 10c0/acaff07cac355b93f17b1b17ebbb84d3cc55af6ab4b7814c3f505e061903e168bc6bf9ddce331552d64dee1525f0b4c549c9ade46aebfac6f69caaed74e90751 + languageName: node + linkType: hard + +"deep-eql@npm:^5.0.1": + version: 5.0.2 + resolution: "deep-eql@npm:5.0.2" + checksum: 10c0/7102cf3b7bb719c6b9c0db2e19bf0aa9318d141581befe8c7ce8ccd39af9eaa4346e5e05adef7f9bd7015da0f13a3a25dcfe306ef79dc8668aedbecb658dd247 + languageName: node + linkType: hard + +"define-data-property@npm:^1.0.1, define-data-property@npm:^1.1.4": + version: 1.1.4 + resolution: "define-data-property@npm:1.1.4" + dependencies: + es-define-property: "npm:^1.0.0" + es-errors: "npm:^1.3.0" + gopd: "npm:^1.0.1" + checksum: 10c0/dea0606d1483eb9db8d930d4eac62ca0fa16738b0b3e07046cddfacf7d8c868bbe13fa0cb263eb91c7d0d527960dc3f2f2471a69ed7816210307f6744fe62e37 + languageName: node + linkType: hard + +"define-properties@npm:^1.2.1": + version: 1.2.1 + resolution: "define-properties@npm:1.2.1" + dependencies: + define-data-property: "npm:^1.0.1" + has-property-descriptors: "npm:^1.0.0" + object-keys: "npm:^1.1.1" + checksum: 10c0/88a152319ffe1396ccc6ded510a3896e77efac7a1bfbaa174a7b00414a1747377e0bb525d303794a47cf30e805c2ec84e575758512c6e44a993076d29fd4e6c3 + languageName: node + linkType: hard + +"depd@npm:2.0.0, depd@npm:~2.0.0": + version: 2.0.0 + resolution: "depd@npm:2.0.0" + checksum: 10c0/58bd06ec20e19529b06f7ad07ddab60e504d9e0faca4bd23079fac2d279c3594334d736508dc350e06e510aba5e22e4594483b3a6562ce7c17dd797f4cc4ad2c + languageName: node + linkType: hard + +"destroy@npm:1.2.0, destroy@npm:~1.2.0": + version: 1.2.0 + resolution: "destroy@npm:1.2.0" + checksum: 10c0/bd7633942f57418f5a3b80d5cb53898127bcf53e24cdf5d5f4396be471417671f0fee48a4ebe9a1e9defbde2a31280011af58a57e090ff822f589b443ed4e643 + languageName: node + linkType: hard + +"didyoumean@npm:^1.2.2": + version: 1.2.2 + resolution: "didyoumean@npm:1.2.2" + checksum: 10c0/95d0b53d23b851aacff56dfadb7ecfedce49da4232233baecfeecb7710248c4aa03f0aa8995062f0acafaf925adf8536bd7044a2e68316fd7d411477599bc27b + languageName: node + linkType: hard + +"dlv@npm:^1.1.3": + version: 1.1.3 + resolution: "dlv@npm:1.1.3" + checksum: 10c0/03eb4e769f19a027fd5b43b59e8a05e3fd2100ac239ebb0bf9a745de35d449e2f25cfaf3aa3934664551d72856f4ae8b7822016ce5c42c2d27c18ae79429ec42 + languageName: node + linkType: hard + +"drizzle-kit@npm:^0.31.5": + version: 0.31.10 + resolution: "drizzle-kit@npm:0.31.10" + dependencies: + "@drizzle-team/brocli": "npm:^0.10.2" + "@esbuild-kit/esm-loader": "npm:^2.5.5" + esbuild: "npm:^0.25.4" + tsx: "npm:^4.21.0" + bin: + drizzle-kit: bin.cjs + checksum: 10c0/83edbf8db1e94c734eab92db09ee2e4b00875d18b50b62e61c8e768a636f3ddb4071050cccf54fe55c2efc19ee79e350e5fe6060906153ffbd7d7534e2590115 + languageName: node + linkType: hard + +"drizzle-orm@npm:0.44.6": + version: 0.44.6 + resolution: "drizzle-orm@npm:0.44.6" + peerDependencies: + "@aws-sdk/client-rds-data": ">=3" + "@cloudflare/workers-types": ">=4" + "@electric-sql/pglite": ">=0.2.0" + "@libsql/client": ">=0.10.0" + "@libsql/client-wasm": ">=0.10.0" + "@neondatabase/serverless": ">=0.10.0" + "@op-engineering/op-sqlite": ">=2" + "@opentelemetry/api": ^1.4.1 + "@planetscale/database": ">=1.13" + "@prisma/client": "*" + "@tidbcloud/serverless": "*" + "@types/better-sqlite3": "*" + "@types/pg": "*" + "@types/sql.js": "*" + "@upstash/redis": ">=1.34.7" + "@vercel/postgres": ">=0.8.0" + "@xata.io/client": "*" + better-sqlite3: ">=7" + bun-types: "*" + expo-sqlite: ">=14.0.0" + gel: ">=2" + knex: "*" + kysely: "*" + mysql2: ">=2" + pg: ">=8" + postgres: ">=3" + sql.js: ">=1" + sqlite3: ">=5" + peerDependenciesMeta: + "@aws-sdk/client-rds-data": + optional: true + "@cloudflare/workers-types": + optional: true + "@electric-sql/pglite": + optional: true + "@libsql/client": + optional: true + "@libsql/client-wasm": + optional: true + "@neondatabase/serverless": + optional: true + "@op-engineering/op-sqlite": + optional: true + "@opentelemetry/api": + optional: true + "@planetscale/database": + optional: true + "@prisma/client": + optional: true + "@tidbcloud/serverless": + optional: true + "@types/better-sqlite3": + optional: true + "@types/pg": + optional: true + "@types/sql.js": + optional: true + "@upstash/redis": + optional: true + "@vercel/postgres": + optional: true + "@xata.io/client": + optional: true + better-sqlite3: + optional: true + bun-types: + optional: true + expo-sqlite: + optional: true + gel: + optional: true + knex: + optional: true + kysely: + optional: true + mysql2: + optional: true + pg: + optional: true + postgres: + optional: true + prisma: + optional: true + sql.js: + optional: true + sqlite3: + optional: true + checksum: 10c0/b9629fd8894f08a33661a33b9e1bd39dc2685df29bb254bb9b0dad2d447464eafbd9d089c92b8977a463c0e9f399d7991273ad369df9d8a5f356fa0493859660 + languageName: node + linkType: hard + +"dunder-proto@npm:^1.0.0, dunder-proto@npm:^1.0.1": + version: 1.0.1 + resolution: "dunder-proto@npm:1.0.1" + dependencies: + call-bind-apply-helpers: "npm:^1.0.1" + es-errors: "npm:^1.3.0" + gopd: "npm:^1.2.0" + checksum: 10c0/199f2a0c1c16593ca0a145dbf76a962f8033ce3129f01284d48c45ed4e14fea9bbacd7b3610b6cdc33486cef20385ac054948fefc6272fcce645c09468f93031 + languageName: node + linkType: hard + +"ecdsa-sig-formatter@npm:1.0.11, ecdsa-sig-formatter@npm:^1.0.11": + version: 1.0.11 + resolution: "ecdsa-sig-formatter@npm:1.0.11" + dependencies: + safe-buffer: "npm:^5.0.1" + checksum: 10c0/ebfbf19d4b8be938f4dd4a83b8788385da353d63307ede301a9252f9f7f88672e76f2191618fd8edfc2f24679236064176fab0b78131b161ee73daa37125408c + languageName: node + linkType: hard + +"ee-first@npm:1.1.1": + version: 1.1.1 + resolution: "ee-first@npm:1.1.1" + checksum: 10c0/b5bb125ee93161bc16bfe6e56c6b04de5ad2aa44234d8f644813cc95d861a6910903132b05093706de2b706599367c4130eb6d170f6b46895686b95f87d017b7 + languageName: node + linkType: hard + +"electron-to-chromium@npm:^1.5.389": + version: 1.5.392 + resolution: "electron-to-chromium@npm:1.5.392" + checksum: 10c0/13b691fb97ef02f6c3da6fecb530d667a2c72c951aaca2c5c13271074d6e2f9a9bb23c84939a6b9274fef1611247f3a7affba7b3961d0bdc0324d59f97046269 + languageName: node + linkType: hard + +"encodeurl@npm:~2.0.0": + version: 2.0.0 + resolution: "encodeurl@npm:2.0.0" + checksum: 10c0/5d317306acb13e6590e28e27924c754163946a2480de11865c991a3a7eed4315cd3fba378b543ca145829569eefe9b899f3d84bb09870f675ae60bc924b01ceb + languageName: node + linkType: hard + +"env-paths@npm:^2.2.0": + version: 2.2.1 + resolution: "env-paths@npm:2.2.1" + checksum: 10c0/285325677bf00e30845e330eec32894f5105529db97496ee3f598478e50f008c5352a41a30e5e72ec9de8a542b5a570b85699cd63bd2bc646dbcb9f311d83bc4 + languageName: node + linkType: hard + +"error-ex@npm:^1.3.1": + version: 1.3.4 + resolution: "error-ex@npm:1.3.4" + dependencies: + is-arrayish: "npm:^0.2.1" + checksum: 10c0/b9e34ff4778b8f3b31a8377e1c654456f4c41aeaa3d10a1138c3b7635d8b7b2e03eb2475d46d8ae055c1f180a1063e100bffabf64ea7e7388b37735df5328664 + languageName: node + linkType: hard + +"es-abstract-get@npm:^1.0.0": + version: 1.0.0 + resolution: "es-abstract-get@npm:1.0.0" + dependencies: + es-errors: "npm:^1.3.0" + es-object-atoms: "npm:^1.1.2" + is-callable: "npm:^1.2.7" + object-inspect: "npm:^1.13.4" + checksum: 10c0/f9b4838ae719752207383a6d95a74590f891122bf26b92f5e72eeedbe53771029e4561f1cf75ea19330b71bcf3d4f536fb0c8f7e2b601fe24d284f46e488c7e3 + languageName: node + linkType: hard + +"es-abstract@npm:^1.23.2, es-abstract@npm:^1.23.5, es-abstract@npm:^1.23.9, es-abstract@npm:^1.24.2": + version: 1.24.2 + resolution: "es-abstract@npm:1.24.2" + dependencies: + array-buffer-byte-length: "npm:^1.0.2" + arraybuffer.prototype.slice: "npm:^1.0.4" + available-typed-arrays: "npm:^1.0.7" + call-bind: "npm:^1.0.8" + call-bound: "npm:^1.0.4" + data-view-buffer: "npm:^1.0.2" + data-view-byte-length: "npm:^1.0.2" + data-view-byte-offset: "npm:^1.0.1" + es-define-property: "npm:^1.0.1" + es-errors: "npm:^1.3.0" + es-object-atoms: "npm:^1.1.1" + es-set-tostringtag: "npm:^2.1.0" + es-to-primitive: "npm:^1.3.0" + function.prototype.name: "npm:^1.1.8" + get-intrinsic: "npm:^1.3.0" + get-proto: "npm:^1.0.1" + get-symbol-description: "npm:^1.1.0" + globalthis: "npm:^1.0.4" + gopd: "npm:^1.2.0" + has-property-descriptors: "npm:^1.0.2" + has-proto: "npm:^1.2.0" + has-symbols: "npm:^1.1.0" + hasown: "npm:^2.0.2" + internal-slot: "npm:^1.1.0" + is-array-buffer: "npm:^3.0.5" + is-callable: "npm:^1.2.7" + is-data-view: "npm:^1.0.2" + is-negative-zero: "npm:^2.0.3" + is-regex: "npm:^1.2.1" + is-set: "npm:^2.0.3" + is-shared-array-buffer: "npm:^1.0.4" + is-string: "npm:^1.1.1" + is-typed-array: "npm:^1.1.15" + is-weakref: "npm:^1.1.1" + math-intrinsics: "npm:^1.1.0" + object-inspect: "npm:^1.13.4" + object-keys: "npm:^1.1.1" + object.assign: "npm:^4.1.7" + own-keys: "npm:^1.0.1" + regexp.prototype.flags: "npm:^1.5.4" + safe-array-concat: "npm:^1.1.3" + safe-push-apply: "npm:^1.0.0" + safe-regex-test: "npm:^1.1.0" + set-proto: "npm:^1.0.0" + stop-iteration-iterator: "npm:^1.1.0" + string.prototype.trim: "npm:^1.2.10" + string.prototype.trimend: "npm:^1.0.9" + string.prototype.trimstart: "npm:^1.0.8" + typed-array-buffer: "npm:^1.0.3" + typed-array-byte-length: "npm:^1.0.3" + typed-array-byte-offset: "npm:^1.0.4" + typed-array-length: "npm:^1.0.7" + unbox-primitive: "npm:^1.1.0" + which-typed-array: "npm:^1.1.19" + checksum: 10c0/67a5bf21ef5c7d775e6f6131a836323900b4d87194cf544394ac68fe31c57fa53828b978af4a4f551ef307f83a2f910a16b6b982760ad3ddc3dc471f98d5fd1b + languageName: node + linkType: hard + +"es-define-property@npm:^1.0.0, es-define-property@npm:^1.0.1": + version: 1.0.1 + resolution: "es-define-property@npm:1.0.1" + checksum: 10c0/3f54eb49c16c18707949ff25a1456728c883e81259f045003499efba399c08bad00deebf65cccde8c0e07908c1a225c9d472b7107e558f2a48e28d530e34527c + languageName: node + linkType: hard + +"es-errors@npm:^1.3.0": + version: 1.3.0 + resolution: "es-errors@npm:1.3.0" + checksum: 10c0/0a61325670072f98d8ae3b914edab3559b6caa980f08054a3b872052640d91da01d38df55df797fcc916389d77fc92b8d5906cf028f4db46d7e3003abecbca85 + languageName: node + linkType: hard + +"es-module-lexer@npm:^1.3.1, es-module-lexer@npm:^1.7.0": + version: 1.7.0 + resolution: "es-module-lexer@npm:1.7.0" + checksum: 10c0/4c935affcbfeba7fb4533e1da10fa8568043df1e3574b869385980de9e2d475ddc36769891936dbb07036edb3c3786a8b78ccf44964cd130dedc1f2c984b6c7b + languageName: node + linkType: hard + +"es-object-atoms@npm:^1.0.0, es-object-atoms@npm:^1.1.1, es-object-atoms@npm:^1.1.2": + version: 1.1.2 + resolution: "es-object-atoms@npm:1.1.2" + dependencies: + es-errors: "npm:^1.3.0" + checksum: 10c0/1772861f094f739d6f41b579cfb9a18579daffeb434552a370a5fbef50a32d22227e27b63fdbb757b7ddd429d1b42fe52ccae7966d9302a2ec221b6f1b41bbc4 + languageName: node + linkType: hard + +"es-set-tostringtag@npm:^2.1.0": + version: 2.1.0 + resolution: "es-set-tostringtag@npm:2.1.0" + dependencies: + es-errors: "npm:^1.3.0" + get-intrinsic: "npm:^1.2.6" + has-tostringtag: "npm:^1.0.2" + hasown: "npm:^2.0.2" + checksum: 10c0/ef2ca9ce49afe3931cb32e35da4dcb6d86ab02592cfc2ce3e49ced199d9d0bb5085fc7e73e06312213765f5efa47cc1df553a6a5154584b21448e9fb8355b1af + languageName: node + linkType: hard + +"es-to-primitive@npm:^1.3.0": + version: 1.3.4 + resolution: "es-to-primitive@npm:1.3.4" + dependencies: + es-abstract-get: "npm:^1.0.0" + es-define-property: "npm:^1.0.1" + es-errors: "npm:^1.3.0" + is-callable: "npm:^1.2.7" + is-date-object: "npm:^1.1.0" + is-symbol: "npm:^1.1.1" + checksum: 10c0/b10029f8b0b13841bade224ff39005a13d76d9cb803cd1efb18cc96a24414e83e2e7ed15d7ad9d0d0bda66884afd211b7b579b8fa31940e05b060934aa7077d8 + languageName: node + linkType: hard + +"esbuild@npm:^0.21.3": + version: 0.21.5 + resolution: "esbuild@npm:0.21.5" + dependencies: + "@esbuild/aix-ppc64": "npm:0.21.5" + "@esbuild/android-arm": "npm:0.21.5" + "@esbuild/android-arm64": "npm:0.21.5" + "@esbuild/android-x64": "npm:0.21.5" + "@esbuild/darwin-arm64": "npm:0.21.5" + "@esbuild/darwin-x64": "npm:0.21.5" + "@esbuild/freebsd-arm64": "npm:0.21.5" + "@esbuild/freebsd-x64": "npm:0.21.5" + "@esbuild/linux-arm": "npm:0.21.5" + "@esbuild/linux-arm64": "npm:0.21.5" + "@esbuild/linux-ia32": "npm:0.21.5" + "@esbuild/linux-loong64": "npm:0.21.5" + "@esbuild/linux-mips64el": "npm:0.21.5" + "@esbuild/linux-ppc64": "npm:0.21.5" + "@esbuild/linux-riscv64": "npm:0.21.5" + "@esbuild/linux-s390x": "npm:0.21.5" + "@esbuild/linux-x64": "npm:0.21.5" + "@esbuild/netbsd-x64": "npm:0.21.5" + "@esbuild/openbsd-x64": "npm:0.21.5" + "@esbuild/sunos-x64": "npm:0.21.5" + "@esbuild/win32-arm64": "npm:0.21.5" + "@esbuild/win32-ia32": "npm:0.21.5" + "@esbuild/win32-x64": "npm:0.21.5" + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 10c0/fa08508adf683c3f399e8a014a6382a6b65542213431e26206c0720e536b31c09b50798747c2a105a4bbba1d9767b8d3615a74c2f7bf1ddf6d836cd11eb672de + languageName: node + linkType: hard + +"esbuild@npm:^0.24.0": + version: 0.24.2 + resolution: "esbuild@npm:0.24.2" + dependencies: + "@esbuild/aix-ppc64": "npm:0.24.2" + "@esbuild/android-arm": "npm:0.24.2" + "@esbuild/android-arm64": "npm:0.24.2" + "@esbuild/android-x64": "npm:0.24.2" + "@esbuild/darwin-arm64": "npm:0.24.2" + "@esbuild/darwin-x64": "npm:0.24.2" + "@esbuild/freebsd-arm64": "npm:0.24.2" + "@esbuild/freebsd-x64": "npm:0.24.2" + "@esbuild/linux-arm": "npm:0.24.2" + "@esbuild/linux-arm64": "npm:0.24.2" + "@esbuild/linux-ia32": "npm:0.24.2" + "@esbuild/linux-loong64": "npm:0.24.2" + "@esbuild/linux-mips64el": "npm:0.24.2" + "@esbuild/linux-ppc64": "npm:0.24.2" + "@esbuild/linux-riscv64": "npm:0.24.2" + "@esbuild/linux-s390x": "npm:0.24.2" + "@esbuild/linux-x64": "npm:0.24.2" + "@esbuild/netbsd-arm64": "npm:0.24.2" + "@esbuild/netbsd-x64": "npm:0.24.2" + "@esbuild/openbsd-arm64": "npm:0.24.2" + "@esbuild/openbsd-x64": "npm:0.24.2" + "@esbuild/sunos-x64": "npm:0.24.2" + "@esbuild/win32-arm64": "npm:0.24.2" + "@esbuild/win32-ia32": "npm:0.24.2" + "@esbuild/win32-x64": "npm:0.24.2" + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-arm64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-arm64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 10c0/5a25bb08b6ba23db6e66851828d848bd3ff87c005a48c02d83e38879058929878a6baa5a414e1141faee0d1dece3f32b5fbc2a87b82ed6a7aa857cf40359aeb5 + languageName: node + linkType: hard + +"esbuild@npm:^0.25.4": + version: 0.25.12 + resolution: "esbuild@npm:0.25.12" + dependencies: + "@esbuild/aix-ppc64": "npm:0.25.12" + "@esbuild/android-arm": "npm:0.25.12" + "@esbuild/android-arm64": "npm:0.25.12" + "@esbuild/android-x64": "npm:0.25.12" + "@esbuild/darwin-arm64": "npm:0.25.12" + "@esbuild/darwin-x64": "npm:0.25.12" + "@esbuild/freebsd-arm64": "npm:0.25.12" + "@esbuild/freebsd-x64": "npm:0.25.12" + "@esbuild/linux-arm": "npm:0.25.12" + "@esbuild/linux-arm64": "npm:0.25.12" + "@esbuild/linux-ia32": "npm:0.25.12" + "@esbuild/linux-loong64": "npm:0.25.12" + "@esbuild/linux-mips64el": "npm:0.25.12" + "@esbuild/linux-ppc64": "npm:0.25.12" + "@esbuild/linux-riscv64": "npm:0.25.12" + "@esbuild/linux-s390x": "npm:0.25.12" + "@esbuild/linux-x64": "npm:0.25.12" + "@esbuild/netbsd-arm64": "npm:0.25.12" + "@esbuild/netbsd-x64": "npm:0.25.12" + "@esbuild/openbsd-arm64": "npm:0.25.12" + "@esbuild/openbsd-x64": "npm:0.25.12" + "@esbuild/openharmony-arm64": "npm:0.25.12" + "@esbuild/sunos-x64": "npm:0.25.12" + "@esbuild/win32-arm64": "npm:0.25.12" + "@esbuild/win32-ia32": "npm:0.25.12" + "@esbuild/win32-x64": "npm:0.25.12" + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-arm64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-arm64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/openharmony-arm64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 10c0/c205357531423220a9de8e1e6c6514242bc9b1666e762cd67ccdf8fdfdc3f1d0bd76f8d9383958b97ad4c953efdb7b6e8c1f9ca5951cd2b7c5235e8755b34a6b + languageName: node + linkType: hard + +"esbuild@npm:^0.27.0 || ^0.28.0, esbuild@npm:~0.28.0": + version: 0.28.1 + resolution: "esbuild@npm:0.28.1" + dependencies: + "@esbuild/aix-ppc64": "npm:0.28.1" + "@esbuild/android-arm": "npm:0.28.1" + "@esbuild/android-arm64": "npm:0.28.1" + "@esbuild/android-x64": "npm:0.28.1" + "@esbuild/darwin-arm64": "npm:0.28.1" + "@esbuild/darwin-x64": "npm:0.28.1" + "@esbuild/freebsd-arm64": "npm:0.28.1" + "@esbuild/freebsd-x64": "npm:0.28.1" + "@esbuild/linux-arm": "npm:0.28.1" + "@esbuild/linux-arm64": "npm:0.28.1" + "@esbuild/linux-ia32": "npm:0.28.1" + "@esbuild/linux-loong64": "npm:0.28.1" + "@esbuild/linux-mips64el": "npm:0.28.1" + "@esbuild/linux-ppc64": "npm:0.28.1" + "@esbuild/linux-riscv64": "npm:0.28.1" + "@esbuild/linux-s390x": "npm:0.28.1" + "@esbuild/linux-x64": "npm:0.28.1" + "@esbuild/netbsd-arm64": "npm:0.28.1" + "@esbuild/netbsd-x64": "npm:0.28.1" + "@esbuild/openbsd-arm64": "npm:0.28.1" + "@esbuild/openbsd-x64": "npm:0.28.1" + "@esbuild/openharmony-arm64": "npm:0.28.1" + "@esbuild/sunos-x64": "npm:0.28.1" + "@esbuild/win32-arm64": "npm:0.28.1" + "@esbuild/win32-ia32": "npm:0.28.1" + "@esbuild/win32-x64": "npm:0.28.1" + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-arm64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-arm64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/openharmony-arm64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 10c0/29cd456a79ce35ac2c7e05fe871330416b2c395c045d849653f843e51378d6e0d6e774d6dcd01b35f4e83238a29bf8decd04fcd34b3780c589a250b21e5f92bb + languageName: node + linkType: hard + +"esbuild@npm:~0.18.20": + version: 0.18.20 + resolution: "esbuild@npm:0.18.20" + dependencies: + "@esbuild/android-arm": "npm:0.18.20" + "@esbuild/android-arm64": "npm:0.18.20" + "@esbuild/android-x64": "npm:0.18.20" + "@esbuild/darwin-arm64": "npm:0.18.20" + "@esbuild/darwin-x64": "npm:0.18.20" + "@esbuild/freebsd-arm64": "npm:0.18.20" + "@esbuild/freebsd-x64": "npm:0.18.20" + "@esbuild/linux-arm": "npm:0.18.20" + "@esbuild/linux-arm64": "npm:0.18.20" + "@esbuild/linux-ia32": "npm:0.18.20" + "@esbuild/linux-loong64": "npm:0.18.20" + "@esbuild/linux-mips64el": "npm:0.18.20" + "@esbuild/linux-ppc64": "npm:0.18.20" + "@esbuild/linux-riscv64": "npm:0.18.20" + "@esbuild/linux-s390x": "npm:0.18.20" + "@esbuild/linux-x64": "npm:0.18.20" + "@esbuild/netbsd-x64": "npm:0.18.20" + "@esbuild/openbsd-x64": "npm:0.18.20" + "@esbuild/sunos-x64": "npm:0.18.20" + "@esbuild/win32-arm64": "npm:0.18.20" + "@esbuild/win32-ia32": "npm:0.18.20" + "@esbuild/win32-x64": "npm:0.18.20" + dependenciesMeta: + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 10c0/473b1d92842f50a303cf948a11ebd5f69581cd254d599dd9d62f9989858e0533f64e83b723b5e1398a5b488c0f5fd088795b4235f65ecaf4f007d4b79f04bc88 + languageName: node + linkType: hard + +"escalade@npm:^3.2.0": + version: 3.2.0 + resolution: "escalade@npm:3.2.0" + checksum: 10c0/ced4dd3a78e15897ed3be74e635110bbf3b08877b0a41be50dcb325ee0e0b5f65fc2d50e9845194d7c4633f327e2e1c6cce00a71b617c5673df0374201d67f65 + languageName: node + linkType: hard + +"escape-html@npm:~1.0.3": + version: 1.0.3 + resolution: "escape-html@npm:1.0.3" + checksum: 10c0/524c739d776b36c3d29fa08a22e03e8824e3b2fd57500e5e44ecf3cc4707c34c60f9ca0781c0e33d191f2991161504c295e98f68c78fe7baa6e57081ec6ac0a3 + languageName: node + linkType: hard + +"escape-string-regexp@npm:^1.0.5": + version: 1.0.5 + resolution: "escape-string-regexp@npm:1.0.5" + checksum: 10c0/a968ad453dd0c2724e14a4f20e177aaf32bb384ab41b674a8454afe9a41c5e6fe8903323e0a1052f56289d04bd600f81278edf140b0fcc02f5cac98d0f5b5371 + languageName: node + linkType: hard + +"estree-walker@npm:^3.0.3": + version: 3.0.3 + resolution: "estree-walker@npm:3.0.3" + dependencies: + "@types/estree": "npm:^1.0.0" + checksum: 10c0/c12e3c2b2642d2bcae7d5aa495c60fa2f299160946535763969a1c83fc74518ffa9c2cd3a8b69ac56aea547df6a8aac25f729a342992ef0bbac5f1c73e78995d + languageName: node + linkType: hard + +"etag@npm:~1.8.1": + version: 1.8.1 + resolution: "etag@npm:1.8.1" + checksum: 10c0/12be11ef62fb9817314d790089a0a49fae4e1b50594135dcb8076312b7d7e470884b5100d249b28c18581b7fd52f8b485689ffae22a11ed9ec17377a33a08f84 + languageName: node + linkType: hard + +"exit-hook@npm:2.2.1": + version: 2.2.1 + resolution: "exit-hook@npm:2.2.1" + checksum: 10c0/0803726d1b60aade6afd10c73e5a7e1bf256ac9bee78362a88e91a4f735e8c67899f2853ddc613072c05af07bbb067a9978a740e614db1aeef167d50c6dc5c09 + languageName: node + linkType: hard + +"expect-type@npm:^1.2.1": + version: 1.4.0 + resolution: "expect-type@npm:1.4.0" + checksum: 10c0/d40d76b8570695d36587beb3cc28494da2ca3ec8f04e67f5622ed2d372d850e401a9adef19c6835e1a8173903f157c79540b34c7b3fbd7cd8ce726cc903c57b7 + languageName: node + linkType: hard + +"exponential-backoff@npm:^3.1.1": + version: 3.1.3 + resolution: "exponential-backoff@npm:3.1.3" + checksum: 10c0/77e3ae682b7b1f4972f563c6dbcd2b0d54ac679e62d5d32f3e5085feba20483cf28bd505543f520e287a56d4d55a28d7874299941faf637e779a1aa5994d1267 + languageName: node + linkType: hard + +"express@npm:^4.19.2": + version: 4.22.2 + resolution: "express@npm:4.22.2" + dependencies: + accepts: "npm:~1.3.8" + array-flatten: "npm:1.1.1" + body-parser: "npm:~1.20.5" + content-disposition: "npm:~0.5.4" + content-type: "npm:~1.0.4" + cookie: "npm:~0.7.1" + cookie-signature: "npm:~1.0.6" + debug: "npm:2.6.9" + depd: "npm:2.0.0" + encodeurl: "npm:~2.0.0" + escape-html: "npm:~1.0.3" + etag: "npm:~1.8.1" + finalhandler: "npm:~1.3.1" + fresh: "npm:~0.5.2" + http-errors: "npm:~2.0.0" + merge-descriptors: "npm:1.0.3" + methods: "npm:~1.1.2" + on-finished: "npm:~2.4.1" + parseurl: "npm:~1.3.3" + path-to-regexp: "npm:~0.1.12" + proxy-addr: "npm:~2.0.7" + qs: "npm:~6.15.1" + range-parser: "npm:~1.2.1" + safe-buffer: "npm:5.2.1" + send: "npm:~0.19.0" + serve-static: "npm:~1.16.2" + setprototypeof: "npm:1.2.0" + statuses: "npm:~2.0.1" + type-is: "npm:~1.6.18" + utils-merge: "npm:1.0.1" + vary: "npm:~1.1.2" + checksum: 10c0/d06dd4379fd217440b30f8abbe45f0e74931114c1395034f03e7d635196ecdab530d4835a1962a6aa34838d61967dc6f1f77846999bba3032373e9e714222c44 + languageName: node + linkType: hard + +"exsolve@npm:^1.0.8": + version: 1.1.0 + resolution: "exsolve@npm:1.1.0" + checksum: 10c0/38ecec58d6e4ddfe31b5e6773a0193057fcb8462e1c0959aee3c9441729b4db901f3b23af68963cb871fb3f83b46c49ed2f69e0ef6d5254951f483c68cc934e9 + languageName: node + linkType: hard + +"extend@npm:^3.0.2": + version: 3.0.2 + resolution: "extend@npm:3.0.2" + checksum: 10c0/73bf6e27406e80aa3e85b0d1c4fd987261e628064e170ca781125c0b635a3dabad5e05adbf07595ea0cf1e6c5396cacb214af933da7cbaf24fe75ff14818e8f9 + languageName: node + linkType: hard + +"fast-glob@npm:^3.3.2": + version: 3.3.3 + resolution: "fast-glob@npm:3.3.3" + dependencies: + "@nodelib/fs.stat": "npm:^2.0.2" + "@nodelib/fs.walk": "npm:^1.2.3" + glob-parent: "npm:^5.1.2" + merge2: "npm:^1.3.0" + micromatch: "npm:^4.0.8" + checksum: 10c0/f6aaa141d0d3384cf73cbcdfc52f475ed293f6d5b65bfc5def368b09163a9f7e5ec2b3014d80f733c405f58e470ee0cc451c2937685045cddcdeaa24199c43fe + languageName: node + linkType: hard + +"fastq@npm:^1.6.0": + version: 1.20.1 + resolution: "fastq@npm:1.20.1" + dependencies: + reusify: "npm:^1.0.4" + checksum: 10c0/e5dd725884decb1f11e5c822221d76136f239d0236f176fab80b7b8f9e7619ae57e6b4e5b73defc21e6b9ef99437ee7b545cff8e6c2c337819633712fa9d352e + languageName: node + linkType: hard + +"fdir@npm:^6.5.0": + version: 6.5.0 + resolution: "fdir@npm:6.5.0" + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + checksum: 10c0/e345083c4306b3aed6cb8ec551e26c36bab5c511e99ea4576a16750ddc8d3240e63826cc624f5ae17ad4dc82e68a253213b60d556c11bfad064b7607847ed07f + languageName: node + linkType: hard + +"fetch-blob@npm:^3.1.2, fetch-blob@npm:^3.1.4": + version: 3.2.0 + resolution: "fetch-blob@npm:3.2.0" + dependencies: + node-domexception: "npm:^1.0.0" + web-streams-polyfill: "npm:^3.0.3" + checksum: 10c0/60054bf47bfa10fb0ba6cb7742acec2f37c1f56344f79a70bb8b1c48d77675927c720ff3191fa546410a0442c998d27ab05e9144c32d530d8a52fbe68f843b69 + languageName: node + linkType: hard + +"fill-range@npm:^7.1.1": + version: 7.1.1 + resolution: "fill-range@npm:7.1.1" + dependencies: + to-regex-range: "npm:^5.0.1" + checksum: 10c0/b75b691bbe065472f38824f694c2f7449d7f5004aa950426a2c28f0306c60db9b880c0b0e4ed819997ffb882d1da02cfcfc819bddc94d71627f5269682edf018 + languageName: node + linkType: hard + +"finalhandler@npm:~1.3.1": + version: 1.3.2 + resolution: "finalhandler@npm:1.3.2" + dependencies: + debug: "npm:2.6.9" + encodeurl: "npm:~2.0.0" + escape-html: "npm:~1.0.3" + on-finished: "npm:~2.4.1" + parseurl: "npm:~1.3.3" + statuses: "npm:~2.0.2" + unpipe: "npm:~1.0.0" + checksum: 10c0/435a4fd65e4e4e4c71bb5474980090b73c353a123dd415583f67836bdd6516e528cf07298e219a82b94631dee7830eae5eece38d3c178073cf7df4e8c182f413 + languageName: node + linkType: hard + +"for-each@npm:^0.3.3, for-each@npm:^0.3.5": + version: 0.3.5 + resolution: "for-each@npm:0.3.5" + dependencies: + is-callable: "npm:^1.2.7" + checksum: 10c0/0e0b50f6a843a282637d43674d1fb278dda1dd85f4f99b640024cfb10b85058aac0cc781bf689d5fe50b4b7f638e91e548560723a4e76e04fe96ae35ef039cee + languageName: node + linkType: hard + +"formdata-polyfill@npm:^4.0.10": + version: 4.0.10 + resolution: "formdata-polyfill@npm:4.0.10" + dependencies: + fetch-blob: "npm:^3.1.2" + checksum: 10c0/5392ec484f9ce0d5e0d52fb5a78e7486637d516179b0eb84d81389d7eccf9ca2f663079da56f761355c0a65792810e3b345dc24db9a8bbbcf24ef3c8c88570c6 + languageName: node + linkType: hard + +"forwarded@npm:0.2.0": + version: 0.2.0 + resolution: "forwarded@npm:0.2.0" + checksum: 10c0/9b67c3fac86acdbc9ae47ba1ddd5f2f81526fa4c8226863ede5600a3f7c7416ef451f6f1e240a3cc32d0fd79fcfe6beb08fd0da454f360032bde70bf80afbb33 + languageName: node + linkType: hard + +"fraction.js@npm:^5.3.4": + version: 5.3.4 + resolution: "fraction.js@npm:5.3.4" + checksum: 10c0/f90079fe9bfc665e0a07079938e8ff71115bce9462f17b32fc283f163b0540ec34dc33df8ed41bb56f028316b04361b9a9995b9ee9258617f8338e0b05c5f95a + languageName: node + linkType: hard + +"fresh@npm:~0.5.2": + version: 0.5.2 + resolution: "fresh@npm:0.5.2" + checksum: 10c0/c6d27f3ed86cc5b601404822f31c900dd165ba63fff8152a3ef714e2012e7535027063bc67ded4cb5b3a49fa596495d46cacd9f47d6328459cf570f08b7d9e5a + languageName: node + linkType: hard + +"fsevents@npm:~2.3.2, fsevents@npm:~2.3.3": + version: 2.3.3 + resolution: "fsevents@npm:2.3.3" + dependencies: + node-gyp: "npm:latest" + checksum: 10c0/a1f0c44595123ed717febbc478aa952e47adfc28e2092be66b8ab1635147254ca6cfe1df792a8997f22716d4cbafc73309899ff7bfac2ac3ad8cf2e4ecc3ec60 + conditions: os=darwin + languageName: node + linkType: hard + +"fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.3#optional!builtin": + version: 2.3.3 + resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1" + dependencies: + node-gyp: "npm:latest" + conditions: os=darwin + languageName: node + linkType: hard + +"function-bind@npm:^1.1.2": + version: 1.1.2 + resolution: "function-bind@npm:1.1.2" + checksum: 10c0/d8680ee1e5fcd4c197e4ac33b2b4dce03c71f4d91717292785703db200f5c21f977c568d28061226f9b5900cbcd2c84463646134fd5337e7925e0942bc3f46d5 + languageName: node + linkType: hard + +"function.prototype.name@npm:^1.1.6, function.prototype.name@npm:^1.1.8": + version: 1.2.0 + resolution: "function.prototype.name@npm:1.2.0" + dependencies: + call-bind: "npm:^1.0.9" + call-bound: "npm:^1.0.4" + es-define-property: "npm:^1.0.1" + es-errors: "npm:^1.3.0" + functions-have-names: "npm:^1.2.3" + has-property-descriptors: "npm:^1.0.2" + hasown: "npm:^2.0.4" + is-callable: "npm:^1.2.7" + is-document.all: "npm:^1.0.0" + checksum: 10c0/b20e6370ef4f7d56d0bedf5719f6684a517a8dd3334209b4d9f51e8834859302a584187156bf024cda9f50ba2479e4d6764ac34af9532ea47d2f4d9fa6bcf90d + languageName: node + linkType: hard + +"functions-have-names@npm:^1.2.3": + version: 1.2.3 + resolution: "functions-have-names@npm:1.2.3" + checksum: 10c0/33e77fd29bddc2d9bb78ab3eb854c165909201f88c75faa8272e35899e2d35a8a642a15e7420ef945e1f64a9670d6aa3ec744106b2aa42be68ca5114025954ca + languageName: node + linkType: hard + +"gaxios@npm:^7.0.0, gaxios@npm:^7.1.4": + version: 7.2.0 + resolution: "gaxios@npm:7.2.0" + dependencies: + extend: "npm:^3.0.2" + https-proxy-agent: "npm:^7.0.1" + node-fetch: "npm:^3.3.2" + checksum: 10c0/a70ea81446224aa385e235f4d318a431b3cbb246be4489a07c7f2ed55f96ddacbb799863f118f2d402e321ade6f5398dd126695f67f103d2d94c2700cec2a1e8 + languageName: node + linkType: hard + +"gcp-metadata@npm:8.1.2": + version: 8.1.2 + resolution: "gcp-metadata@npm:8.1.2" + dependencies: + gaxios: "npm:^7.0.0" + google-logging-utils: "npm:^1.0.0" + json-bigint: "npm:^1.0.0" + checksum: 10c0/15a61231a9410dc11c2828d2c9fdc8b0a939f1af746195c44edc6f2ffea0acab52cef3a7b9828069a36fd5d68bda730f7328a415fe42a01258f6e249dfba6908 + languageName: node + linkType: hard + +"generator-function@npm:^2.0.0": + version: 2.0.1 + resolution: "generator-function@npm:2.0.1" + checksum: 10c0/8a9f59df0f01cfefafdb3b451b80555e5cf6d76487095db91ac461a0e682e4ff7a9dbce15f4ecec191e53586d59eece01949e05a4b4492879600bbbe8e28d6b8 + languageName: node + linkType: hard + +"gensync@npm:^1.0.0-beta.2": + version: 1.0.0-beta.2 + resolution: "gensync@npm:1.0.0-beta.2" + checksum: 10c0/782aba6cba65b1bb5af3b095d96249d20edbe8df32dbf4696fd49be2583faf676173bf4809386588828e4dd76a3354fcbeb577bab1c833ccd9fc4577f26103f8 + languageName: node + linkType: hard + +"get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6, get-intrinsic@npm:^1.2.7, get-intrinsic@npm:^1.3.0": + version: 1.3.1 + resolution: "get-intrinsic@npm:1.3.1" + dependencies: + async-function: "npm:^1.0.0" + async-generator-function: "npm:^1.0.0" + call-bind-apply-helpers: "npm:^1.0.2" + es-define-property: "npm:^1.0.1" + es-errors: "npm:^1.3.0" + es-object-atoms: "npm:^1.1.1" + function-bind: "npm:^1.1.2" + generator-function: "npm:^2.0.0" + get-proto: "npm:^1.0.1" + gopd: "npm:^1.2.0" + has-symbols: "npm:^1.1.0" + hasown: "npm:^2.0.2" + math-intrinsics: "npm:^1.1.0" + checksum: 10c0/9f4ab0cf7efe0fd2c8185f52e6f637e708f3a112610c88869f8f041bb9ecc2ce44bf285dfdbdc6f4f7c277a5b88d8e94a432374d97cca22f3de7fc63795deb5d + languageName: node + linkType: hard + +"get-port@npm:5.1.1": + version: 5.1.1 + resolution: "get-port@npm:5.1.1" + checksum: 10c0/2873877a469b24e6d5e0be490724a17edb39fafc795d1d662e7bea951ca649713b4a50117a473f9d162312cb0e946597bd0e049ed2f866e79e576e8e213d3d1c + languageName: node + linkType: hard + +"get-proto@npm:^1.0.1": + version: 1.0.1 + resolution: "get-proto@npm:1.0.1" + dependencies: + dunder-proto: "npm:^1.0.1" + es-object-atoms: "npm:^1.0.0" + checksum: 10c0/9224acb44603c5526955e83510b9da41baf6ae73f7398875fba50edc5e944223a89c4a72b070fcd78beb5f7bdda58ecb6294adc28f7acfc0da05f76a2399643c + languageName: node + linkType: hard + +"get-symbol-description@npm:^1.1.0": + version: 1.1.0 + resolution: "get-symbol-description@npm:1.1.0" + dependencies: + call-bound: "npm:^1.0.3" + es-errors: "npm:^1.3.0" + get-intrinsic: "npm:^1.2.6" + checksum: 10c0/d6a7d6afca375779a4b307738c9e80dbf7afc0bdbe5948768d54ab9653c865523d8920e670991a925936eb524b7cb6a6361d199a760b21d0ca7620194455aa4b + languageName: node + linkType: hard + +"get-tsconfig@npm:^4.7.0": + version: 4.14.0 + resolution: "get-tsconfig@npm:4.14.0" + dependencies: + resolve-pkg-maps: "npm:^1.0.0" + checksum: 10c0/abc2b9275468eb589079a0b7a95eb5107c14fdd0ca6dda1bff116fe774ea1f79975421dcb22a0c86b4f820fcc69a7655dddf9b6d6a8a2c06fcb59e19794c0724 + languageName: node + linkType: hard + +"glob-parent@npm:^5.1.2, glob-parent@npm:~5.1.2": + version: 5.1.2 + resolution: "glob-parent@npm:5.1.2" + dependencies: + is-glob: "npm:^4.0.1" + checksum: 10c0/cab87638e2112bee3f839ef5f6e0765057163d39c66be8ec1602f3823da4692297ad4e972de876ea17c44d652978638d2fd583c6713d0eb6591706825020c9ee + languageName: node + linkType: hard + +"glob-parent@npm:^6.0.2": + version: 6.0.2 + resolution: "glob-parent@npm:6.0.2" + dependencies: + is-glob: "npm:^4.0.3" + checksum: 10c0/317034d88654730230b3f43bb7ad4f7c90257a426e872ea0bf157473ac61c99bf5d205fad8f0185f989be8d2fa6d3c7dce1645d99d545b6ea9089c39f838e7f8 + languageName: node + linkType: hard + +"globalthis@npm:^1.0.4": + version: 1.0.4 + resolution: "globalthis@npm:1.0.4" + dependencies: + define-properties: "npm:^1.2.1" + gopd: "npm:^1.0.1" + checksum: 10c0/9d156f313af79d80b1566b93e19285f481c591ad6d0d319b4be5e03750d004dde40a39a0f26f7e635f9007a3600802f53ecd85a759b86f109e80a5f705e01846 + languageName: node + linkType: hard + +"google-auth-library@npm:^10.3.0": + version: 10.9.0 + resolution: "google-auth-library@npm:10.9.0" + dependencies: + base64-js: "npm:^1.3.0" + ecdsa-sig-formatter: "npm:^1.0.11" + gaxios: "npm:^7.1.4" + gcp-metadata: "npm:8.1.2" + google-logging-utils: "npm:1.1.3" + jws: "npm:^4.0.0" + checksum: 10c0/d731d0bff429c63e995a954f4a08ccbcdb159bff322c529c467e89a98de215fff6630ddf94c9a29a1737ab8d820ff5943b7131c51faabae04497f146d62636dc + languageName: node + linkType: hard + +"google-logging-utils@npm:1.1.3": + version: 1.1.3 + resolution: "google-logging-utils@npm:1.1.3" + checksum: 10c0/e65201c7e96543bd1423b9324013736646b9eed60941e0bfa47b9bfd146d2f09cf3df1c99ca60b7d80a726075263ead049ee72de53372cb8458c3bc55c2c1e59 + languageName: node + linkType: hard + +"google-logging-utils@npm:^1.0.0": + version: 1.1.4 + resolution: "google-logging-utils@npm:1.1.4" + checksum: 10c0/860873974dd31678553f1074eb8fcf49c6417807086f4645ee8d4eaa81e8dce39f8f7a4b6856be4fda6e5d812b2df10e7abeb6dfe28353d28724cf81357c5a53 + languageName: node + linkType: hard + +"gopd@npm:^1.0.1, gopd@npm:^1.2.0": + version: 1.2.0 + resolution: "gopd@npm:1.2.0" + checksum: 10c0/50fff1e04ba2b7737c097358534eacadad1e68d24cccee3272e04e007bed008e68d2614f3987788428fd192a5ae3889d08fb2331417e4fc4a9ab366b2043cead + languageName: node + linkType: hard + +"graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.2.6": + version: 4.2.11 + resolution: "graceful-fs@npm:4.2.11" + checksum: 10c0/386d011a553e02bc594ac2ca0bd6d9e4c22d7fa8cfbfc448a6d148c59ea881b092db9dbe3547ae4b88e55f1b01f7c4a2ecc53b310c042793e63aa44cf6c257f2 + languageName: node + linkType: hard + +"has-bigints@npm:^1.0.2": + version: 1.1.0 + resolution: "has-bigints@npm:1.1.0" + checksum: 10c0/2de0cdc4a1ccf7a1e75ffede1876994525ac03cc6f5ae7392d3415dd475cd9eee5bceec63669ab61aa997ff6cceebb50ef75561c7002bed8988de2b9d1b40788 + languageName: node + linkType: hard + +"has-flag@npm:^3.0.0": + version: 3.0.0 + resolution: "has-flag@npm:3.0.0" + checksum: 10c0/1c6c83b14b8b1b3c25b0727b8ba3e3b647f99e9e6e13eb7322107261de07a4c1be56fc0d45678fc376e09772a3a1642ccdaf8fc69bdf123b6c086598397ce473 + languageName: node + linkType: hard + +"has-property-descriptors@npm:^1.0.0, has-property-descriptors@npm:^1.0.2": + version: 1.0.2 + resolution: "has-property-descriptors@npm:1.0.2" + dependencies: + es-define-property: "npm:^1.0.0" + checksum: 10c0/253c1f59e80bb476cf0dde8ff5284505d90c3bdb762983c3514d36414290475fe3fd6f574929d84de2a8eec00d35cf07cb6776205ff32efd7c50719125f00236 + languageName: node + linkType: hard + +"has-proto@npm:^1.2.0": + version: 1.2.0 + resolution: "has-proto@npm:1.2.0" + dependencies: + dunder-proto: "npm:^1.0.0" + checksum: 10c0/46538dddab297ec2f43923c3d35237df45d8c55a6fc1067031e04c13ed8a9a8f94954460632fd4da84c31a1721eefee16d901cbb1ae9602bab93bb6e08f93b95 + languageName: node + linkType: hard + +"has-symbols@npm:^1.0.3, has-symbols@npm:^1.1.0": + version: 1.1.0 + resolution: "has-symbols@npm:1.1.0" + checksum: 10c0/dde0a734b17ae51e84b10986e651c664379018d10b91b6b0e9b293eddb32f0f069688c841fb40f19e9611546130153e0a2a48fd7f512891fb000ddfa36f5a20e + languageName: node + linkType: hard + +"has-tostringtag@npm:^1.0.2": + version: 1.0.2 + resolution: "has-tostringtag@npm:1.0.2" + dependencies: + has-symbols: "npm:^1.0.3" + checksum: 10c0/a8b166462192bafe3d9b6e420a1d581d93dd867adb61be223a17a8d6dad147aa77a8be32c961bb2f27b3ef893cae8d36f564ab651f5e9b7938ae86f74027c48c + languageName: node + linkType: hard + +"hasown@npm:^2.0.2, hasown@npm:^2.0.3, hasown@npm:^2.0.4": + version: 2.0.4 + resolution: "hasown@npm:2.0.4" + dependencies: + function-bind: "npm:^1.1.2" + checksum: 10c0/2d8de939e270b70618f8cebb69746620db10617dbb495bc66ddad326955ea24d3ca4af133aff3eb7c1853e0218f867bc2b050ec26fe02e3aea58f880ffc5e506 + languageName: node + linkType: hard + +"helmdocs-grant-outreach-engine@workspace:.": + version: 0.0.0-use.local + resolution: "helmdocs-grant-outreach-engine@workspace:." + dependencies: + prettier: "npm:^3.6.2" + prettier-plugin-organize-imports: "npm:^4.3.0" + turbo: "npm:^2.1.2" + typescript: "npm:^5.9.3" + languageName: unknown + linkType: soft + +"hosted-git-info@npm:^2.1.4": + version: 2.8.9 + resolution: "hosted-git-info@npm:2.8.9" + checksum: 10c0/317cbc6b1bbbe23c2a40ae23f3dafe9fa349ce42a89a36f930e3f9c0530c179a3882d2ef1e4141a4c3674d6faaea862138ec55b43ad6f75e387fda2483a13c70 + languageName: node + linkType: hard + +"http-errors@npm:~2.0.0, http-errors@npm:~2.0.1": + version: 2.0.1 + resolution: "http-errors@npm:2.0.1" + dependencies: + depd: "npm:~2.0.0" + inherits: "npm:~2.0.4" + setprototypeof: "npm:~1.2.0" + statuses: "npm:~2.0.2" + toidentifier: "npm:~1.0.1" + checksum: 10c0/fb38906cef4f5c83952d97661fe14dc156cb59fe54812a42cd448fa57b5c5dfcb38a40a916957737bd6b87aab257c0648d63eb5b6a9ca9f548e105b6072712d4 + languageName: node + linkType: hard + +"https-proxy-agent@npm:^7.0.1": + version: 7.0.6 + resolution: "https-proxy-agent@npm:7.0.6" + dependencies: + agent-base: "npm:^7.1.2" + debug: "npm:4" + checksum: 10c0/f729219bc735edb621fa30e6e84e60ee5d00802b8247aac0d7b79b0bd6d4b3294737a337b93b86a0bd9e68099d031858a39260c976dc14cdbba238ba1f8779ac + languageName: node + linkType: hard + +"iconv-lite@npm:~0.4.24": + version: 0.4.24 + resolution: "iconv-lite@npm:0.4.24" + dependencies: + safer-buffer: "npm:>= 2.1.2 < 3" + checksum: 10c0/c6886a24cc00f2a059767440ec1bc00d334a89f250db8e0f7feb4961c8727118457e27c495ba94d082e51d3baca378726cd110aaf7ded8b9bbfd6a44760cf1d4 + languageName: node + linkType: hard + +"inherits@npm:~2.0.4": + version: 2.0.4 + resolution: "inherits@npm:2.0.4" + checksum: 10c0/4e531f648b29039fb7426fb94075e6545faa1eb9fe83c29f0b6d9e7263aceb4289d2d4557db0d428188eeb449cc7c5e77b0a0b2c4e248ff2a65933a0dee49ef2 + languageName: node + linkType: hard + +"internal-slot@npm:^1.1.0": + version: 1.1.0 + resolution: "internal-slot@npm:1.1.0" + dependencies: + es-errors: "npm:^1.3.0" + hasown: "npm:^2.0.2" + side-channel: "npm:^1.1.0" + checksum: 10c0/03966f5e259b009a9bf1a78d60da920df198af4318ec004f57b8aef1dd3fe377fbc8cce63a96e8c810010302654de89f9e19de1cd8ad0061d15be28a695465c7 + languageName: node + linkType: hard + +"ipaddr.js@npm:1.9.1": + version: 1.9.1 + resolution: "ipaddr.js@npm:1.9.1" + checksum: 10c0/0486e775047971d3fdb5fb4f063829bac45af299ae0b82dcf3afa2145338e08290563a2a70f34b732d795ecc8311902e541a8530eeb30d75860a78ff4e94ce2a + languageName: node + linkType: hard + +"is-array-buffer@npm:^3.0.4, is-array-buffer@npm:^3.0.5": + version: 3.0.5 + resolution: "is-array-buffer@npm:3.0.5" + dependencies: + call-bind: "npm:^1.0.8" + call-bound: "npm:^1.0.3" + get-intrinsic: "npm:^1.2.6" + checksum: 10c0/c5c9f25606e86dbb12e756694afbbff64bc8b348d1bc989324c037e1068695131930199d6ad381952715dad3a9569333817f0b1a72ce5af7f883ce802e49c83d + languageName: node + linkType: hard + +"is-arrayish@npm:^0.2.1": + version: 0.2.1 + resolution: "is-arrayish@npm:0.2.1" + checksum: 10c0/e7fb686a739068bb70f860b39b67afc62acc62e36bb61c5f965768abce1873b379c563e61dd2adad96ebb7edf6651111b385e490cf508378959b0ed4cac4e729 + languageName: node + linkType: hard + +"is-async-function@npm:^2.0.0": + version: 2.1.1 + resolution: "is-async-function@npm:2.1.1" + dependencies: + async-function: "npm:^1.0.0" + call-bound: "npm:^1.0.3" + get-proto: "npm:^1.0.1" + has-tostringtag: "npm:^1.0.2" + safe-regex-test: "npm:^1.1.0" + checksum: 10c0/d70c236a5e82de6fc4d44368ffd0c2fee2b088b893511ce21e679da275a5ecc6015ff59a7d7e1bdd7ca39f71a8dbdd253cf8cce5c6b3c91cdd5b42b5ce677298 + languageName: node + linkType: hard + +"is-bigint@npm:^1.1.0": + version: 1.1.0 + resolution: "is-bigint@npm:1.1.0" + dependencies: + has-bigints: "npm:^1.0.2" + checksum: 10c0/f4f4b905ceb195be90a6ea7f34323bf1c18e3793f18922e3e9a73c684c29eeeeff5175605c3a3a74cc38185fe27758f07efba3dbae812e5c5afbc0d2316b40e4 + languageName: node + linkType: hard + +"is-binary-path@npm:~2.1.0": + version: 2.1.0 + resolution: "is-binary-path@npm:2.1.0" + dependencies: + binary-extensions: "npm:^2.0.0" + checksum: 10c0/a16eaee59ae2b315ba36fad5c5dcaf8e49c3e27318f8ab8fa3cdb8772bf559c8d1ba750a589c2ccb096113bb64497084361a25960899cb6172a6925ab6123d38 + languageName: node + linkType: hard + +"is-boolean-object@npm:^1.2.1": + version: 1.2.2 + resolution: "is-boolean-object@npm:1.2.2" + dependencies: + call-bound: "npm:^1.0.3" + has-tostringtag: "npm:^1.0.2" + checksum: 10c0/36ff6baf6bd18b3130186990026f5a95c709345c39cd368468e6c1b6ab52201e9fd26d8e1f4c066357b4938b0f0401e1a5000e08257787c1a02f3a719457001e + languageName: node + linkType: hard + +"is-callable@npm:^1.2.7": + version: 1.2.7 + resolution: "is-callable@npm:1.2.7" + checksum: 10c0/ceebaeb9d92e8adee604076971dd6000d38d6afc40bb843ea8e45c5579b57671c3f3b50d7f04869618242c6cee08d1b67806a8cb8edaaaf7c0748b3720d6066f + languageName: node + linkType: hard + +"is-core-module@npm:^2.16.1": + version: 2.16.2 + resolution: "is-core-module@npm:2.16.2" + dependencies: + hasown: "npm:^2.0.3" + checksum: 10c0/14b4258390283709c15476d023ec173e27458d5d014ccdb8ed39d576e551c3fa45498b7c9fe178f1529c4cb2648ddd58852a6a62107a019f6e349529f277518a + languageName: node + linkType: hard + +"is-data-view@npm:^1.0.1, is-data-view@npm:^1.0.2": + version: 1.0.2 + resolution: "is-data-view@npm:1.0.2" + dependencies: + call-bound: "npm:^1.0.2" + get-intrinsic: "npm:^1.2.6" + is-typed-array: "npm:^1.1.13" + checksum: 10c0/ef3548a99d7e7f1370ce21006baca6d40c73e9f15c941f89f0049c79714c873d03b02dae1c64b3f861f55163ecc16da06506c5b8a1d4f16650b3d9351c380153 + languageName: node + linkType: hard + +"is-date-object@npm:^1.1.0": + version: 1.1.0 + resolution: "is-date-object@npm:1.1.0" + dependencies: + call-bound: "npm:^1.0.2" + has-tostringtag: "npm:^1.0.2" + checksum: 10c0/1a4d199c8e9e9cac5128d32e6626fa7805175af9df015620ac0d5d45854ccf348ba494679d872d37301032e35a54fc7978fba1687e8721b2139aea7870cafa2f + languageName: node + linkType: hard + +"is-document.all@npm:^1.0.0": + version: 1.0.0 + resolution: "is-document.all@npm:1.0.0" + dependencies: + call-bound: "npm:^1.0.4" + checksum: 10c0/955c20ed5bf01d49da8243b4c714947a6ff64b6d9ba0e12bdbfa654a3e7c47f72cc01c6cd2905e85512d02bc3a1290edd73857bca8842566ff9dcfb7c3f92dae + languageName: node + linkType: hard + +"is-extglob@npm:^2.1.1": + version: 2.1.1 + resolution: "is-extglob@npm:2.1.1" + checksum: 10c0/5487da35691fbc339700bbb2730430b07777a3c21b9ebaecb3072512dfd7b4ba78ac2381a87e8d78d20ea08affb3f1971b4af629173a6bf435ff8a4c47747912 + languageName: node + linkType: hard + +"is-finalizationregistry@npm:^1.1.0": + version: 1.1.1 + resolution: "is-finalizationregistry@npm:1.1.1" + dependencies: + call-bound: "npm:^1.0.3" + checksum: 10c0/818dff679b64f19e228a8205a1e2d09989a98e98def3a817f889208cfcbf918d321b251aadf2c05918194803ebd2eb01b14fc9d0b2bea53d984f4137bfca5e97 + languageName: node + linkType: hard + +"is-generator-function@npm:^1.0.10": + version: 1.1.2 + resolution: "is-generator-function@npm:1.1.2" + dependencies: + call-bound: "npm:^1.0.4" + generator-function: "npm:^2.0.0" + get-proto: "npm:^1.0.1" + has-tostringtag: "npm:^1.0.2" + safe-regex-test: "npm:^1.1.0" + checksum: 10c0/83da102e89c3e3b71d67b51d47c9f9bc862bceb58f87201727e27f7fa19d1d90b0ab223644ecaee6fc6e3d2d622bb25c966fbdaf87c59158b01ce7c0fe2fa372 + languageName: node + linkType: hard + +"is-glob@npm:^4.0.1, is-glob@npm:^4.0.3, is-glob@npm:~4.0.1": + version: 4.0.3 + resolution: "is-glob@npm:4.0.3" + dependencies: + is-extglob: "npm:^2.1.1" + checksum: 10c0/17fb4014e22be3bbecea9b2e3a76e9e34ff645466be702f1693e8f1ee1adac84710d0be0bd9f967d6354036fd51ab7c2741d954d6e91dae6bb69714de92c197a + languageName: node + linkType: hard + +"is-map@npm:^2.0.3": + version: 2.0.3 + resolution: "is-map@npm:2.0.3" + checksum: 10c0/2c4d431b74e00fdda7162cd8e4b763d6f6f217edf97d4f8538b94b8702b150610e2c64961340015fe8df5b1fcee33ccd2e9b62619c4a8a3a155f8de6d6d355fc + languageName: node + linkType: hard + +"is-negative-zero@npm:^2.0.3": + version: 2.0.3 + resolution: "is-negative-zero@npm:2.0.3" + checksum: 10c0/bcdcf6b8b9714063ffcfa9929c575ac69bfdabb8f4574ff557dfc086df2836cf07e3906f5bbc4f2a5c12f8f3ba56af640c843cdfc74da8caed86c7c7d66fd08e + languageName: node + linkType: hard + +"is-number-object@npm:^1.1.1": + version: 1.1.1 + resolution: "is-number-object@npm:1.1.1" + dependencies: + call-bound: "npm:^1.0.3" + has-tostringtag: "npm:^1.0.2" + checksum: 10c0/97b451b41f25135ff021d85c436ff0100d84a039bb87ffd799cbcdbea81ef30c464ced38258cdd34f080be08fc3b076ca1f472086286d2aa43521d6ec6a79f53 + languageName: node + linkType: hard + +"is-number@npm:^7.0.0": + version: 7.0.0 + resolution: "is-number@npm:7.0.0" + checksum: 10c0/b4686d0d3053146095ccd45346461bc8e53b80aeb7671cc52a4de02dbbf7dc0d1d2a986e2fe4ae206984b4d34ef37e8b795ebc4f4295c978373e6575e295d811 + languageName: node + linkType: hard + +"is-regex@npm:^1.2.1": + version: 1.2.1 + resolution: "is-regex@npm:1.2.1" + dependencies: + call-bound: "npm:^1.0.2" + gopd: "npm:^1.2.0" + has-tostringtag: "npm:^1.0.2" + hasown: "npm:^2.0.2" + checksum: 10c0/1d3715d2b7889932349241680032e85d0b492cfcb045acb75ffc2c3085e8d561184f1f7e84b6f8321935b4aea39bc9c6ba74ed595b57ce4881a51dfdbc214e04 + languageName: node + linkType: hard + +"is-set@npm:^2.0.3": + version: 2.0.3 + resolution: "is-set@npm:2.0.3" + checksum: 10c0/f73732e13f099b2dc879c2a12341cfc22ccaca8dd504e6edae26484bd5707a35d503fba5b4daad530a9b088ced1ae6c9d8200fd92e09b428fe14ea79ce8080b7 + languageName: node + linkType: hard + +"is-shared-array-buffer@npm:^1.0.4": + version: 1.0.4 + resolution: "is-shared-array-buffer@npm:1.0.4" + dependencies: + call-bound: "npm:^1.0.3" + checksum: 10c0/65158c2feb41ff1edd6bbd6fd8403a69861cf273ff36077982b5d4d68e1d59278c71691216a4a64632bd76d4792d4d1d2553901b6666d84ade13bba5ea7bc7db + languageName: node + linkType: hard + +"is-string@npm:^1.1.1": + version: 1.1.1 + resolution: "is-string@npm:1.1.1" + dependencies: + call-bound: "npm:^1.0.3" + has-tostringtag: "npm:^1.0.2" + checksum: 10c0/2f518b4e47886bb81567faba6ffd0d8a8333cf84336e2e78bf160693972e32ad00fe84b0926491cc598dee576fdc55642c92e62d0cbe96bf36f643b6f956f94d + languageName: node + linkType: hard + +"is-symbol@npm:^1.1.1": + version: 1.1.1 + resolution: "is-symbol@npm:1.1.1" + dependencies: + call-bound: "npm:^1.0.2" + has-symbols: "npm:^1.1.0" + safe-regex-test: "npm:^1.1.0" + checksum: 10c0/f08f3e255c12442e833f75a9e2b84b2d4882fdfd920513cf2a4a2324f0a5b076c8fd913778e3ea5d258d5183e9d92c0cd20e04b03ab3df05316b049b2670af1e + languageName: node + linkType: hard + +"is-typed-array@npm:^1.1.13, is-typed-array@npm:^1.1.14, is-typed-array@npm:^1.1.15": + version: 1.1.15 + resolution: "is-typed-array@npm:1.1.15" + dependencies: + which-typed-array: "npm:^1.1.16" + checksum: 10c0/415511da3669e36e002820584e264997ffe277ff136643a3126cc949197e6ca3334d0f12d084e83b1994af2e9c8141275c741cf2b7da5a2ff62dd0cac26f76c4 + languageName: node + linkType: hard + +"is-weakmap@npm:^2.0.2": + version: 2.0.2 + resolution: "is-weakmap@npm:2.0.2" + checksum: 10c0/443c35bb86d5e6cc5929cd9c75a4024bb0fff9586ed50b092f94e700b89c43a33b186b76dbc6d54f3d3d09ece689ab38dcdc1af6a482cbe79c0f2da0a17f1299 + languageName: node + linkType: hard + +"is-weakref@npm:^1.0.2, is-weakref@npm:^1.1.1": + version: 1.1.1 + resolution: "is-weakref@npm:1.1.1" + dependencies: + call-bound: "npm:^1.0.3" + checksum: 10c0/8e0a9c07b0c780949a100e2cab2b5560a48ecd4c61726923c1a9b77b6ab0aa0046c9e7fb2206042296817045376dee2c8ab1dabe08c7c3dfbf195b01275a085b + languageName: node + linkType: hard + +"is-weakset@npm:^2.0.3": + version: 2.0.4 + resolution: "is-weakset@npm:2.0.4" + dependencies: + call-bound: "npm:^1.0.3" + get-intrinsic: "npm:^1.2.6" + checksum: 10c0/6491eba08acb8dc9532da23cb226b7d0192ede0b88f16199e592e4769db0a077119c1f5d2283d1e0d16d739115f70046e887e477eb0e66cd90e1bb29f28ba647 + languageName: node + linkType: hard + +"is-what@npm:^4.1.8": + version: 4.1.16 + resolution: "is-what@npm:4.1.16" + checksum: 10c0/611f1947776826dcf85b57cfb7bd3b3ea6f4b94a9c2f551d4a53f653cf0cb9d1e6518846648256d46ee6c91d114b6d09d2ac8a07306f7430c5900f87466aae5b + languageName: node + linkType: hard + +"isarray@npm:^2.0.5": + version: 2.0.5 + resolution: "isarray@npm:2.0.5" + checksum: 10c0/4199f14a7a13da2177c66c31080008b7124331956f47bca57dd0b6ea9f11687aa25e565a2c7a2b519bc86988d10398e3049a1f5df13c9f6b7664154690ae79fd + languageName: node + linkType: hard + +"isbot@npm:4": + version: 4.4.0 + resolution: "isbot@npm:4.4.0" + checksum: 10c0/8cccbd8151a859c4a6c46578aa25984a5cad90ff91b375ab644c20f0a2bf02defa5429739b2de94f68b045d37766837e12991d858ad7cdf5f01e0071d4342e49 + languageName: node + linkType: hard + +"isbot@npm:^5.1.11": + version: 5.2.1 + resolution: "isbot@npm:5.2.1" + checksum: 10c0/83bf2852897694f28e46d742dc9a9e722fb650a7412d9288f4eab17247a706d242e98b5794caf93804d6566df54fbe4be9e38cc2604dba04a5018d69a6abe252 + languageName: node + linkType: hard + +"isexe@npm:^2.0.0": + version: 2.0.0 + resolution: "isexe@npm:2.0.0" + checksum: 10c0/228cfa503fadc2c31596ab06ed6aa82c9976eec2bfd83397e7eaf06d0ccf42cd1dfd6743bf9aeb01aebd4156d009994c5f76ea898d2832c1fe342da923ca457d + languageName: node + linkType: hard + +"isexe@npm:^4.0.0": + version: 4.0.0 + resolution: "isexe@npm:4.0.0" + checksum: 10c0/5884815115bceac452877659a9c7726382531592f43dc29e5d48b7c4100661aed54018cb90bd36cb2eaeba521092570769167acbb95c18d39afdccbcca06c5ce + languageName: node + linkType: hard + +"jiti@npm:^1.21.7": + version: 1.21.7 + resolution: "jiti@npm:1.21.7" + bin: + jiti: bin/jiti.js + checksum: 10c0/77b61989c758ff32407cdae8ddc77f85e18e1a13fc4977110dbd2e05fc761842f5f71bce684d9a01316e1c4263971315a111385759951080bbfe17cbb5de8f7a + languageName: node + linkType: hard + +"js-tokens@npm:^4.0.0": + version: 4.0.0 + resolution: "js-tokens@npm:4.0.0" + checksum: 10c0/e248708d377aa058eacf2037b07ded847790e6de892bbad3dac0abba2e759cb9f121b00099a65195616badcb6eca8d14d975cb3e89eb1cfda644756402c8aeed + languageName: node + linkType: hard + +"js-tokens@npm:^9.0.1": + version: 9.0.1 + resolution: "js-tokens@npm:9.0.1" + checksum: 10c0/68dcab8f233dde211a6b5fd98079783cbcd04b53617c1250e3553ee16ab3e6134f5e65478e41d82f6d351a052a63d71024553933808570f04dbf828d7921e80e + languageName: node + linkType: hard + +"jsesc@npm:3.0.2": + version: 3.0.2 + resolution: "jsesc@npm:3.0.2" + bin: + jsesc: bin/jsesc + checksum: 10c0/ef22148f9e793180b14d8a145ee6f9f60f301abf443288117b4b6c53d0ecd58354898dc506ccbb553a5f7827965cd38bc5fb726575aae93c5e8915e2de8290e1 + languageName: node + linkType: hard + +"jsesc@npm:^3.0.2": + version: 3.1.0 + resolution: "jsesc@npm:3.1.0" + bin: + jsesc: bin/jsesc + checksum: 10c0/531779df5ec94f47e462da26b4cbf05eb88a83d9f08aac2ba04206508fc598527a153d08bd462bae82fc78b3eaa1a908e1a4a79f886e9238641c4cdefaf118b1 + languageName: node + linkType: hard + +"json-bigint@npm:^1.0.0": + version: 1.0.0 + resolution: "json-bigint@npm:1.0.0" + dependencies: + bignumber.js: "npm:^9.0.0" + checksum: 10c0/e3f34e43be3284b573ea150a3890c92f06d54d8ded72894556357946aeed9877fd795f62f37fe16509af189fd314ab1104d0fd0f163746ad231b9f378f5b33f4 + languageName: node + linkType: hard + +"json-parse-better-errors@npm:^1.0.1": + version: 1.0.2 + resolution: "json-parse-better-errors@npm:1.0.2" + checksum: 10c0/2f1287a7c833e397c9ddd361a78638e828fc523038bb3441fd4fc144cfd2c6cd4963ffb9e207e648cf7b692600f1e1e524e965c32df5152120910e4903a47dcb + languageName: node + linkType: hard + +"json5@npm:^2.2.3": + version: 2.2.3 + resolution: "json5@npm:2.2.3" + bin: + json5: lib/cli.js + checksum: 10c0/5a04eed94810fa55c5ea138b2f7a5c12b97c3750bc63d11e511dcecbfef758003861522a070c2272764ee0f4e3e323862f386945aeb5b85b87ee43f084ba586c + languageName: node + linkType: hard + +"jwa@npm:^2.0.1": + version: 2.0.1 + resolution: "jwa@npm:2.0.1" + dependencies: + buffer-equal-constant-time: "npm:^1.0.1" + ecdsa-sig-formatter: "npm:1.0.11" + safe-buffer: "npm:^5.0.1" + checksum: 10c0/ab3ebc6598e10dc11419d4ed675c9ca714a387481466b10e8a6f3f65d8d9c9237e2826f2505280a739cf4cbcf511cb288eeec22b5c9c63286fc5a2e4f97e78cf + languageName: node + linkType: hard + +"jws@npm:^4.0.0": + version: 4.0.1 + resolution: "jws@npm:4.0.1" + dependencies: + jwa: "npm:^2.0.1" + safe-buffer: "npm:^5.0.1" + checksum: 10c0/6be1ed93023aef570ccc5ea8d162b065840f3ef12f0d1bb3114cade844de7a357d5dc558201d9a65101e70885a6fa56b17462f520e6b0d426195510618a154d0 + languageName: node + linkType: hard + +"lilconfig@npm:^3.1.1, lilconfig@npm:^3.1.3": + version: 3.1.3 + resolution: "lilconfig@npm:3.1.3" + checksum: 10c0/f5604e7240c5c275743561442fbc5abf2a84ad94da0f5adc71d25e31fa8483048de3dcedcb7a44112a942fed305fd75841cdf6c9681c7f640c63f1049e9a5dcc + languageName: node + linkType: hard + +"lines-and-columns@npm:^1.1.6": + version: 1.2.4 + resolution: "lines-and-columns@npm:1.2.4" + checksum: 10c0/3da6ee62d4cd9f03f5dc90b4df2540fb85b352081bee77fe4bbcd12c9000ead7f35e0a38b8d09a9bb99b13223446dd8689ff3c4959807620726d788701a83d2d + languageName: node + linkType: hard + +"load-json-file@npm:^4.0.0": + version: 4.0.0 + resolution: "load-json-file@npm:4.0.0" + dependencies: + graceful-fs: "npm:^4.1.2" + parse-json: "npm:^4.0.0" + pify: "npm:^3.0.0" + strip-bom: "npm:^3.0.0" + checksum: 10c0/6b48f6a0256bdfcc8970be2c57f68f10acb2ee7e63709b386b2febb6ad3c86198f840889cdbe71d28f741cbaa2f23a7771206b138cd1bdd159564511ca37c1d5 + languageName: node + linkType: hard + +"lodash@npm:^4.17.21": + version: 4.18.1 + resolution: "lodash@npm:4.18.1" + checksum: 10c0/757228fc68805c59789e82185135cf85f05d0b2d3d54631d680ca79ec21944ec8314d4533639a14b8bcfbd97a517e78960933041a5af17ecb693ec6eecb99a27 + languageName: node + linkType: hard + +"long@npm:^5.3.2": + version: 5.3.2 + resolution: "long@npm:5.3.2" + checksum: 10c0/7130fe1cbce2dca06734b35b70d380ca3f70271c7f8852c922a7c62c86c4e35f0c39290565eca7133c625908d40e126ac57c02b1b1a4636b9457d77e1e60b981 + languageName: node + linkType: hard + +"loupe@npm:^3.1.0, loupe@npm:^3.1.4": + version: 3.2.1 + resolution: "loupe@npm:3.2.1" + checksum: 10c0/910c872cba291309664c2d094368d31a68907b6f5913e989d301b5c25f30e97d76d77f23ab3bf3b46d0f601ff0b6af8810c10c31b91d2c6b2f132809ca2cc705 + languageName: node + linkType: hard + +"lru-cache@npm:^5.1.1": + version: 5.1.1 + resolution: "lru-cache@npm:5.1.1" + dependencies: + yallist: "npm:^3.0.2" + checksum: 10c0/89b2ef2ef45f543011e38737b8a8622a2f8998cddf0e5437174ef8f1f70a8b9d14a918ab3e232cb3ba343b7abddffa667f0b59075b2b80e6b4d63c3de6127482 + languageName: node + linkType: hard + +"magic-string@npm:^0.30.17": + version: 0.30.21 + resolution: "magic-string@npm:0.30.21" + dependencies: + "@jridgewell/sourcemap-codec": "npm:^1.5.5" + checksum: 10c0/299378e38f9a270069fc62358522ddfb44e94244baa0d6a8980ab2a9b2490a1d03b236b447eee309e17eb3bddfa482c61259d47960eb018a904f0ded52780c4a + languageName: node + linkType: hard + +"math-intrinsics@npm:^1.1.0": + version: 1.1.0 + resolution: "math-intrinsics@npm:1.1.0" + checksum: 10c0/7579ff94e899e2f76ab64491d76cf606274c874d8f2af4a442c016bd85688927fcfca157ba6bf74b08e9439dc010b248ce05b96cc7c126a354c3bae7fcb48b7f + languageName: node + linkType: hard + +"media-typer@npm:0.3.0": + version: 0.3.0 + resolution: "media-typer@npm:0.3.0" + checksum: 10c0/d160f31246907e79fed398470285f21bafb45a62869dc469b1c8877f3f064f5eabc4bcc122f9479b8b605bc5c76187d7871cf84c4ee3ecd3e487da1993279928 + languageName: node + linkType: hard + +"memorystream@npm:^0.3.1": + version: 0.3.1 + resolution: "memorystream@npm:0.3.1" + checksum: 10c0/4bd164657711d9747ff5edb0508b2944414da3464b7fe21ac5c67cf35bba975c4b446a0124bd0f9a8be54cfc18faf92e92bd77563a20328b1ccf2ff04e9f39b9 + languageName: node + linkType: hard + +"merge-descriptors@npm:1.0.3": + version: 1.0.3 + resolution: "merge-descriptors@npm:1.0.3" + checksum: 10c0/866b7094afd9293b5ea5dcd82d71f80e51514bed33b4c4e9f516795dc366612a4cbb4dc94356e943a8a6914889a914530badff27f397191b9b75cda20b6bae93 + languageName: node + linkType: hard + +"merge2@npm:^1.3.0": + version: 1.4.1 + resolution: "merge2@npm:1.4.1" + checksum: 10c0/254a8a4605b58f450308fc474c82ac9a094848081bf4c06778200207820e5193726dc563a0d2c16468810516a5c97d9d3ea0ca6585d23c58ccfff2403e8dbbeb + languageName: node + linkType: hard + +"methods@npm:~1.1.2": + version: 1.1.2 + resolution: "methods@npm:1.1.2" + checksum: 10c0/bdf7cc72ff0a33e3eede03708c08983c4d7a173f91348b4b1e4f47d4cdbf734433ad971e7d1e8c77247d9e5cd8adb81ea4c67b0a2db526b758b2233d7814b8b2 + languageName: node + linkType: hard + +"micromatch@npm:^4.0.8": + version: 4.0.8 + resolution: "micromatch@npm:4.0.8" + dependencies: + braces: "npm:^3.0.3" + picomatch: "npm:^2.3.1" + checksum: 10c0/166fa6eb926b9553f32ef81f5f531d27b4ce7da60e5baf8c021d043b27a388fb95e46a8038d5045877881e673f8134122b59624d5cecbd16eb50a42e7a6b5ca8 + languageName: node + linkType: hard + +"mime-db@npm:1.52.0": + version: 1.52.0 + resolution: "mime-db@npm:1.52.0" + checksum: 10c0/0557a01deebf45ac5f5777fe7740b2a5c309c6d62d40ceab4e23da9f821899ce7a900b7ac8157d4548ddbb7beffe9abc621250e6d182b0397ec7f10c7b91a5aa + languageName: node + linkType: hard + +"mime-db@npm:>= 1.43.0 < 2": + version: 1.54.0 + resolution: "mime-db@npm:1.54.0" + checksum: 10c0/8d907917bc2a90fa2df842cdf5dfeaf509adc15fe0531e07bb2f6ab15992416479015828d6a74200041c492e42cce3ebf78e5ce714388a0a538ea9c53eece284 + languageName: node + linkType: hard + +"mime-types@npm:~2.1.24, mime-types@npm:~2.1.34": + version: 2.1.35 + resolution: "mime-types@npm:2.1.35" + dependencies: + mime-db: "npm:1.52.0" + checksum: 10c0/82fb07ec56d8ff1fc999a84f2f217aa46cb6ed1033fefaabd5785b9a974ed225c90dc72fff460259e66b95b73648596dbcc50d51ed69cdf464af2d237d3149b2 + languageName: node + linkType: hard + +"mime@npm:1.6.0": + version: 1.6.0 + resolution: "mime@npm:1.6.0" + bin: + mime: cli.js + checksum: 10c0/b92cd0adc44888c7135a185bfd0dddc42c32606401c72896a842ae15da71eb88858f17669af41e498b463cd7eb998f7b48939a25b08374c7924a9c8a6f8a81b0 + languageName: node + linkType: hard + +"minimatch@npm:^3.0.4": + version: 3.1.5 + resolution: "minimatch@npm:3.1.5" + dependencies: + brace-expansion: "npm:^1.1.7" + checksum: 10c0/2ecbdc0d33f07bddb0315a8b5afbcb761307a8778b48f0b312418ccbced99f104a2d17d8aca7573433c70e8ccd1c56823a441897a45e384ea76ef401a26ace70 + languageName: node + linkType: hard + +"minipass@npm:^7.0.4, minipass@npm:^7.1.2": + version: 7.1.3 + resolution: "minipass@npm:7.1.3" + checksum: 10c0/539da88daca16533211ea5a9ee98dc62ff5742f531f54640dd34429e621955e91cc280a91a776026264b7f9f6735947629f920944e9c1558369e8bf22eb33fbb + languageName: node + linkType: hard + +"minizlib@npm:^3.1.0": + version: 3.1.0 + resolution: "minizlib@npm:3.1.0" + dependencies: + minipass: "npm:^7.1.2" + checksum: 10c0/5aad75ab0090b8266069c9aabe582c021ae53eb33c6c691054a13a45db3b4f91a7fb1bd79151e6b4e9e9a86727b522527c0a06ec7d45206b745d54cd3097bcec + languageName: node + linkType: hard + +"morgan@npm:^1.10.1": + version: 1.11.0 + resolution: "morgan@npm:1.11.0" + dependencies: + basic-auth: "npm:~2.0.1" + debug: "npm:2.6.9" + depd: "npm:~2.0.0" + on-finished: "npm:~2.4.1" + on-headers: "npm:~1.1.0" + checksum: 10c0/1d8c67bdeb34dc4112f5b6cbff0d5b4ef14bfdcb9f67a7ae67fc7a77282c18dd493a11dc8dff1ff2e285c1846947cfdc3698fdceb1fa7bea2a09fe6cb754455d + languageName: node + linkType: hard + +"ms@npm:2.0.0": + version: 2.0.0 + resolution: "ms@npm:2.0.0" + checksum: 10c0/f8fda810b39fd7255bbdc451c46286e549794fcc700dc9cd1d25658bbc4dc2563a5de6fe7c60f798a16a60c6ceb53f033cb353f493f0cf63e5199b702943159d + languageName: node + linkType: hard + +"ms@npm:2.1.3, ms@npm:^2.1.3": + version: 2.1.3 + resolution: "ms@npm:2.1.3" + checksum: 10c0/d924b57e7312b3b63ad21fc5b3dc0af5e78d61a1fc7cfb5457edaf26326bf62be5307cc87ffb6862ef1c2b33b0233cdb5d4f01c4c958cc0d660948b65a287a48 + languageName: node + linkType: hard + +"mz@npm:^2.7.0": + version: 2.7.0 + resolution: "mz@npm:2.7.0" + dependencies: + any-promise: "npm:^1.0.0" + object-assign: "npm:^4.0.1" + thenify-all: "npm:^1.0.0" + checksum: 10c0/103114e93f87362f0b56ab5b2e7245051ad0276b646e3902c98397d18bb8f4a77f2ea4a2c9d3ad516034ea3a56553b60d3f5f78220001ca4c404bd711bd0af39 + languageName: node + linkType: hard + +"nanoid@npm:^3.3.12": + version: 3.3.16 + resolution: "nanoid@npm:3.3.16" + bin: + nanoid: bin/nanoid.cjs + checksum: 10c0/bbf2dcffe22d2b62d16de2711752070b539c0f644c7916f823ad6521986b2078cbe524f2d6240f58c46e9141ea0c7b87a029e100c0f7f175228cacaf30e41bba + languageName: node + linkType: hard + +"negotiator@npm:0.6.3": + version: 0.6.3 + resolution: "negotiator@npm:0.6.3" + checksum: 10c0/3ec9fd413e7bf071c937ae60d572bc67155262068ed522cf4b3be5edbe6ddf67d095ec03a3a14ebf8fc8e95f8e1d61be4869db0dbb0de696f6b837358bd43fc2 + languageName: node + linkType: hard + +"negotiator@npm:~0.6.4": + version: 0.6.4 + resolution: "negotiator@npm:0.6.4" + checksum: 10c0/3e677139c7fb7628a6f36335bf11a885a62c21d5390204590a1a214a5631fcbe5ea74ef6a610b60afe84b4d975cbe0566a23f20ee17c77c73e74b80032108dea + languageName: node + linkType: hard + +"nice-try@npm:^1.0.4": + version: 1.0.5 + resolution: "nice-try@npm:1.0.5" + checksum: 10c0/95568c1b73e1d0d4069a3e3061a2102d854513d37bcfda73300015b7ba4868d3b27c198d1dbbd8ebdef4112fc2ed9e895d4a0f2e1cce0bd334f2a1346dc9205f + languageName: node + linkType: hard + +"node-domexception@npm:^1.0.0": + version: 1.0.0 + resolution: "node-domexception@npm:1.0.0" + checksum: 10c0/5e5d63cda29856402df9472335af4bb13875e1927ad3be861dc5ebde38917aecbf9ae337923777af52a48c426b70148815e890a5d72760f1b4d758cc671b1a2b + languageName: node + linkType: hard + +"node-fetch@npm:^3.3.2": + version: 3.3.2 + resolution: "node-fetch@npm:3.3.2" + dependencies: + data-uri-to-buffer: "npm:^4.0.0" + fetch-blob: "npm:^3.1.4" + formdata-polyfill: "npm:^4.0.10" + checksum: 10c0/f3d5e56190562221398c9f5750198b34cf6113aa304e34ee97c94fd300ec578b25b2c2906edba922050fce983338fde0d5d34fcb0fc3336ade5bd0e429ad7538 + languageName: node + linkType: hard + +"node-gyp@npm:latest": + version: 13.0.1 + resolution: "node-gyp@npm:13.0.1" + dependencies: + env-paths: "npm:^2.2.0" + exponential-backoff: "npm:^3.1.1" + graceful-fs: "npm:^4.2.6" + nopt: "npm:^10.0.0" + proc-log: "npm:^7.0.0" + semver: "npm:^7.3.5" + tar: "npm:^7.5.4" + tinyglobby: "npm:^0.2.12" + undici: "npm:^8.4.1" + which: "npm:^7.0.0" + bin: + node-gyp: bin/node-gyp.js + checksum: 10c0/424077bc9e9bbe953a8e86db473ba818cbc6a121714008c977fd589e21e5f0c811fbf22faac730dc7182450b5e52df301811d01ae3373898658d999b7710f4e6 + languageName: node + linkType: hard + +"node-releases@npm:^2.0.51": + version: 2.0.51 + resolution: "node-releases@npm:2.0.51" + checksum: 10c0/6cf3fe1f9eabe02ce6de67e9db77b73edc9f5625003791e1f8f239f8e2a851450a5aeb1eff2cc43cfb26312e19b26bfe07626bf428479e6a76f56553fc6148da + languageName: node + linkType: hard + +"nopt@npm:^10.0.0": + version: 10.0.1 + resolution: "nopt@npm:10.0.1" + dependencies: + abbrev: "npm:^5.0.0" + bin: + nopt: bin/nopt.js + checksum: 10c0/980d89257f9587f3e1f77877ddbf905d6aa3b738ec33e49a4fa1a059a0dd82eb28063982b150654a7ae9de386f2ead60e56172db7d37cf56de545f7392a2a26a + languageName: node + linkType: hard + +"normalize-package-data@npm:^2.3.2": + version: 2.5.0 + resolution: "normalize-package-data@npm:2.5.0" + dependencies: + hosted-git-info: "npm:^2.1.4" + resolve: "npm:^1.10.0" + semver: "npm:2 || 3 || 4 || 5" + validate-npm-package-license: "npm:^3.0.1" + checksum: 10c0/357cb1646deb42f8eb4c7d42c4edf0eec312f3628c2ef98501963cc4bbe7277021b2b1d977f982b2edce78f5a1014613ce9cf38085c3df2d76730481357ca504 + languageName: node + linkType: hard + +"normalize-path@npm:^3.0.0, normalize-path@npm:~3.0.0": + version: 3.0.0 + resolution: "normalize-path@npm:3.0.0" + checksum: 10c0/e008c8142bcc335b5e38cf0d63cfd39d6cf2d97480af9abdbe9a439221fd4d749763bab492a8ee708ce7a194bb00c9da6d0a115018672310850489137b3da046 + languageName: node + linkType: hard + +"npm-run-all@npm:^4.1.5": + version: 4.1.5 + resolution: "npm-run-all@npm:4.1.5" + dependencies: + ansi-styles: "npm:^3.2.1" + chalk: "npm:^2.4.1" + cross-spawn: "npm:^6.0.5" + memorystream: "npm:^0.3.1" + minimatch: "npm:^3.0.4" + pidtree: "npm:^0.3.0" + read-pkg: "npm:^3.0.0" + shell-quote: "npm:^1.6.1" + string.prototype.padend: "npm:^3.0.0" + bin: + npm-run-all: bin/npm-run-all/index.js + run-p: bin/run-p/index.js + run-s: bin/run-s/index.js + checksum: 10c0/736ee39bd35454d3efaa4a2e53eba6c523e2e17fba21a18edcce6b221f5cab62000bef16bb6ae8aff9e615831e6b0eb25ab51d52d60e6fa6f4ea880e4c6d31f4 + languageName: node + linkType: hard + +"object-assign@npm:^4.0.1": + version: 4.1.1 + resolution: "object-assign@npm:4.1.1" + checksum: 10c0/1f4df9945120325d041ccf7b86f31e8bcc14e73d29171e37a7903050e96b81323784ec59f93f102ec635bcf6fa8034ba3ea0a8c7e69fa202b87ae3b6cec5a414 + languageName: node + linkType: hard + +"object-hash@npm:^3.0.0": + version: 3.0.0 + resolution: "object-hash@npm:3.0.0" + checksum: 10c0/a06844537107b960c1c8b96cd2ac8592a265186bfa0f6ccafe0d34eabdb526f6fa81da1f37c43df7ed13b12a4ae3457a16071603bcd39d8beddb5f08c37b0f47 + languageName: node + linkType: hard + +"object-inspect@npm:^1.13.3, object-inspect@npm:^1.13.4": + version: 1.13.4 + resolution: "object-inspect@npm:1.13.4" + checksum: 10c0/d7f8711e803b96ea3191c745d6f8056ce1f2496e530e6a19a0e92d89b0fa3c76d910c31f0aa270432db6bd3b2f85500a376a83aaba849a8d518c8845b3211692 + languageName: node + linkType: hard + +"object-keys@npm:^1.1.1": + version: 1.1.1 + resolution: "object-keys@npm:1.1.1" + checksum: 10c0/b11f7ccdbc6d406d1f186cdadb9d54738e347b2692a14439ca5ac70c225fa6db46db809711b78589866d47b25fc3e8dee0b4c722ac751e11180f9380e3d8601d + languageName: node + linkType: hard + +"object.assign@npm:^4.1.7": + version: 4.1.7 + resolution: "object.assign@npm:4.1.7" + dependencies: + call-bind: "npm:^1.0.8" + call-bound: "npm:^1.0.3" + define-properties: "npm:^1.2.1" + es-object-atoms: "npm:^1.0.0" + has-symbols: "npm:^1.1.0" + object-keys: "npm:^1.1.1" + checksum: 10c0/3b2732bd860567ea2579d1567525168de925a8d852638612846bd8082b3a1602b7b89b67b09913cbb5b9bd6e95923b2ae73580baa9d99cb4e990564e8cbf5ddc + languageName: node + linkType: hard + +"on-finished@npm:~2.4.1": + version: 2.4.1 + resolution: "on-finished@npm:2.4.1" + dependencies: + ee-first: "npm:1.1.1" + checksum: 10c0/46fb11b9063782f2d9968863d9cbba33d77aa13c17f895f56129c274318b86500b22af3a160fe9995aa41317efcd22941b6eba747f718ced08d9a73afdb087b4 + languageName: node + linkType: hard + +"on-headers@npm:~1.1.0": + version: 1.1.0 + resolution: "on-headers@npm:1.1.0" + checksum: 10c0/2c3b6b0d68ec9adbd561dc2d61c9b14da8ac03d8a2f0fd9e97bdf0600c887d5d97f664ff3be6876cf40cda6e3c587d73a4745e10b426ac50c7664fc5a0dfc0a1 + languageName: node + linkType: hard + +"own-keys@npm:^1.0.1": + version: 1.0.1 + resolution: "own-keys@npm:1.0.1" + dependencies: + get-intrinsic: "npm:^1.2.6" + object-keys: "npm:^1.1.1" + safe-push-apply: "npm:^1.0.0" + checksum: 10c0/6dfeb3455bff92ec3f16a982d4e3e65676345f6902d9f5ded1d8265a6318d0200ce461956d6d1c70053c7fe9f9fe65e552faac03f8140d37ef0fdd108e67013a + languageName: node + linkType: hard + +"p-map@npm:^7.0.3": + version: 7.0.5 + resolution: "p-map@npm:7.0.5" + checksum: 10c0/4ccbb67d36a8d6e2125ab64ccabee424d1c89b03c10afb43830523c7b9aacc1005e5d21ca7aea70bdc556bd548b7bde65df23c7a18091bbf2b5daf9b86fbc8c5 + languageName: node + linkType: hard + +"p-retry@npm:^4.6.2": + version: 4.6.2 + resolution: "p-retry@npm:4.6.2" + dependencies: + "@types/retry": "npm:0.12.0" + retry: "npm:^0.13.1" + checksum: 10c0/d58512f120f1590cfedb4c2e0c42cb3fa66f3cea8a4646632fcb834c56055bb7a6f138aa57b20cc236fb207c9d694e362e0b5c2b14d9b062f67e8925580c73b0 + languageName: node + linkType: hard + +"parse-json@npm:^4.0.0": + version: 4.0.0 + resolution: "parse-json@npm:4.0.0" + dependencies: + error-ex: "npm:^1.3.1" + json-parse-better-errors: "npm:^1.0.1" + checksum: 10c0/8d80790b772ccb1bcea4e09e2697555e519d83d04a77c2b4237389b813f82898943a93ffff7d0d2406203bdd0c30dcf95b1661e3a53f83d0e417f053957bef32 + languageName: node + linkType: hard + +"parseurl@npm:~1.3.3": + version: 1.3.3 + resolution: "parseurl@npm:1.3.3" + checksum: 10c0/90dd4760d6f6174adb9f20cf0965ae12e23879b5f5464f38e92fce8073354341e4b3b76fa3d878351efe7d01e617121955284cfd002ab087fba1a0726ec0b4f5 + languageName: node + linkType: hard + +"path-key@npm:^2.0.1": + version: 2.0.1 + resolution: "path-key@npm:2.0.1" + checksum: 10c0/dd2044f029a8e58ac31d2bf34c34b93c3095c1481942960e84dd2faa95bbb71b9b762a106aead0646695330936414b31ca0bd862bf488a937ad17c8c5d73b32b + languageName: node + linkType: hard + +"path-parse@npm:^1.0.7": + version: 1.0.7 + resolution: "path-parse@npm:1.0.7" + checksum: 10c0/11ce261f9d294cc7a58d6a574b7f1b935842355ec66fba3c3fd79e0f036462eaf07d0aa95bb74ff432f9afef97ce1926c720988c6a7451d8a584930ae7de86e1 + languageName: node + linkType: hard + +"path-to-regexp@npm:~0.1.12": + version: 0.1.13 + resolution: "path-to-regexp@npm:0.1.13" + checksum: 10c0/1cae3921739c154a8926e136185a10c916f79a249b9072a5001b266d96e193860ca03867e8e8cc808b786862d750f427ed93686bc259355442c3407a62deab1a + languageName: node + linkType: hard + +"path-type@npm:^3.0.0": + version: 3.0.0 + resolution: "path-type@npm:3.0.0" + dependencies: + pify: "npm:^3.0.0" + checksum: 10c0/1332c632f1cac15790ebab8dd729b67ba04fc96f81647496feb1c2975d862d046f41e4b975dbd893048999b2cc90721f72924ad820acc58c78507ba7141a8e56 + languageName: node + linkType: hard + +"pathe@npm:^1.1.2": + version: 1.1.2 + resolution: "pathe@npm:1.1.2" + checksum: 10c0/64ee0a4e587fb0f208d9777a6c56e4f9050039268faaaaecd50e959ef01bf847b7872785c36483fa5cdcdbdfdb31fef2ff222684d4fc21c330ab60395c681897 + languageName: node + linkType: hard + +"pathe@npm:^2.0.3": + version: 2.0.3 + resolution: "pathe@npm:2.0.3" + checksum: 10c0/c118dc5a8b5c4166011b2b70608762e260085180bb9e33e80a50dcdb1e78c010b1624f4280c492c92b05fc276715a4c357d1f9edc570f8f1b3d90b6839ebaca1 + languageName: node + linkType: hard + +"pathval@npm:^2.0.0": + version: 2.0.1 + resolution: "pathval@npm:2.0.1" + checksum: 10c0/460f4709479fbf2c45903a65655fc8f0a5f6d808f989173aeef5fdea4ff4f303dc13f7870303999add60ec49d4c14733895c0a869392e9866f1091fa64fd7581 + languageName: node + linkType: hard + +"pg-cloudflare@npm:^1.3.0": + version: 1.4.0 + resolution: "pg-cloudflare@npm:1.4.0" + checksum: 10c0/553764d00055052648393cda53c1feb065991d6f9fbfdeb56cf8396c5b33377ab2897aaf5dc9cd3933d09023a1f01e8b1ca755431dcf5fd71c92ea277e2888f1 + languageName: node + linkType: hard + +"pg-connection-string@npm:^2.12.0": + version: 2.14.0 + resolution: "pg-connection-string@npm:2.14.0" + checksum: 10c0/c26d85970f782de72b90aa45b4acfa34df90242fc08bc676e8827297164044dbfb40a2cf20b0646bd02662a6b14c5ed27f322db627e41e582f2b705bc41e8b47 + languageName: node + linkType: hard + +"pg-int8@npm:1.0.1": + version: 1.0.1 + resolution: "pg-int8@npm:1.0.1" + checksum: 10c0/be6a02d851fc2a4ae3e9de81710d861de3ba35ac927268973eb3cb618873a05b9424656df464dd43bd7dc3fc5295c3f5b3c8349494f87c7af50ec59ef14e0b98 + languageName: node + linkType: hard + +"pg-pool@npm:^3.13.0": + version: 3.14.0 + resolution: "pg-pool@npm:3.14.0" + peerDependencies: + pg: ">=8.0" + checksum: 10c0/3dd706e67e3b317e29409d9eb3bd44e960eabd86db2a9711a9391bbd43881f8ce7a5f7054a341509558ee05e9bf0a3cc7aef037039da0790ce1e16762ade3ba6 + languageName: node + linkType: hard + +"pg-protocol@npm:*, pg-protocol@npm:^1.13.0": + version: 1.15.0 + resolution: "pg-protocol@npm:1.15.0" + checksum: 10c0/f1acad1e693d23ce70f8a5fec108cb960d6b8546b3a3fb2cf9113211069ef0f8890e2cc26b5f94e11bf81ff6d851b11c0643de12327cd07e0d1127ae1cb80791 + languageName: node + linkType: hard + +"pg-types@npm:2.2.0, pg-types@npm:^2.2.0": + version: 2.2.0 + resolution: "pg-types@npm:2.2.0" + dependencies: + pg-int8: "npm:1.0.1" + postgres-array: "npm:~2.0.0" + postgres-bytea: "npm:~1.0.0" + postgres-date: "npm:~1.0.4" + postgres-interval: "npm:^1.1.0" + checksum: 10c0/ab3f8069a323f601cd2d2279ca8c425447dab3f9b61d933b0601d7ffc00d6200df25e26a4290b2b0783b59278198f7dd2ed03e94c4875797919605116a577c65 + languageName: node + linkType: hard + +"pg@npm:8.20.0": + version: 8.20.0 + resolution: "pg@npm:8.20.0" + dependencies: + pg-cloudflare: "npm:^1.3.0" + pg-connection-string: "npm:^2.12.0" + pg-pool: "npm:^3.13.0" + pg-protocol: "npm:^1.13.0" + pg-types: "npm:2.2.0" + pgpass: "npm:1.0.5" + peerDependencies: + pg-native: ">=3.0.1" + dependenciesMeta: + pg-cloudflare: + optional: true + peerDependenciesMeta: + pg-native: + optional: true + checksum: 10c0/e21d44b9fb3ec188e67778d7abd32d945a546f2da5128b6c8c16da8ae1e42fdc953c0d6f0a2ee65d11f31808c1dffaf908cb9c880cd2e8f0ae05525e4b8bc832 + languageName: node + linkType: hard + +"pgpass@npm:1.0.5": + version: 1.0.5 + resolution: "pgpass@npm:1.0.5" + dependencies: + split2: "npm:^4.1.0" + checksum: 10c0/5ea6c9b2de04c33abb08d33a2dded303c4a3c7162a9264519cbe85c0a9857d712463140ba42fad0c7cd4b21f644dd870b45bb2e02fcbe505b4de0744fd802c1d + languageName: node + linkType: hard + +"picocolors@npm:^1.1.1": + version: 1.1.1 + resolution: "picocolors@npm:1.1.1" + checksum: 10c0/e2e3e8170ab9d7c7421969adaa7e1b31434f789afb9b3f115f6b96d91945041ac3ceb02e9ec6fe6510ff036bcc0bf91e69a1772edc0b707e12b19c0f2d6bcf58 + languageName: node + linkType: hard + +"picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.3.1": + version: 2.3.2 + resolution: "picomatch@npm:2.3.2" + checksum: 10c0/a554d1709e59be97d1acb9eaedbbc700a5c03dbd4579807baed95100b00420bc729335440ef15004ae2378984e2487a7c1cebd743cfdb72b6fa9ab69223c0d61 + languageName: node + linkType: hard + +"picomatch@npm:^4.0.2, picomatch@npm:^4.0.3, picomatch@npm:^4.0.4": + version: 4.0.5 + resolution: "picomatch@npm:4.0.5" + checksum: 10c0/947bc6b6e1ff1e6c5aaf95b107a0839d12802f4f7b867663f67d47accba939ca1cb582cf99dfc30438efa1c4648ac5990967e783e8929c36b03e8440704ef1bd + languageName: node + linkType: hard + +"pidtree@npm:^0.3.0": + version: 0.3.1 + resolution: "pidtree@npm:0.3.1" + bin: + pidtree: bin/pidtree.js + checksum: 10c0/cd69b0182f749f45ab48584e3442c48c5dc4512502c18d5b0147a33b042c41a4db4269b9ce2f7c48f11833ee5e79d81f5ebc6f7bf8372d4ea55726f60dc505a1 + languageName: node + linkType: hard + +"pify@npm:^2.3.0": + version: 2.3.0 + resolution: "pify@npm:2.3.0" + checksum: 10c0/551ff8ab830b1052633f59cb8adc9ae8407a436e06b4a9718bcb27dc5844b83d535c3a8512b388b6062af65a98c49bdc0dd523d8b2617b188f7c8fee457158dc + languageName: node + linkType: hard + +"pify@npm:^3.0.0": + version: 3.0.0 + resolution: "pify@npm:3.0.0" + checksum: 10c0/fead19ed9d801f1b1fcd0638a1ac53eabbb0945bf615f2f8806a8b646565a04a1b0e7ef115c951d225f042cca388fdc1cd3add46d10d1ed6951c20bd2998af10 + languageName: node + linkType: hard + +"pirates@npm:^4.0.1": + version: 4.0.7 + resolution: "pirates@npm:4.0.7" + checksum: 10c0/a51f108dd811beb779d58a76864bbd49e239fa40c7984cd11596c75a121a8cc789f1c8971d8bb15f0dbf9d48b76c05bb62fcbce840f89b688c0fa64b37e8478a + languageName: node + linkType: hard + +"pkg-types@npm:^2.3.0": + version: 2.3.1 + resolution: "pkg-types@npm:2.3.1" + dependencies: + confbox: "npm:^0.2.4" + exsolve: "npm:^1.0.8" + pathe: "npm:^2.0.3" + checksum: 10c0/dd9b20682426abaddcac9dc786aa22542e12df15a03dea2afa7bf54da0933358c6c7f545c0c3c1c7b24a2904ab4a451bf4e34a50c6837e458359eea30c257ed4 + languageName: node + linkType: hard + +"possible-typed-array-names@npm:^1.0.0, possible-typed-array-names@npm:^1.1.0": + version: 1.1.0 + resolution: "possible-typed-array-names@npm:1.1.0" + checksum: 10c0/c810983414142071da1d644662ce4caebce890203eb2bc7bf119f37f3fe5796226e117e6cca146b521921fa6531072674174a3325066ac66fce089a53e1e5196 + languageName: node + linkType: hard + +"postcss-import@npm:^15.1.0": + version: 15.1.0 + resolution: "postcss-import@npm:15.1.0" + dependencies: + postcss-value-parser: "npm:^4.0.0" + read-cache: "npm:^1.0.0" + resolve: "npm:^1.1.7" + peerDependencies: + postcss: ^8.0.0 + checksum: 10c0/518aee5c83ea6940e890b0be675a2588db68b2582319f48c3b4e06535a50ea6ee45f7e63e4309f8754473245c47a0372632378d1d73d901310f295a92f26f17b + languageName: node + linkType: hard + +"postcss-js@npm:^4.0.1": + version: 4.1.0 + resolution: "postcss-js@npm:4.1.0" + dependencies: + camelcase-css: "npm:^2.0.1" + peerDependencies: + postcss: ^8.4.21 + checksum: 10c0/a3cf6e725f3e9ecd7209732f8844a0063a1380b718ccbcf93832b6ec2cd7e63ff70dd2fed49eb2483c7482296860a0f7badd3115b5d0fa05ea648eb6d9dfc9c6 + languageName: node + linkType: hard + +"postcss-load-config@npm:^4.0.2 || ^5.0 || ^6.0": + version: 6.0.1 + resolution: "postcss-load-config@npm:6.0.1" + dependencies: + lilconfig: "npm:^3.1.1" + peerDependencies: + jiti: ">=1.21.0" + postcss: ">=8.0.9" + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + jiti: + optional: true + postcss: + optional: true + tsx: + optional: true + yaml: + optional: true + checksum: 10c0/74173a58816dac84e44853f7afbd283f4ef13ca0b6baeba27701214beec33f9e309b128f8102e2b173e8d45ecba45d279a9be94b46bf48d219626aa9b5730848 + languageName: node + linkType: hard + +"postcss-nested@npm:^6.2.0": + version: 6.2.0 + resolution: "postcss-nested@npm:6.2.0" + dependencies: + postcss-selector-parser: "npm:^6.1.1" + peerDependencies: + postcss: ^8.2.14 + checksum: 10c0/7f9c3f2d764191a39364cbdcec350f26a312431a569c9ef17408021424726b0d67995ff5288405e3724bb7152a4c92f73c027e580ec91e798800ed3c52e2bc6e + languageName: node + linkType: hard + +"postcss-selector-parser@npm:6.0.10": + version: 6.0.10 + resolution: "postcss-selector-parser@npm:6.0.10" + dependencies: + cssesc: "npm:^3.0.0" + util-deprecate: "npm:^1.0.2" + checksum: 10c0/a0b27c5e3f7604c8dc7cd83f145fdd7b21448e0d86072da99e0d78e536ba27aa9db2d42024c50aa530408ee517c4bdc0260529e1afb56608f9a82e839c207e82 + languageName: node + linkType: hard + +"postcss-selector-parser@npm:^6.1.1, postcss-selector-parser@npm:^6.1.2": + version: 6.1.4 + resolution: "postcss-selector-parser@npm:6.1.4" + dependencies: + cssesc: "npm:^3.0.0" + util-deprecate: "npm:^1.0.2" + checksum: 10c0/996f3290dee08ecb073d5f396d1134e619494cfd83140aec07a29618ee1e76d370769a8959d4c0cfefb24aa96dcffa4e7c4937dd881e03b735d50cba959ceb19 + languageName: node + linkType: hard + +"postcss-value-parser@npm:^4.0.0, postcss-value-parser@npm:^4.2.0": + version: 4.2.0 + resolution: "postcss-value-parser@npm:4.2.0" + checksum: 10c0/f4142a4f56565f77c1831168e04e3effd9ffcc5aebaf0f538eee4b2d465adfd4b85a44257bb48418202a63806a7da7fe9f56c330aebb3cac898e46b4cbf49161 + languageName: node + linkType: hard + +"postcss@npm:^8.4.43, postcss@npm:^8.4.47, postcss@npm:^8.5.6": + version: 8.5.19 + resolution: "postcss@npm:8.5.19" + dependencies: + nanoid: "npm:^3.3.12" + picocolors: "npm:^1.1.1" + source-map-js: "npm:^1.2.1" + checksum: 10c0/302af9e4dfa8cce4f43af1798576b3a77ab7b23abac5522328c6c7a830d49b31281cba9d6071e478e6970a05ef82346d1570b8fbffae912f26a4f1a1a07f0a44 + languageName: node + linkType: hard + +"postgres-array@npm:~2.0.0": + version: 2.0.0 + resolution: "postgres-array@npm:2.0.0" + checksum: 10c0/cbd56207e4141d7fbf08c86f2aebf21fa7064943d3f808ec85f442ff94b48d891e7a144cc02665fb2de5dbcb9b8e3183a2ac749959e794b4a4cfd379d7a21d08 + languageName: node + linkType: hard + +"postgres-bytea@npm:~1.0.0": + version: 1.0.1 + resolution: "postgres-bytea@npm:1.0.1" + checksum: 10c0/10b28a27c9d703d5befd97c443e62b551096d1014bc59ab574c65bf0688de7f3f068003b2aea8dcff83cf0f6f9a35f9f74457c38856cf8eb81b00cf3fb44f164 + languageName: node + linkType: hard + +"postgres-date@npm:~1.0.4": + version: 1.0.7 + resolution: "postgres-date@npm:1.0.7" + checksum: 10c0/0ff91fccc64003e10b767fcfeefb5eaffbc522c93aa65d5051c49b3c4ce6cb93ab091a7d22877a90ad60b8874202c6f1d0f935f38a7235ed3b258efd54b97ca9 + languageName: node + linkType: hard + +"postgres-interval@npm:^1.1.0": + version: 1.2.0 + resolution: "postgres-interval@npm:1.2.0" + dependencies: + xtend: "npm:^4.0.0" + checksum: 10c0/c1734c3cb79e7f22579af0b268a463b1fa1d084e742a02a7a290c4f041e349456f3bee3b4ee0bb3f226828597f7b76deb615c1b857db9a742c45520100456272 + languageName: node + linkType: hard + +"prettier-plugin-organize-imports@npm:^4.3.0": + version: 4.3.0 + resolution: "prettier-plugin-organize-imports@npm:4.3.0" + peerDependencies: + prettier: ">=2.0" + typescript: ">=2.9" + vue-tsc: ^2.1.0 || 3 + peerDependenciesMeta: + vue-tsc: + optional: true + checksum: 10c0/57e5a3e611f1da6614fe1228d306b9a361d9249140abfdfcf21cffc9de13bda204c01f44bed0396786269541b63e12b05e1eab03e43d2e201320e4211af88bc6 + languageName: node + linkType: hard + +"prettier@npm:^3.6.2": + version: 3.9.5 + resolution: "prettier@npm:3.9.5" + bin: + prettier: bin/prettier.cjs + checksum: 10c0/0349dd9e6d8010965de6f69e6b6ee2484b8caf78c66675afced6c75ad60ff24c3a2a4f41153fb8adeaee4d8f539641b48cebc71d283cec4d146e9962126c5ceb + languageName: node + linkType: hard + +"proc-log@npm:^7.0.0": + version: 7.0.0 + resolution: "proc-log@npm:7.0.0" + checksum: 10c0/b89c2d862604f35fec795477b0c7e376feab3ba0d4f4d291c4e959567442697cf451ac557d0623c1cc38af45a78128b983410f397a10c5d3a67f76c33de4754b + languageName: node + linkType: hard + +"protobufjs@npm:^7.5.4": + version: 7.6.5 + resolution: "protobufjs@npm:7.6.5" + dependencies: + "@protobufjs/aspromise": "npm:^1.1.2" + "@protobufjs/base64": "npm:^1.1.2" + "@protobufjs/codegen": "npm:^2.0.5" + "@protobufjs/eventemitter": "npm:^1.1.1" + "@protobufjs/fetch": "npm:^1.1.1" + "@protobufjs/float": "npm:^1.0.2" + "@protobufjs/path": "npm:^1.1.2" + "@protobufjs/pool": "npm:^1.1.0" + "@protobufjs/utf8": "npm:^1.1.1" + "@types/node": "npm:>=13.7.0" + long: "npm:^5.3.2" + checksum: 10c0/863eaca9c6f45bfcfb8787c545f53e9c6696507e328e3981b4643c12d35df7f2826021c10e3fd30bef342c13a8e9d306547bac9c0849ee3bf50f770a12f01dc5 + languageName: node + linkType: hard + +"proxy-addr@npm:~2.0.7": + version: 2.0.7 + resolution: "proxy-addr@npm:2.0.7" + dependencies: + forwarded: "npm:0.2.0" + ipaddr.js: "npm:1.9.1" + checksum: 10c0/c3eed999781a35f7fd935f398b6d8920b6fb00bbc14287bc6de78128ccc1a02c89b95b56742bf7cf0362cc333c61d138532049c7dedc7a328ef13343eff81210 + languageName: node + linkType: hard + +"qs@npm:~6.15.1": + version: 6.15.3 + resolution: "qs@npm:6.15.3" + dependencies: + es-define-property: "npm:^1.0.1" + side-channel: "npm:^1.1.1" + checksum: 10c0/8f3f6e45ece255347d57696628401cde29e9ec649fff698b53bd3150dea7cefdf33036e1bc1826b9f110bfa7cb0ec4ab9f5297eca628ce216c55af82c304e08e + languageName: node + linkType: hard + +"queue-microtask@npm:^1.2.2": + version: 1.2.3 + resolution: "queue-microtask@npm:1.2.3" + checksum: 10c0/900a93d3cdae3acd7d16f642c29a642aea32c2026446151f0778c62ac089d4b8e6c986811076e1ae180a694cedf077d453a11b58ff0a865629a4f82ab558e102 + languageName: node + linkType: hard + +"range-parser@npm:~1.2.1": + version: 1.2.1 + resolution: "range-parser@npm:1.2.1" + checksum: 10c0/96c032ac2475c8027b7a4e9fe22dc0dfe0f6d90b85e496e0f016fbdb99d6d066de0112e680805075bd989905e2123b3b3d002765149294dce0c1f7f01fcc2ea0 + languageName: node + linkType: hard + +"raw-body@npm:~2.5.3": + version: 2.5.3 + resolution: "raw-body@npm:2.5.3" + dependencies: + bytes: "npm:~3.1.2" + http-errors: "npm:~2.0.1" + iconv-lite: "npm:~0.4.24" + unpipe: "npm:~1.0.0" + checksum: 10c0/449844344fc90547fb994383a494b83300e4f22199f146a79f68d78a199a8f2a923ea9fd29c3be979bfd50291a3884733619ffc15ba02a32e703b612f8d3f74a + languageName: node + linkType: hard + +"react-dom@npm:^19.0.0": + version: 19.2.7 + resolution: "react-dom@npm:19.2.7" + dependencies: + scheduler: "npm:^0.27.0" + peerDependencies: + react: ^19.2.7 + checksum: 10c0/970ff600f6e80d47d39e2f226f12f226173b3cba3382efc97c5f0cd663de9af38c7a4c11c213fb936094faeac83060d660247accaa96b752180d5b951b9cfecb + languageName: node + linkType: hard + +"react-refresh@npm:^0.14.0": + version: 0.14.2 + resolution: "react-refresh@npm:0.14.2" + checksum: 10c0/875b72ef56b147a131e33f2abd6ec059d1989854b3ff438898e4f9310bfcc73acff709445b7ba843318a953cb9424bcc2c05af2b3d80011cee28f25aef3e2ebb + languageName: node + linkType: hard + +"react-router@npm:^7.9.1": + version: 7.18.1 + resolution: "react-router@npm:7.18.1" + dependencies: + cookie: "npm:^1.0.1" + set-cookie-parser: "npm:^2.6.0" + peerDependencies: + react: ">=18" + react-dom: ">=18" + peerDependenciesMeta: + react-dom: + optional: true + checksum: 10c0/1a559de58d656bad5565f2232e4f4fab7e9f5282dfaf95bc24f195a06e7e85b281077fe5c21ed3ff527e3d90d930447e0d0bb0f0713950d2a56ad0f0377a7d09 + languageName: node + linkType: hard + +"react@npm:^19.0.0": + version: 19.2.7 + resolution: "react@npm:19.2.7" + checksum: 10c0/0bd0e2f1bbd4ba97561c6597bf8a5fec05e6476fe61e165c1065598d16668efc6715205599c94d3ddd49d36cb0f21cbf1b9bcc18ee840b805ce222c3e8d558ac + languageName: node + linkType: hard + +"read-cache@npm:^1.0.0": + version: 1.0.0 + resolution: "read-cache@npm:1.0.0" + dependencies: + pify: "npm:^2.3.0" + checksum: 10c0/90cb2750213c7dd7c80cb420654344a311fdec12944e81eb912cd82f1bc92aea21885fa6ce442e3336d9fccd663b8a7a19c46d9698e6ca55620848ab932da814 + languageName: node + linkType: hard + +"read-pkg@npm:^3.0.0": + version: 3.0.0 + resolution: "read-pkg@npm:3.0.0" + dependencies: + load-json-file: "npm:^4.0.0" + normalize-package-data: "npm:^2.3.2" + path-type: "npm:^3.0.0" + checksum: 10c0/65acf2df89fbcd506b48b7ced56a255ba00adf7ecaa2db759c86cc58212f6fd80f1f0b7a85c848551a5d0685232e9b64f45c1fd5b48d85df2761a160767eeb93 + languageName: node + linkType: hard + +"readdirp@npm:^4.0.1": + version: 4.1.2 + resolution: "readdirp@npm:4.1.2" + checksum: 10c0/60a14f7619dec48c9c850255cd523e2717001b0e179dc7037cfa0895da7b9e9ab07532d324bfb118d73a710887d1e35f79c495fa91582784493e085d18c72c62 + languageName: node + linkType: hard + +"readdirp@npm:~3.6.0": + version: 3.6.0 + resolution: "readdirp@npm:3.6.0" + dependencies: + picomatch: "npm:^2.2.1" + checksum: 10c0/6fa848cf63d1b82ab4e985f4cf72bd55b7dcfd8e0a376905804e48c3634b7e749170940ba77b32804d5fe93b3cc521aa95a8d7e7d725f830da6d93f3669ce66b + languageName: node + linkType: hard + +"reflect.getprototypeof@npm:^1.0.10, reflect.getprototypeof@npm:^1.0.9": + version: 1.0.10 + resolution: "reflect.getprototypeof@npm:1.0.10" + dependencies: + call-bind: "npm:^1.0.8" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.9" + es-errors: "npm:^1.3.0" + es-object-atoms: "npm:^1.0.0" + get-intrinsic: "npm:^1.2.7" + get-proto: "npm:^1.0.1" + which-builtin-type: "npm:^1.2.1" + checksum: 10c0/7facec28c8008876f8ab98e80b7b9cb4b1e9224353fd4756dda5f2a4ab0d30fa0a5074777c6df24e1e0af463a2697513b0a11e548d99cf52f21f7bc6ba48d3ac + languageName: node + linkType: hard + +"regexp.prototype.flags@npm:^1.5.4": + version: 1.5.4 + resolution: "regexp.prototype.flags@npm:1.5.4" + dependencies: + call-bind: "npm:^1.0.8" + define-properties: "npm:^1.2.1" + es-errors: "npm:^1.3.0" + get-proto: "npm:^1.0.1" + gopd: "npm:^1.2.0" + set-function-name: "npm:^2.0.2" + checksum: 10c0/83b88e6115b4af1c537f8dabf5c3744032cb875d63bc05c288b1b8c0ef37cbe55353f95d8ca817e8843806e3e150b118bc624e4279b24b4776b4198232735a77 + languageName: node + linkType: hard + +"resolve-pkg-maps@npm:^1.0.0": + version: 1.0.0 + resolution: "resolve-pkg-maps@npm:1.0.0" + checksum: 10c0/fb8f7bbe2ca281a73b7ef423a1cbc786fb244bd7a95cbe5c3fba25b27d327150beca8ba02f622baea65919a57e061eb5005204daa5f93ed590d9b77463a567ab + languageName: node + linkType: hard + +"resolve@npm:^1.1.7, resolve@npm:^1.10.0, resolve@npm:^1.22.8": + version: 1.22.12 + resolution: "resolve@npm:1.22.12" + dependencies: + es-errors: "npm:^1.3.0" + is-core-module: "npm:^2.16.1" + path-parse: "npm:^1.0.7" + supports-preserve-symlinks-flag: "npm:^1.0.0" + bin: + resolve: bin/resolve + checksum: 10c0/b16dc9b537c02e8c3388f7d3dcff9741d3071625f9a97ac1c885f2b0ca51e78df22328fb6d6ef214dd9101fb7cfc19aa2836fe3410402a94f3f7b8639c7149bf + languageName: node + linkType: hard + +"resolve@patch:resolve@npm%3A^1.1.7#optional!builtin, resolve@patch:resolve@npm%3A^1.10.0#optional!builtin, resolve@patch:resolve@npm%3A^1.22.8#optional!builtin": + version: 1.22.12 + resolution: "resolve@patch:resolve@npm%3A1.22.12#optional!builtin::version=1.22.12&hash=c3c19d" + dependencies: + es-errors: "npm:^1.3.0" + is-core-module: "npm:^2.16.1" + path-parse: "npm:^1.0.7" + supports-preserve-symlinks-flag: "npm:^1.0.0" + bin: + resolve: bin/resolve + checksum: 10c0/fc6519984ae1f894d877c0060ba8b1f5ba3bc0e85a02f74e141929c118c23d74d9735619a9cc2965397387e514884245c65d72a40731dcb6cfc84c7bcdc8321e + languageName: node + linkType: hard + +"retry@npm:^0.13.1": + version: 0.13.1 + resolution: "retry@npm:0.13.1" + checksum: 10c0/9ae822ee19db2163497e074ea919780b1efa00431d197c7afdb950e42bf109196774b92a49fc9821f0b8b328a98eea6017410bfc5e8a0fc19c85c6d11adb3772 + languageName: node + linkType: hard + +"reusify@npm:^1.0.4": + version: 1.1.0 + resolution: "reusify@npm:1.1.0" + checksum: 10c0/4eff0d4a5f9383566c7d7ec437b671cc51b25963bd61bf127c3f3d3f68e44a026d99b8d2f1ad344afff8d278a8fe70a8ea092650a716d22287e8bef7126bb2fa + languageName: node + linkType: hard + +"rollup@npm:^4.20.0, rollup@npm:^4.43.0": + version: 4.62.2 + resolution: "rollup@npm:4.62.2" + dependencies: + "@rollup/rollup-android-arm-eabi": "npm:4.62.2" + "@rollup/rollup-android-arm64": "npm:4.62.2" + "@rollup/rollup-darwin-arm64": "npm:4.62.2" + "@rollup/rollup-darwin-x64": "npm:4.62.2" + "@rollup/rollup-freebsd-arm64": "npm:4.62.2" + "@rollup/rollup-freebsd-x64": "npm:4.62.2" + "@rollup/rollup-linux-arm-gnueabihf": "npm:4.62.2" + "@rollup/rollup-linux-arm-musleabihf": "npm:4.62.2" + "@rollup/rollup-linux-arm64-gnu": "npm:4.62.2" + "@rollup/rollup-linux-arm64-musl": "npm:4.62.2" + "@rollup/rollup-linux-loong64-gnu": "npm:4.62.2" + "@rollup/rollup-linux-loong64-musl": "npm:4.62.2" + "@rollup/rollup-linux-ppc64-gnu": "npm:4.62.2" + "@rollup/rollup-linux-ppc64-musl": "npm:4.62.2" + "@rollup/rollup-linux-riscv64-gnu": "npm:4.62.2" + "@rollup/rollup-linux-riscv64-musl": "npm:4.62.2" + "@rollup/rollup-linux-s390x-gnu": "npm:4.62.2" + "@rollup/rollup-linux-x64-gnu": "npm:4.62.2" + "@rollup/rollup-linux-x64-musl": "npm:4.62.2" + "@rollup/rollup-openbsd-x64": "npm:4.62.2" + "@rollup/rollup-openharmony-arm64": "npm:4.62.2" + "@rollup/rollup-win32-arm64-msvc": "npm:4.62.2" + "@rollup/rollup-win32-ia32-msvc": "npm:4.62.2" + "@rollup/rollup-win32-x64-gnu": "npm:4.62.2" + "@rollup/rollup-win32-x64-msvc": "npm:4.62.2" + "@types/estree": "npm:1.0.9" + fsevents: "npm:~2.3.2" + dependenciesMeta: + "@rollup/rollup-android-arm-eabi": + optional: true + "@rollup/rollup-android-arm64": + optional: true + "@rollup/rollup-darwin-arm64": + optional: true + "@rollup/rollup-darwin-x64": + optional: true + "@rollup/rollup-freebsd-arm64": + optional: true + "@rollup/rollup-freebsd-x64": + optional: true + "@rollup/rollup-linux-arm-gnueabihf": + optional: true + "@rollup/rollup-linux-arm-musleabihf": + optional: true + "@rollup/rollup-linux-arm64-gnu": + optional: true + "@rollup/rollup-linux-arm64-musl": + optional: true + "@rollup/rollup-linux-loong64-gnu": + optional: true + "@rollup/rollup-linux-loong64-musl": + optional: true + "@rollup/rollup-linux-ppc64-gnu": + optional: true + "@rollup/rollup-linux-ppc64-musl": + optional: true + "@rollup/rollup-linux-riscv64-gnu": + optional: true + "@rollup/rollup-linux-riscv64-musl": + optional: true + "@rollup/rollup-linux-s390x-gnu": + optional: true + "@rollup/rollup-linux-x64-gnu": + optional: true + "@rollup/rollup-linux-x64-musl": + optional: true + "@rollup/rollup-openbsd-x64": + optional: true + "@rollup/rollup-openharmony-arm64": + optional: true + "@rollup/rollup-win32-arm64-msvc": + optional: true + "@rollup/rollup-win32-ia32-msvc": + optional: true + "@rollup/rollup-win32-x64-gnu": + optional: true + "@rollup/rollup-win32-x64-msvc": + optional: true + fsevents: + optional: true + bin: + rollup: dist/bin/rollup + checksum: 10c0/83ff5f4a1fea3fa05db2ef56beceee8c33d4a72b818e19c562f1e85c41076fe5b12aadc44048bb73e60b83336df82154b017fa6bf0186f3141643e6f215fbdcb + languageName: node + linkType: hard + +"run-parallel@npm:^1.1.9": + version: 1.2.0 + resolution: "run-parallel@npm:1.2.0" + dependencies: + queue-microtask: "npm:^1.2.2" + checksum: 10c0/200b5ab25b5b8b7113f9901bfe3afc347e19bb7475b267d55ad0eb86a62a46d77510cb0f232507c9e5d497ebda569a08a9867d0d14f57a82ad5564d991588b39 + languageName: node + linkType: hard + +"safe-array-concat@npm:^1.1.3": + version: 1.1.4 + resolution: "safe-array-concat@npm:1.1.4" + dependencies: + call-bind: "npm:^1.0.9" + call-bound: "npm:^1.0.4" + get-intrinsic: "npm:^1.3.0" + has-symbols: "npm:^1.1.0" + isarray: "npm:^2.0.5" + checksum: 10c0/95fb4904ab1d9360a666fe5ba6d88f1c4a3a39682739e4512cff809fc6b5722a94bd95189211015bfb45859a7ffbc3340ea303ae22721c91c59e8946d310975a + languageName: node + linkType: hard + +"safe-buffer@npm:5.1.2": + version: 5.1.2 + resolution: "safe-buffer@npm:5.1.2" + checksum: 10c0/780ba6b5d99cc9a40f7b951d47152297d0e260f0df01472a1b99d4889679a4b94a13d644f7dbc4f022572f09ae9005fa2fbb93bbbd83643316f365a3e9a45b21 + languageName: node + linkType: hard + +"safe-buffer@npm:5.2.1, safe-buffer@npm:^5.0.1": + version: 5.2.1 + resolution: "safe-buffer@npm:5.2.1" + checksum: 10c0/6501914237c0a86e9675d4e51d89ca3c21ffd6a31642efeba25ad65720bce6921c9e7e974e5be91a786b25aa058b5303285d3c15dbabf983a919f5f630d349f3 + languageName: node + linkType: hard + +"safe-push-apply@npm:^1.0.0": + version: 1.0.0 + resolution: "safe-push-apply@npm:1.0.0" + dependencies: + es-errors: "npm:^1.3.0" + isarray: "npm:^2.0.5" + checksum: 10c0/831f1c9aae7436429e7862c7e46f847dfe490afac20d0ee61bae06108dbf5c745a0de3568ada30ccdd3eeb0864ca8331b2eef703abd69bfea0745b21fd320750 + languageName: node + linkType: hard + +"safe-regex-test@npm:^1.1.0": + version: 1.1.0 + resolution: "safe-regex-test@npm:1.1.0" + dependencies: + call-bound: "npm:^1.0.2" + es-errors: "npm:^1.3.0" + is-regex: "npm:^1.2.1" + checksum: 10c0/f2c25281bbe5d39cddbbce7f86fca5ea9b3ce3354ea6cd7c81c31b006a5a9fff4286acc5450a3b9122c56c33eba69c56b9131ad751457b2b4a585825e6a10665 + languageName: node + linkType: hard + +"safer-buffer@npm:>= 2.1.2 < 3": + version: 2.1.2 + resolution: "safer-buffer@npm:2.1.2" + checksum: 10c0/7e3c8b2e88a1841c9671094bbaeebd94448111dd90a81a1f606f3f67708a6ec57763b3b47f06da09fc6054193e0e6709e77325415dc8422b04497a8070fa02d4 + languageName: node + linkType: hard + +"scheduler@npm:^0.27.0": + version: 0.27.0 + resolution: "scheduler@npm:0.27.0" + checksum: 10c0/4f03048cb05a3c8fddc45813052251eca00688f413a3cee236d984a161da28db28ba71bd11e7a3dd02f7af84ab28d39fb311431d3b3772fed557945beb00c452 + languageName: node + linkType: hard + +"semver@npm:2 || 3 || 4 || 5, semver@npm:^5.5.0": + version: 5.7.2 + resolution: "semver@npm:5.7.2" + bin: + semver: bin/semver + checksum: 10c0/e4cf10f86f168db772ae95d86ba65b3fd6c5967c94d97c708ccb463b778c2ee53b914cd7167620950fc07faf5a564e6efe903836639e512a1aa15fbc9667fa25 + languageName: node + linkType: hard + +"semver@npm:^6.3.1": + version: 6.3.1 + resolution: "semver@npm:6.3.1" + bin: + semver: bin/semver.js + checksum: 10c0/e3d79b609071caa78bcb6ce2ad81c7966a46a7431d9d58b8800cfa9cb6a63699b3899a0e4bcce36167a284578212d9ae6942b6929ba4aa5015c079a67751d42d + languageName: node + linkType: hard + +"semver@npm:^7.3.5, semver@npm:^7.3.7": + version: 7.8.5 + resolution: "semver@npm:7.8.5" + bin: + semver: bin/semver.js + checksum: 10c0/b1f3127a5be8125a94f37188b361c212466c292c6910adce3ec106cff5dc211ccaedc4739c11bb70fda59d6fc1f040a9bca289f4e093451521a2372e5231fe0c + languageName: node + linkType: hard + +"send@npm:~0.19.0, send@npm:~0.19.1": + version: 0.19.2 + resolution: "send@npm:0.19.2" + dependencies: + debug: "npm:2.6.9" + depd: "npm:2.0.0" + destroy: "npm:1.2.0" + encodeurl: "npm:~2.0.0" + escape-html: "npm:~1.0.3" + etag: "npm:~1.8.1" + fresh: "npm:~0.5.2" + http-errors: "npm:~2.0.1" + mime: "npm:1.6.0" + ms: "npm:2.1.3" + on-finished: "npm:~2.4.1" + range-parser: "npm:~1.2.1" + statuses: "npm:~2.0.2" + checksum: 10c0/20c2389fe0fdf3fc499938cac598bc32272287e993c4960717381a10de8550028feadfb9076f959a3a3ebdea42e1f690e116f0d16468fa56b9fd41866d3dc267 + languageName: node + linkType: hard + +"serialize-error@npm:8.1.0": + version: 8.1.0 + resolution: "serialize-error@npm:8.1.0" + dependencies: + type-fest: "npm:^0.20.2" + checksum: 10c0/8cfd89f43ca93e283c5f1d16178a536bdfac9bc6029f4a9df988610cc399bc4f2478d1f10ce40b9dff66b863a5158a19b438fbec929045c96d92174f6bca1e88 + languageName: node + linkType: hard + +"serve-static@npm:~1.16.2": + version: 1.16.3 + resolution: "serve-static@npm:1.16.3" + dependencies: + encodeurl: "npm:~2.0.0" + escape-html: "npm:~1.0.3" + parseurl: "npm:~1.3.3" + send: "npm:~0.19.1" + checksum: 10c0/36320397a073c71bedf58af48a4a100fe6d93f07459af4d6f08b9a7217c04ce2a4939e0effd842dc7bece93ffcd59eb52f58c4fff2a8e002dc29ae6b219cd42b + languageName: node + linkType: hard + +"set-cookie-parser@npm:^2.6.0": + version: 2.7.2 + resolution: "set-cookie-parser@npm:2.7.2" + checksum: 10c0/4381a9eb7ee951dfe393fe7aacf76b9a3b4e93a684d2162ab35594fa4053cc82a4d7d7582bf397718012c9adcf839b8cd8f57c6c42901ea9effe33c752da4a45 + languageName: node + linkType: hard + +"set-function-length@npm:^1.2.2": + version: 1.2.2 + resolution: "set-function-length@npm:1.2.2" + dependencies: + define-data-property: "npm:^1.1.4" + es-errors: "npm:^1.3.0" + function-bind: "npm:^1.1.2" + get-intrinsic: "npm:^1.2.4" + gopd: "npm:^1.0.1" + has-property-descriptors: "npm:^1.0.2" + checksum: 10c0/82850e62f412a258b71e123d4ed3873fa9377c216809551192bb6769329340176f109c2eeae8c22a8d386c76739855f78e8716515c818bcaef384b51110f0f3c + languageName: node + linkType: hard + +"set-function-name@npm:^2.0.2": + version: 2.0.2 + resolution: "set-function-name@npm:2.0.2" + dependencies: + define-data-property: "npm:^1.1.4" + es-errors: "npm:^1.3.0" + functions-have-names: "npm:^1.2.3" + has-property-descriptors: "npm:^1.0.2" + checksum: 10c0/fce59f90696c450a8523e754abb305e2b8c73586452619c2bad5f7bf38c7b6b4651895c9db895679c5bef9554339cf3ef1c329b66ece3eda7255785fbe299316 + languageName: node + linkType: hard + +"set-proto@npm:^1.0.0": + version: 1.0.0 + resolution: "set-proto@npm:1.0.0" + dependencies: + dunder-proto: "npm:^1.0.1" + es-errors: "npm:^1.3.0" + es-object-atoms: "npm:^1.0.0" + checksum: 10c0/ca5c3ccbba479d07c30460e367e66337cec825560b11e8ba9c5ebe13a2a0d6021ae34eddf94ff3dfe17a3104dc1f191519cb6c48378b503e5c3f36393938776a + languageName: node + linkType: hard + +"setprototypeof@npm:1.2.0, setprototypeof@npm:~1.2.0": + version: 1.2.0 + resolution: "setprototypeof@npm:1.2.0" + checksum: 10c0/68733173026766fa0d9ecaeb07f0483f4c2dc70ca376b3b7c40b7cda909f94b0918f6c5ad5ce27a9160bdfb475efaa9d5e705a11d8eaae18f9835d20976028bc + languageName: node + linkType: hard + +"shebang-command@npm:^1.2.0": + version: 1.2.0 + resolution: "shebang-command@npm:1.2.0" + dependencies: + shebang-regex: "npm:^1.0.0" + checksum: 10c0/7b20dbf04112c456b7fc258622dafd566553184ac9b6938dd30b943b065b21dabd3776460df534cc02480db5e1b6aec44700d985153a3da46e7db7f9bd21326d + languageName: node + linkType: hard + +"shebang-regex@npm:^1.0.0": + version: 1.0.0 + resolution: "shebang-regex@npm:1.0.0" + checksum: 10c0/9abc45dee35f554ae9453098a13fdc2f1730e525a5eb33c51f096cc31f6f10a4b38074c1ebf354ae7bffa7229506083844008dfc3bb7818228568c0b2dc1fff2 + languageName: node + linkType: hard + +"shell-quote@npm:^1.6.1": + version: 1.10.0 + resolution: "shell-quote@npm:1.10.0" + checksum: 10c0/46ee59bfd972ce6a45500c44ed130dff2d0a7d6fbac9841e59d548518cad8060a06393c9a5dcbc0cede294ad80b2a2cd8c904679e09265f53efc0a0879f30961 + languageName: node + linkType: hard + +"side-channel-list@npm:^1.0.1": + version: 1.0.1 + resolution: "side-channel-list@npm:1.0.1" + dependencies: + es-errors: "npm:^1.3.0" + object-inspect: "npm:^1.13.4" + checksum: 10c0/d346c787fd2f9f1c2fdea14f00e8250118db0e7596d85a6cb9faa75f105d31a73a8f7a341c93d7df2a2429098c3d37a77bd3be9e88c37094b8c01807bc77c7a2 + languageName: node + linkType: hard + +"side-channel-map@npm:^1.0.1": + version: 1.0.1 + resolution: "side-channel-map@npm:1.0.1" + dependencies: + call-bound: "npm:^1.0.2" + es-errors: "npm:^1.3.0" + get-intrinsic: "npm:^1.2.5" + object-inspect: "npm:^1.13.3" + checksum: 10c0/010584e6444dd8a20b85bc926d934424bd809e1a3af941cace229f7fdcb751aada0fb7164f60c2e22292b7fa3c0ff0bce237081fd4cdbc80de1dc68e95430672 + languageName: node + linkType: hard + +"side-channel-weakmap@npm:^1.0.2": + version: 1.0.2 + resolution: "side-channel-weakmap@npm:1.0.2" + dependencies: + call-bound: "npm:^1.0.2" + es-errors: "npm:^1.3.0" + get-intrinsic: "npm:^1.2.5" + object-inspect: "npm:^1.13.3" + side-channel-map: "npm:^1.0.1" + checksum: 10c0/71362709ac233e08807ccd980101c3e2d7efe849edc51455030327b059f6c4d292c237f94dc0685031dd11c07dd17a68afde235d6cf2102d949567f98ab58185 + languageName: node + linkType: hard + +"side-channel@npm:^1.1.0, side-channel@npm:^1.1.1": + version: 1.1.1 + resolution: "side-channel@npm:1.1.1" + dependencies: + es-errors: "npm:^1.3.0" + object-inspect: "npm:^1.13.4" + side-channel-list: "npm:^1.0.1" + side-channel-map: "npm:^1.0.1" + side-channel-weakmap: "npm:^1.0.2" + checksum: 10c0/dc0ab81d67f61bda9247d053ce93f41c3fd8ad2bdcb9cf9d8d2f8540d488f26d87a5e99ebfc07eea49ec025867b2452b705442d974b1478f0395e69f6bfb3270 + languageName: node + linkType: hard + +"siginfo@npm:^2.0.0": + version: 2.0.0 + resolution: "siginfo@npm:2.0.0" + checksum: 10c0/3def8f8e516fbb34cb6ae415b07ccc5d9c018d85b4b8611e3dc6f8be6d1899f693a4382913c9ed51a06babb5201639d76453ab297d1c54a456544acf5c892e34 + languageName: node + linkType: hard + +"source-map-js@npm:^1.2.1": + version: 1.2.1 + resolution: "source-map-js@npm:1.2.1" + checksum: 10c0/7bda1fc4c197e3c6ff17de1b8b2c20e60af81b63a52cb32ec5a5d67a20a7d42651e2cb34ebe93833c5a2a084377e17455854fee3e21e7925c64a51b6a52b0faf + languageName: node + linkType: hard + +"source-map-support@npm:^0.5.21": + version: 0.5.21 + resolution: "source-map-support@npm:0.5.21" + dependencies: + buffer-from: "npm:^1.0.0" + source-map: "npm:^0.6.0" + checksum: 10c0/9ee09942f415e0f721d6daad3917ec1516af746a8120bba7bb56278707a37f1eb8642bde456e98454b8a885023af81a16e646869975f06afc1a711fb90484e7d + languageName: node + linkType: hard + +"source-map@npm:^0.6.0": + version: 0.6.1 + resolution: "source-map@npm:0.6.1" + checksum: 10c0/ab55398007c5e5532957cb0beee2368529618ac0ab372d789806f5718123cc4367d57de3904b4e6a4170eb5a0b0f41373066d02ca0735a0c4d75c7d328d3e011 + languageName: node + linkType: hard + +"spdx-correct@npm:^3.0.0": + version: 3.2.0 + resolution: "spdx-correct@npm:3.2.0" + dependencies: + spdx-expression-parse: "npm:^3.0.0" + spdx-license-ids: "npm:^3.0.0" + checksum: 10c0/49208f008618b9119208b0dadc9208a3a55053f4fd6a0ae8116861bd22696fc50f4142a35ebfdb389e05ccf2de8ad142573fefc9e26f670522d899f7b2fe7386 + languageName: node + linkType: hard + +"spdx-exceptions@npm:^2.1.0": + version: 2.5.0 + resolution: "spdx-exceptions@npm:2.5.0" + checksum: 10c0/37217b7762ee0ea0d8b7d0c29fd48b7e4dfb94096b109d6255b589c561f57da93bf4e328c0290046115961b9209a8051ad9f525e48d433082fc79f496a4ea940 + languageName: node + linkType: hard + +"spdx-expression-parse@npm:^3.0.0": + version: 3.0.1 + resolution: "spdx-expression-parse@npm:3.0.1" + dependencies: + spdx-exceptions: "npm:^2.1.0" + spdx-license-ids: "npm:^3.0.0" + checksum: 10c0/6f8a41c87759fa184a58713b86c6a8b028250f158159f1d03ed9d1b6ee4d9eefdc74181c8ddc581a341aa971c3e7b79e30b59c23b05d2436d5de1c30bdef7171 + languageName: node + linkType: hard + +"spdx-license-ids@npm:^3.0.0": + version: 3.0.23 + resolution: "spdx-license-ids@npm:3.0.23" + checksum: 10c0/8495620f6f2a237749cce922ea2d593a66f7885c301b1a0f5542183e7041182f27f616a8f13345cefdea0c9b3e0899328e0aa8cec100cf4f3fac4bb3bd975515 + languageName: node + linkType: hard + +"split2@npm:^4.1.0": + version: 4.2.0 + resolution: "split2@npm:4.2.0" + checksum: 10c0/b292beb8ce9215f8c642bb68be6249c5a4c7f332fc8ecadae7be5cbdf1ea95addc95f0459ef2e7ad9d45fd1064698a097e4eb211c83e772b49bc0ee423e91534 + languageName: node + linkType: hard + +"stackback@npm:0.0.2": + version: 0.0.2 + resolution: "stackback@npm:0.0.2" + checksum: 10c0/89a1416668f950236dd5ac9f9a6b2588e1b9b62b1b6ad8dff1bfc5d1a15dbf0aafc9b52d2226d00c28dffff212da464eaeebfc6b7578b9d180cef3e3782c5983 + languageName: node + linkType: hard + +"statuses@npm:~2.0.1, statuses@npm:~2.0.2": + version: 2.0.2 + resolution: "statuses@npm:2.0.2" + checksum: 10c0/a9947d98ad60d01f6b26727570f3bcceb6c8fa789da64fe6889908fe2e294d57503b14bf2b5af7605c2d36647259e856635cd4c49eab41667658ec9d0080ec3f + languageName: node + linkType: hard + +"std-env@npm:^3.9.0": + version: 3.10.0 + resolution: "std-env@npm:3.10.0" + checksum: 10c0/1814927a45004d36dde6707eaf17552a546769bc79a6421be2c16ce77d238158dfe5de30910b78ec30d95135cc1c59ea73ee22d2ca170f8b9753f84da34c427f + languageName: node + linkType: hard + +"stop-iteration-iterator@npm:^1.1.0": + version: 1.1.0 + resolution: "stop-iteration-iterator@npm:1.1.0" + dependencies: + es-errors: "npm:^1.3.0" + internal-slot: "npm:^1.1.0" + checksum: 10c0/de4e45706bb4c0354a4b1122a2b8cc45a639e86206807ce0baf390ee9218d3ef181923fa4d2b67443367c491aa255c5fbaa64bb74648e3c5b48299928af86c09 + languageName: node + linkType: hard + +"string.prototype.padend@npm:^3.0.0": + version: 3.1.6 + resolution: "string.prototype.padend@npm:3.1.6" + dependencies: + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.2" + es-object-atoms: "npm:^1.0.0" + checksum: 10c0/8f2c8c1f3db1efcdc210668c80c87f2cea1253d6029ff296a172b5e13edc9adebeed4942d023de8d31f9b13b69f3f5d73de7141959b1f09817fba5f527e83be1 + languageName: node + linkType: hard + +"string.prototype.trim@npm:^1.2.10": + version: 1.2.11 + resolution: "string.prototype.trim@npm:1.2.11" + dependencies: + call-bind: "npm:^1.0.9" + call-bound: "npm:^1.0.4" + define-data-property: "npm:^1.1.4" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.24.2" + es-object-atoms: "npm:^1.1.2" + has-property-descriptors: "npm:^1.0.2" + safe-regex-test: "npm:^1.1.0" + checksum: 10c0/b153cf8ed06db82ff40e27829e88e5c13f45eff9799f1d5707626e25989b488b059d6f5d57011e07f77745e28451e16735f295bc59c8ae146a4fd73a442366b0 + languageName: node + linkType: hard + +"string.prototype.trimend@npm:^1.0.9": + version: 1.0.10 + resolution: "string.prototype.trimend@npm:1.0.10" + dependencies: + call-bind: "npm:^1.0.9" + call-bound: "npm:^1.0.4" + define-properties: "npm:^1.2.1" + es-object-atoms: "npm:^1.1.2" + checksum: 10c0/cc09233181769047a5330becfd5740fec5f0c8137886e7b553626788b00f75df9f34db1159bc52dbb7fc389b8ebb6e1dab44c8c9e31eb600039729a542013286 + languageName: node + linkType: hard + +"string.prototype.trimstart@npm:^1.0.8": + version: 1.0.8 + resolution: "string.prototype.trimstart@npm:1.0.8" + dependencies: + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-object-atoms: "npm:^1.0.0" + checksum: 10c0/d53af1899959e53c83b64a5fd120be93e067da740e7e75acb433849aa640782fb6c7d4cd5b84c954c84413745a3764df135a8afeb22908b86a835290788d8366 + languageName: node + linkType: hard + +"strip-bom@npm:^3.0.0": + version: 3.0.0 + resolution: "strip-bom@npm:3.0.0" + checksum: 10c0/51201f50e021ef16672593d7434ca239441b7b760e905d9f33df6e4f3954ff54ec0e0a06f100d028af0982d6f25c35cd5cda2ce34eaebccd0250b8befb90d8f1 + languageName: node + linkType: hard + +"strip-literal@npm:^3.0.0": + version: 3.1.0 + resolution: "strip-literal@npm:3.1.0" + dependencies: + js-tokens: "npm:^9.0.1" + checksum: 10c0/50918f669915d9ad0fe4b7599902b735f853f2201c97791ead00104a654259c0c61bc2bc8fa3db05109339b61f4cf09e47b94ecc874ffbd0e013965223893af8 + languageName: node + linkType: hard + +"sucrase@npm:^3.35.0": + version: 3.35.1 + resolution: "sucrase@npm:3.35.1" + dependencies: + "@jridgewell/gen-mapping": "npm:^0.3.2" + commander: "npm:^4.0.0" + lines-and-columns: "npm:^1.1.6" + mz: "npm:^2.7.0" + pirates: "npm:^4.0.1" + tinyglobby: "npm:^0.2.11" + ts-interface-checker: "npm:^0.1.9" + bin: + sucrase: bin/sucrase + sucrase-node: bin/sucrase-node + checksum: 10c0/6fa22329c261371feb9560630d961ad0d0b9c87dce21ea74557c5f3ffbe5c1ee970ea8bcce9962ae9c90c3c47165ffa7dd41865c7414f5d8ea7a40755d612c5c + languageName: node + linkType: hard + +"superjson@npm:^1.13, superjson@npm:^1.13.3": + version: 1.13.3 + resolution: "superjson@npm:1.13.3" + dependencies: + copy-anything: "npm:^3.0.2" + checksum: 10c0/389a0a0c86884dd0558361af5d6d7f37102b71dda9595a665fe8b39d1ba0e57c859e39a9bd79b6f1fde6f4dcceac49a1c205f248d292744b2a340ee52846efdb + languageName: node + linkType: hard + +"supports-color@npm:^5.3.0": + version: 5.5.0 + resolution: "supports-color@npm:5.5.0" + dependencies: + has-flag: "npm:^3.0.0" + checksum: 10c0/6ae5ff319bfbb021f8a86da8ea1f8db52fac8bd4d499492e30ec17095b58af11f0c55f8577390a749b1c4dde691b6a0315dab78f5f54c9b3d83f8fb5905c1c05 + languageName: node + linkType: hard + +"supports-preserve-symlinks-flag@npm:^1.0.0": + version: 1.0.0 + resolution: "supports-preserve-symlinks-flag@npm:1.0.0" + checksum: 10c0/6c4032340701a9950865f7ae8ef38578d8d7053f5e10518076e6554a9381fa91bd9c6850193695c141f32b21f979c985db07265a758867bac95de05f7d8aeb39 + languageName: node + linkType: hard + +"tailwindcss-animate@npm:^1.0.7": + version: 1.0.7 + resolution: "tailwindcss-animate@npm:1.0.7" + peerDependencies: + tailwindcss: "*" + checksum: 10c0/ec7dbd1631076b97d66a1fbaaa06e0725fccfa63119221e8d87a997b02dcede98ad88bb1ef6665b968f5d260fcefb10592e0299ca70208d365b37761edf5e19a + languageName: node + linkType: hard + +"tailwindcss@npm:^3.4.13": + version: 3.4.19 + resolution: "tailwindcss@npm:3.4.19" + dependencies: + "@alloc/quick-lru": "npm:^5.2.0" + arg: "npm:^5.0.2" + chokidar: "npm:^3.6.0" + didyoumean: "npm:^1.2.2" + dlv: "npm:^1.1.3" + fast-glob: "npm:^3.3.2" + glob-parent: "npm:^6.0.2" + is-glob: "npm:^4.0.3" + jiti: "npm:^1.21.7" + lilconfig: "npm:^3.1.3" + micromatch: "npm:^4.0.8" + normalize-path: "npm:^3.0.0" + object-hash: "npm:^3.0.0" + picocolors: "npm:^1.1.1" + postcss: "npm:^8.4.47" + postcss-import: "npm:^15.1.0" + postcss-js: "npm:^4.0.1" + postcss-load-config: "npm:^4.0.2 || ^5.0 || ^6.0" + postcss-nested: "npm:^6.2.0" + postcss-selector-parser: "npm:^6.1.2" + resolve: "npm:^1.22.8" + sucrase: "npm:^3.35.0" + bin: + tailwind: lib/cli.js + tailwindcss: lib/cli.js + checksum: 10c0/e1063daccb9e5a508b357ec73b0011354204b2366b56496d6f0cc822733a55a0551502cb85856a2257ef9b676d0026616daaaa176d391f3216df57fbd693c581 + languageName: node + linkType: hard + +"tar@npm:^7.5.4": + version: 7.5.20 + resolution: "tar@npm:7.5.20" + dependencies: + "@isaacs/fs-minipass": "npm:^4.0.0" + chownr: "npm:^3.0.0" + minipass: "npm:^7.1.2" + minizlib: "npm:^3.1.0" + yallist: "npm:^5.0.0" + checksum: 10c0/4df4335c6d958b76adf1eaced55889dec3ca1c51f450658a074af80694bb0d9c154a8e93fdf4da617372d1575b121295379993961bbe4cd4b0867c0e5689846a + languageName: node + linkType: hard + +"thenify-all@npm:^1.0.0": + version: 1.6.0 + resolution: "thenify-all@npm:1.6.0" + dependencies: + thenify: "npm:>= 3.1.0 < 4" + checksum: 10c0/9b896a22735e8122754fe70f1d65f7ee691c1d70b1f116fda04fea103d0f9b356e3676cb789506e3909ae0486a79a476e4914b0f92472c2e093d206aed4b7d6b + languageName: node + linkType: hard + +"thenify@npm:>= 3.1.0 < 4": + version: 3.3.1 + resolution: "thenify@npm:3.3.1" + dependencies: + any-promise: "npm:^1.0.0" + checksum: 10c0/f375aeb2b05c100a456a30bc3ed07ef03a39cbdefe02e0403fb714b8c7e57eeaad1a2f5c4ecfb9ce554ce3db9c2b024eba144843cd9e344566d9fcee73b04767 + languageName: node + linkType: hard + +"tinybench@npm:^2.9.0": + version: 2.9.0 + resolution: "tinybench@npm:2.9.0" + checksum: 10c0/c3500b0f60d2eb8db65250afe750b66d51623057ee88720b7f064894a6cb7eb93360ca824a60a31ab16dab30c7b1f06efe0795b352e37914a9d4bad86386a20c + languageName: node + linkType: hard + +"tinyexec@npm:^0.3.2": + version: 0.3.2 + resolution: "tinyexec@npm:0.3.2" + checksum: 10c0/3efbf791a911be0bf0821eab37a3445c2ba07acc1522b1fa84ae1e55f10425076f1290f680286345ed919549ad67527d07281f1c19d584df3b74326909eb1f90 + languageName: node + linkType: hard + +"tinyglobby@npm:^0.2.11, tinyglobby@npm:^0.2.12, tinyglobby@npm:^0.2.14, tinyglobby@npm:^0.2.15": + version: 0.2.17 + resolution: "tinyglobby@npm:0.2.17" + dependencies: + fdir: "npm:^6.5.0" + picomatch: "npm:^4.0.4" + checksum: 10c0/7f7bb0f197c88bc4b20c231e0deca4240ca3bf313a88f5a7fee93a872b84966a4d50220947c0455ad07a60b3b360961c5b7fd979222aeb716a9f99b412002e4c + languageName: node + linkType: hard + +"tinypool@npm:^1.1.1": + version: 1.1.1 + resolution: "tinypool@npm:1.1.1" + checksum: 10c0/bf26727d01443061b04fa863f571016950888ea994ba0cd8cba3a1c51e2458d84574341ab8dbc3664f1c3ab20885c8cf9ff1cc4b18201f04c2cde7d317fff69b + languageName: node + linkType: hard + +"tinyrainbow@npm:^2.0.0": + version: 2.0.0 + resolution: "tinyrainbow@npm:2.0.0" + checksum: 10c0/c83c52bef4e0ae7fb8ec6a722f70b5b6fa8d8be1c85792e829f56c0e1be94ab70b293c032dc5048d4d37cfe678f1f5babb04bdc65fd123098800148ca989184f + languageName: node + linkType: hard + +"tinyspy@npm:^4.0.3": + version: 4.0.4 + resolution: "tinyspy@npm:4.0.4" + checksum: 10c0/a8020fc17799251e06a8398dcc352601d2770aa91c556b9531ecd7a12581161fd1c14e81cbdaff0c1306c93bfdde8ff6d1c1a3f9bbe6d91604f0fd4e01e2f1eb + languageName: node + linkType: hard + +"to-regex-range@npm:^5.0.1": + version: 5.0.1 + resolution: "to-regex-range@npm:5.0.1" + dependencies: + is-number: "npm:^7.0.0" + checksum: 10c0/487988b0a19c654ff3e1961b87f471702e708fa8a8dd02a298ef16da7206692e8552a0250e8b3e8759270f62e9d8314616f6da274734d3b558b1fc7b7724e892 + languageName: node + linkType: hard + +"toidentifier@npm:~1.0.1": + version: 1.0.1 + resolution: "toidentifier@npm:1.0.1" + checksum: 10c0/93937279934bd66cc3270016dd8d0afec14fb7c94a05c72dc57321f8bd1fa97e5bea6d1f7c89e728d077ca31ea125b78320a616a6c6cd0e6b9cb94cb864381c1 + languageName: node + linkType: hard + +"ts-interface-checker@npm:^0.1.9": + version: 0.1.13 + resolution: "ts-interface-checker@npm:0.1.13" + checksum: 10c0/232509f1b84192d07b81d1e9b9677088e590ac1303436da1e92b296e9be8e31ea042e3e1fd3d29b1742ad2c959e95afe30f63117b8f1bc3a3850070a5142fea7 + languageName: node + linkType: hard + +"tsx@npm:^4.19.2, tsx@npm:^4.21.0": + version: 4.23.1 + resolution: "tsx@npm:4.23.1" + dependencies: + esbuild: "npm:~0.28.0" + fsevents: "npm:~2.3.3" + dependenciesMeta: + fsevents: + optional: true + bin: + tsx: dist/cli.mjs + checksum: 10c0/2f7393c09ffd161701342c87141c6158945967c0fdb6476f157a72e53554cbc983b8e516b441beafde27c9840d8ffd619802acce6b6be5e9e197d693e111a55d + languageName: node + linkType: hard + +"turbo@npm:^2.1.2": + version: 2.10.5 + resolution: "turbo@npm:2.10.5" + dependencies: + "@turbo/darwin-64": "npm:2.10.5" + "@turbo/darwin-arm64": "npm:2.10.5" + "@turbo/linux-64": "npm:2.10.5" + "@turbo/linux-arm64": "npm:2.10.5" + "@turbo/windows-64": "npm:2.10.5" + "@turbo/windows-arm64": "npm:2.10.5" + dependenciesMeta: + "@turbo/darwin-64": + optional: true + "@turbo/darwin-arm64": + optional: true + "@turbo/linux-64": + optional: true + "@turbo/linux-arm64": + optional: true + "@turbo/windows-64": + optional: true + "@turbo/windows-arm64": + optional: true + bin: + turbo: bin/turbo + checksum: 10c0/081a8a4f4afe4026bde29ef9d0c98a1403437e9e72cd066973a82ec2f2482a1c6bd34973a997a23131520f37b9c063ba29a79583e18d0d70eeed8cd6baa1a003 + languageName: node + linkType: hard + +"type-fest@npm:^0.20.2": + version: 0.20.2 + resolution: "type-fest@npm:0.20.2" + checksum: 10c0/dea9df45ea1f0aaa4e2d3bed3f9a0bfe9e5b2592bddb92eb1bf06e50bcf98dbb78189668cd8bc31a0511d3fc25539b4cd5c704497e53e93e2d40ca764b10bfc3 + languageName: node + linkType: hard + +"type-is@npm:~1.6.18": + version: 1.6.18 + resolution: "type-is@npm:1.6.18" + dependencies: + media-typer: "npm:0.3.0" + mime-types: "npm:~2.1.24" + checksum: 10c0/a23daeb538591b7efbd61ecf06b6feb2501b683ffdc9a19c74ef5baba362b4347e42f1b4ed81f5882a8c96a3bfff7f93ce3ffaf0cbbc879b532b04c97a55db9d + languageName: node + linkType: hard + +"typed-array-buffer@npm:^1.0.3": + version: 1.0.3 + resolution: "typed-array-buffer@npm:1.0.3" + dependencies: + call-bound: "npm:^1.0.3" + es-errors: "npm:^1.3.0" + is-typed-array: "npm:^1.1.14" + checksum: 10c0/1105071756eb248774bc71646bfe45b682efcad93b55532c6ffa4518969fb6241354e4aa62af679ae83899ec296d69ef88f1f3763657cdb3a4d29321f7b83079 + languageName: node + linkType: hard + +"typed-array-byte-length@npm:^1.0.3": + version: 1.0.3 + resolution: "typed-array-byte-length@npm:1.0.3" + dependencies: + call-bind: "npm:^1.0.8" + for-each: "npm:^0.3.3" + gopd: "npm:^1.2.0" + has-proto: "npm:^1.2.0" + is-typed-array: "npm:^1.1.14" + checksum: 10c0/6ae083c6f0354f1fce18b90b243343b9982affd8d839c57bbd2c174a5d5dc71be9eb7019ffd12628a96a4815e7afa85d718d6f1e758615151d5f35df841ffb3e + languageName: node + linkType: hard + +"typed-array-byte-offset@npm:^1.0.4": + version: 1.0.4 + resolution: "typed-array-byte-offset@npm:1.0.4" + dependencies: + available-typed-arrays: "npm:^1.0.7" + call-bind: "npm:^1.0.8" + for-each: "npm:^0.3.3" + gopd: "npm:^1.2.0" + has-proto: "npm:^1.2.0" + is-typed-array: "npm:^1.1.15" + reflect.getprototypeof: "npm:^1.0.9" + checksum: 10c0/3d805b050c0c33b51719ee52de17c1cd8e6a571abdf0fffb110e45e8dd87a657e8b56eee94b776b13006d3d347a0c18a730b903cf05293ab6d92e99ff8f77e53 + languageName: node + linkType: hard + +"typed-array-length@npm:^1.0.7": + version: 1.0.8 + resolution: "typed-array-length@npm:1.0.8" + dependencies: + call-bind: "npm:^1.0.9" + for-each: "npm:^0.3.5" + gopd: "npm:^1.2.0" + is-typed-array: "npm:^1.1.15" + possible-typed-array-names: "npm:^1.1.0" + reflect.getprototypeof: "npm:^1.0.10" + checksum: 10c0/5319f740fc426a3217182c2f7c87656acb0903e046de5a938e30167337d26abf1bb3ad4b32833a72521a4cc58223aec80627b38b357d0a3d5fd64881427e77ab + languageName: node + linkType: hard + +"typescript@npm:^5.9.3": + version: 5.9.3 + resolution: "typescript@npm:5.9.3" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/6bd7552ce39f97e711db5aa048f6f9995b53f1c52f7d8667c1abdc1700c68a76a308f579cd309ce6b53646deb4e9a1be7c813a93baaf0a28ccd536a30270e1c5 + languageName: node + linkType: hard + +"typescript@patch:typescript@npm%3A^5.9.3#optional!builtin": + version: 5.9.3 + resolution: "typescript@patch:typescript@npm%3A5.9.3#optional!builtin::version=5.9.3&hash=8c6c40" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/6f7e53bf0d9702350deeb6f35e08b69cbc8b958c33e0ec77bdc0ad6a6c8e280f3959dcbfde6f5b0848bece57810696489deaaa53d75de3578ff255d168c1efbd + languageName: node + linkType: hard + +"unbox-primitive@npm:^1.1.0": + version: 1.1.0 + resolution: "unbox-primitive@npm:1.1.0" + dependencies: + call-bound: "npm:^1.0.3" + has-bigints: "npm:^1.0.2" + has-symbols: "npm:^1.1.0" + which-boxed-primitive: "npm:^1.1.1" + checksum: 10c0/7dbd35ab02b0e05fe07136c72cb9355091242455473ec15057c11430129bab38b7b3624019b8778d02a881c13de44d63cd02d122ee782fb519e1de7775b5b982 + languageName: node + linkType: hard + +"undici-types@npm:~6.21.0": + version: 6.21.0 + resolution: "undici-types@npm:6.21.0" + checksum: 10c0/c01ed51829b10aa72fc3ce64b747f8e74ae9b60eafa19a7b46ef624403508a54c526ffab06a14a26b3120d055e1104d7abe7c9017e83ced038ea5cf52f8d5e04 + languageName: node + linkType: hard + +"undici-types@npm:~8.3.0": + version: 8.3.0 + resolution: "undici-types@npm:8.3.0" + checksum: 10c0/c8aa7e2fbebfce519654dafadc0ece59be888d2ccaf180fb4495da875e7b536d2456345c384069c7e6f3e9c9ab7435f074957da306f142343eee86ff8048855a + languageName: node + linkType: hard + +"undici@npm:^8.4.1": + version: 8.7.0 + resolution: "undici@npm:8.7.0" + checksum: 10c0/b7e5ecb4de82fa4f905011a77544fe7ba4da06f27167ff99313a7ae2869f3cb233d676dcd085533bc69f36989bc40871f5ae6ea6e4c9b91db92616b9e91b579d + languageName: node + linkType: hard + +"unpipe@npm:~1.0.0": + version: 1.0.0 + resolution: "unpipe@npm:1.0.0" + checksum: 10c0/193400255bd48968e5c5383730344fbb4fa114cdedfab26e329e50dd2d81b134244bb8a72c6ac1b10ab0281a58b363d06405632c9d49ca9dfd5e90cbd7d0f32c + languageName: node + linkType: hard + +"update-browserslist-db@npm:^1.2.3": + version: 1.2.3 + resolution: "update-browserslist-db@npm:1.2.3" + dependencies: + escalade: "npm:^3.2.0" + picocolors: "npm:^1.1.1" + peerDependencies: + browserslist: ">= 4.21.0" + bin: + update-browserslist-db: cli.js + checksum: 10c0/13a00355ea822388f68af57410ce3255941d5fb9b7c49342c4709a07c9f230bbef7f7499ae0ca7e0de532e79a82cc0c4edbd125f1a323a1845bf914efddf8bec + languageName: node + linkType: hard + +"util-deprecate@npm:^1.0.2": + version: 1.0.2 + resolution: "util-deprecate@npm:1.0.2" + checksum: 10c0/41a5bdd214df2f6c3ecf8622745e4a366c4adced864bc3c833739791aeeeb1838119af7daed4ba36428114b5c67dcda034a79c882e97e43c03e66a4dd7389942 + languageName: node + linkType: hard + +"utils-merge@npm:1.0.1": + version: 1.0.1 + resolution: "utils-merge@npm:1.0.1" + checksum: 10c0/02ba649de1b7ca8854bfe20a82f1dfbdda3fb57a22ab4a8972a63a34553cf7aa51bc9081cf7e001b035b88186d23689d69e71b510e610a09a4c66f68aa95b672 + languageName: node + linkType: hard + +"valibot@npm:^1.2.0": + version: 1.4.2 + resolution: "valibot@npm:1.4.2" + peerDependencies: + typescript: ">=5" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/71b861de928fd5533b03fc4d84c619c07602f176d2bc6b12c5c80b2a6e889574c4126c843dd0771ce450bbb29566ca6df13d5b538ce5e4c39c2fccfb7623cf4e + languageName: node + linkType: hard + +"validate-npm-package-license@npm:^3.0.1": + version: 3.0.4 + resolution: "validate-npm-package-license@npm:3.0.4" + dependencies: + spdx-correct: "npm:^3.0.0" + spdx-expression-parse: "npm:^3.0.0" + checksum: 10c0/7b91e455a8de9a0beaa9fe961e536b677da7f48c9a493edf4d4d4a87fd80a7a10267d438723364e432c2fcd00b5650b5378275cded362383ef570276e6312f4f + languageName: node + linkType: hard + +"vary@npm:~1.1.2": + version: 1.1.2 + resolution: "vary@npm:1.1.2" + checksum: 10c0/f15d588d79f3675135ba783c91a4083dcd290a2a5be9fcb6514220a1634e23df116847b1cc51f66bfb0644cf9353b2abb7815ae499bab06e46dd33c1a6bf1f4f + languageName: node + linkType: hard + +"vite-node@npm:3.2.4, vite-node@npm:^3.2.2": + version: 3.2.4 + resolution: "vite-node@npm:3.2.4" + dependencies: + cac: "npm:^6.7.14" + debug: "npm:^4.4.1" + es-module-lexer: "npm:^1.7.0" + pathe: "npm:^2.0.3" + vite: "npm:^5.0.0 || ^6.0.0 || ^7.0.0-0" + bin: + vite-node: vite-node.mjs + checksum: 10c0/6ceca67c002f8ef6397d58b9539f80f2b5d79e103a18367288b3f00a8ab55affa3d711d86d9112fce5a7fa658a212a087a005a045eb8f4758947dd99af2a6c6b + languageName: node + linkType: hard + +"vite@npm:^5.0.0 || ^6.0.0 || ^7.0.0-0": + version: 7.3.6 + resolution: "vite@npm:7.3.6" + dependencies: + esbuild: "npm:^0.27.0 || ^0.28.0" + fdir: "npm:^6.5.0" + fsevents: "npm:~2.3.3" + picomatch: "npm:^4.0.3" + postcss: "npm:^8.5.6" + rollup: "npm:^4.43.0" + tinyglobby: "npm:^0.2.15" + peerDependencies: + "@types/node": ^20.19.0 || >=22.12.0 + jiti: ">=1.21.0" + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: ">=0.54.8" + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + dependenciesMeta: + fsevents: + optional: true + peerDependenciesMeta: + "@types/node": + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + bin: + vite: bin/vite.js + checksum: 10c0/c6d359f84ad362f5c97ff7988b77c90add04162c60cdf6059375a7d9e3217848c53a8cb6b6c48517bef84b6bba4c9bc85319a889b5236acb7d5a2bc8cb7b9a8d + languageName: node + linkType: hard + +"vite@npm:^5.4.11, vite@npm:^5.4.9": + version: 5.4.21 + resolution: "vite@npm:5.4.21" + dependencies: + esbuild: "npm:^0.21.3" + fsevents: "npm:~2.3.3" + postcss: "npm:^8.4.43" + rollup: "npm:^4.20.0" + peerDependencies: + "@types/node": ^18.0.0 || >=20.0.0 + less: "*" + lightningcss: ^1.21.0 + sass: "*" + sass-embedded: "*" + stylus: "*" + sugarss: "*" + terser: ^5.4.0 + dependenciesMeta: + fsevents: + optional: true + peerDependenciesMeta: + "@types/node": + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + bin: + vite: bin/vite.js + checksum: 10c0/468336a1409f728b464160cbf02672e72271fb688d0e605e776b74a89d27e1029509eef3a3a6c755928d8011e474dbf234824d054d07960be5f23cd176bc72de + languageName: node + linkType: hard + +"vitest@npm:^3.2.4": + version: 3.2.7 + resolution: "vitest@npm:3.2.7" + dependencies: + "@types/chai": "npm:^5.2.2" + "@vitest/expect": "npm:3.2.7" + "@vitest/mocker": "npm:3.2.7" + "@vitest/pretty-format": "npm:^3.2.7" + "@vitest/runner": "npm:3.2.7" + "@vitest/snapshot": "npm:3.2.7" + "@vitest/spy": "npm:3.2.7" + "@vitest/utils": "npm:3.2.7" + chai: "npm:^5.2.0" + debug: "npm:^4.4.1" + expect-type: "npm:^1.2.1" + magic-string: "npm:^0.30.17" + pathe: "npm:^2.0.3" + picomatch: "npm:^4.0.2" + std-env: "npm:^3.9.0" + tinybench: "npm:^2.9.0" + tinyexec: "npm:^0.3.2" + tinyglobby: "npm:^0.2.14" + tinypool: "npm:^1.1.1" + tinyrainbow: "npm:^2.0.0" + vite: "npm:^5.0.0 || ^6.0.0 || ^7.0.0-0" + vite-node: "npm:3.2.4" + why-is-node-running: "npm:^2.3.0" + peerDependencies: + "@edge-runtime/vm": "*" + "@types/debug": ^4.1.12 + "@types/node": ^18.0.0 || ^20.0.0 || >=22.0.0 + "@vitest/browser": 3.2.7 + "@vitest/ui": 3.2.7 + happy-dom: "*" + jsdom: "*" + peerDependenciesMeta: + "@edge-runtime/vm": + optional: true + "@types/debug": + optional: true + "@types/node": + optional: true + "@vitest/browser": + optional: true + "@vitest/ui": + optional: true + happy-dom: + optional: true + jsdom: + optional: true + bin: + vitest: ./vitest.mjs + checksum: 10c0/4eb7a63a1d62b88c425bcbc609835055a5644a1994d6f47605fe396dddf732e3c0277c9ec89a028fe81557dc4051dd15fc15b9eba6a51d1466cd5c682ddc1261 + languageName: node + linkType: hard + +"web-streams-polyfill@npm:^3.0.3": + version: 3.3.3 + resolution: "web-streams-polyfill@npm:3.3.3" + checksum: 10c0/64e855c47f6c8330b5436147db1c75cb7e7474d924166800e8e2aab5eb6c76aac4981a84261dd2982b3e754490900b99791c80ae1407a9fa0dcff74f82ea3a7f + languageName: node + linkType: hard + +"which-boxed-primitive@npm:^1.1.0, which-boxed-primitive@npm:^1.1.1": + version: 1.1.1 + resolution: "which-boxed-primitive@npm:1.1.1" + dependencies: + is-bigint: "npm:^1.1.0" + is-boolean-object: "npm:^1.2.1" + is-number-object: "npm:^1.1.1" + is-string: "npm:^1.1.1" + is-symbol: "npm:^1.1.1" + checksum: 10c0/aceea8ede3b08dede7dce168f3883323f7c62272b49801716e8332ff750e7ae59a511ae088840bc6874f16c1b7fd296c05c949b0e5b357bfe3c431b98c417abe + languageName: node + linkType: hard + +"which-builtin-type@npm:^1.2.1": + version: 1.2.1 + resolution: "which-builtin-type@npm:1.2.1" + dependencies: + call-bound: "npm:^1.0.2" + function.prototype.name: "npm:^1.1.6" + has-tostringtag: "npm:^1.0.2" + is-async-function: "npm:^2.0.0" + is-date-object: "npm:^1.1.0" + is-finalizationregistry: "npm:^1.1.0" + is-generator-function: "npm:^1.0.10" + is-regex: "npm:^1.2.1" + is-weakref: "npm:^1.0.2" + isarray: "npm:^2.0.5" + which-boxed-primitive: "npm:^1.1.0" + which-collection: "npm:^1.0.2" + which-typed-array: "npm:^1.1.16" + checksum: 10c0/8dcf323c45e5c27887800df42fbe0431d0b66b1163849bb7d46b5a730ad6a96ee8bfe827d078303f825537844ebf20c02459de41239a0a9805e2fcb3cae0d471 + languageName: node + linkType: hard + +"which-collection@npm:^1.0.2": + version: 1.0.2 + resolution: "which-collection@npm:1.0.2" + dependencies: + is-map: "npm:^2.0.3" + is-set: "npm:^2.0.3" + is-weakmap: "npm:^2.0.2" + is-weakset: "npm:^2.0.3" + checksum: 10c0/3345fde20964525a04cdf7c4a96821f85f0cc198f1b2ecb4576e08096746d129eb133571998fe121c77782ac8f21cbd67745a3d35ce100d26d4e684c142ea1f2 + languageName: node + linkType: hard + +"which-typed-array@npm:^1.1.16, which-typed-array@npm:^1.1.19": + version: 1.1.22 + resolution: "which-typed-array@npm:1.1.22" + dependencies: + available-typed-arrays: "npm:^1.0.7" + call-bind: "npm:^1.0.9" + call-bound: "npm:^1.0.4" + for-each: "npm:^0.3.5" + get-proto: "npm:^1.0.1" + gopd: "npm:^1.2.0" + has-tostringtag: "npm:^1.0.2" + checksum: 10c0/e59db184a4e78b461fac3b05fafc1e7badbbedafbf04a967ee1de73717f1f9723a79699e7b5de71d449541cb5da8353efc01a4f8a72a152479850e54fa196c40 + languageName: node + linkType: hard + +"which@npm:^1.2.9": + version: 1.3.1 + resolution: "which@npm:1.3.1" + dependencies: + isexe: "npm:^2.0.0" + bin: + which: ./bin/which + checksum: 10c0/e945a8b6bbf6821aaaef7f6e0c309d4b615ef35699576d5489b4261da9539f70393c6b2ce700ee4321c18f914ebe5644bc4631b15466ffbaad37d83151f6af59 + languageName: node + linkType: hard + +"which@npm:^7.0.0": + version: 7.0.0 + resolution: "which@npm:7.0.0" + dependencies: + isexe: "npm:^4.0.0" + bin: + node-which: bin/which.js + checksum: 10c0/ca0b54f198f78bbc4b7c02e34bda8d335cb352e0adb4cbca1c37b1a957af3a879a82c4c27ca6525bc942f548d8b64f816ef6528360af9f3de55ffb9b979b620d + languageName: node + linkType: hard + +"why-is-node-running@npm:^2.3.0": + version: 2.3.0 + resolution: "why-is-node-running@npm:2.3.0" + dependencies: + siginfo: "npm:^2.0.0" + stackback: "npm:0.0.2" + bin: + why-is-node-running: cli.js + checksum: 10c0/1cde0b01b827d2cf4cb11db962f3958b9175d5d9e7ac7361d1a7b0e2dc6069a263e69118bd974c4f6d0a890ef4eedfe34cf3d5167ec14203dbc9a18620537054 + languageName: node + linkType: hard + +"ws@npm:^8.18.0, ws@npm:^8.18.1": + version: 8.21.1 + resolution: "ws@npm:8.21.1" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 10c0/c4c6f1d95f6d465262de2037c57c715725d67e078dd49420ede4e19115668aba159cb64d85c5e89c06eb2826be599e62e5095860ad6cc54ff42e8bd7684e1db8 + languageName: node + linkType: hard + +"xtend@npm:^4.0.0": + version: 4.0.2 + resolution: "xtend@npm:4.0.2" + checksum: 10c0/366ae4783eec6100f8a02dff02ac907bf29f9a00b82ac0264b4d8b832ead18306797e283cf19de776538babfdcb2101375ec5646b59f08c52128ac4ab812ed0e + languageName: node + linkType: hard + +"yallist@npm:^3.0.2": + version: 3.1.1 + resolution: "yallist@npm:3.1.1" + checksum: 10c0/c66a5c46bc89af1625476f7f0f2ec3653c1a1791d2f9407cfb4c2ba812a1e1c9941416d71ba9719876530e3340a99925f697142989371b72d93b9ee628afd8c1 + languageName: node + linkType: hard + +"yallist@npm:^5.0.0": + version: 5.0.0 + resolution: "yallist@npm:5.0.0" + checksum: 10c0/a499c81ce6d4a1d260d4ea0f6d49ab4da09681e32c3f0472dee16667ed69d01dae63a3b81745a24bd78476ec4fcf856114cb4896ace738e01da34b2c42235416 + languageName: node + linkType: hard + +"yaml@npm:^2.8.3": + version: 2.9.0 + resolution: "yaml@npm:2.9.0" + bin: + yaml: bin.mjs + checksum: 10c0/f340718df45e97a9551b9bf9dac61c80050bc464513b710debfb5067c380c8472e3b67809cffacb4ab5ffb5e66ef9310816c88b05f371cec60abfedd8c88e0a2 + languageName: node + linkType: hard + +"zod@npm:^3.25.0": + version: 3.25.76 + resolution: "zod@npm:3.25.76" + checksum: 10c0/5718ec35e3c40b600316c5b4c5e4976f7fee68151bc8f8d90ec18a469be9571f072e1bbaace10f1e85cf8892ea12d90821b200e980ab46916a6166a4260a983c + languageName: node + linkType: hard