Migrate stack to SQLite and set Coolify defaults

This commit is contained in:
2026-02-26 15:45:22 -05:00
parent d1df80dfc2
commit f2ac1c426e
18 changed files with 1266 additions and 2218 deletions

View File

@@ -4,16 +4,13 @@ NEXT_PUBLIC_API_URL=
# Local docker host port (used by docker-compose.override.yml)
APP_PORT=3000
# Better Auth / PostgreSQL
# For app running outside Docker, localhost is typical.
# In Docker Compose deployment, default internal host is: postgres
DATABASE_URL=postgres://postgres:postgres@localhost:5432/fiscal_clone
POSTGRES_DB=fiscal_clone
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
# Better Auth / SQLite
# For app running outside Docker, this resolves to ./data/fiscal.sqlite
# In Docker Compose deployment, default path is /app/data/fiscal.sqlite
DATABASE_URL=file:data/fiscal.sqlite
BETTER_AUTH_SECRET=replace-with-a-long-random-secret
BETTER_AUTH_BASE_URL=http://localhost:3000
BETTER_AUTH_TRUSTED_ORIGINS=http://localhost:3000
BETTER_AUTH_BASE_URL=https://fiscal.b11studio.xyz
BETTER_AUTH_TRUSTED_ORIGINS=https://fiscal.b11studio.xyz
# OpenClaw / ZeroClaw (OpenAI-compatible)
OPENCLAW_BASE_URL=http://localhost:4000
@@ -23,8 +20,7 @@ OPENCLAW_MODEL=zeroclaw
# SEC API etiquette
SEC_USER_AGENT=Fiscal Clone <support@fiscal.local>
# Workflow runtime (Postgres world)
WORKFLOW_TARGET_WORLD=@workflow/world-postgres
WORKFLOW_POSTGRES_URL=postgres://postgres:postgres@localhost:5432/fiscal_clone
WORKFLOW_POSTGRES_JOB_PREFIX=fiscal_clone
WORKFLOW_POSTGRES_WORKER_CONCURRENCY=10
# Workflow runtime (Local world)
WORKFLOW_TARGET_WORLD=local
WORKFLOW_LOCAL_DATA_DIR=.workflow-data
WORKFLOW_LOCAL_QUEUE_CONCURRENCY=100

1
.gitignore vendored
View File

@@ -39,3 +39,4 @@ out/
# Local app runtime state
data/*.json
.workflow-data/

View File

@@ -5,7 +5,7 @@ RUN bun install --frozen-lockfile
FROM deps AS builder
ARG NEXT_PUBLIC_API_URL=
ARG DATABASE_URL=postgres://postgres:postgres@localhost:5432/fiscal_clone
ARG DATABASE_URL=file:data/fiscal.sqlite
ENV NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}
ENV DATABASE_URL=${DATABASE_URL}
ENV NEXT_TELEMETRY_DISABLED=1
@@ -29,8 +29,10 @@ COPY --from=deps /app/node_modules ./node_modules
COPY --from=deps /app/package.json ./package.json
COPY --from=deps /app/bun.lock ./bun.lock
RUN mkdir -p /app/data /app/.workflow-data
EXPOSE 3000
ENV PORT=3000
CMD ["sh", "-c", "./node_modules/.bin/drizzle-kit migrate --config /app/drizzle.config.ts && ./node_modules/.bin/workflow-postgres-setup && bun server.js"]
CMD ["sh", "-c", "./node_modules/.bin/drizzle-kit migrate --config /app/drizzle.config.ts && bun server.js"]

View File

@@ -9,30 +9,25 @@ Turbopack-first rebuild of a fiscal.ai-style terminal with OpenClaw integration.
- Elysia route layer mounted in Next Route Handlers
- Turbopack for `dev` and `build`
- Better Auth (email/password + magic link)
- Drizzle ORM (PostgreSQL) + Better Auth Drizzle adapter
- Drizzle ORM (SQLite) + Better Auth Drizzle adapter
- Internal API routes via Elysia app module (`lib/server/api/app.ts`)
- Eden Treaty for type-safe frontend API calls
- Workflow DevKit + Postgres World for durable background task execution
- PostgreSQL-backed domain storage (watchlist, holdings, filings, tasks, insights)
- Workflow DevKit Local World for background task execution
- SQLite-backed domain storage (watchlist, holdings, filings, tasks, insights)
- OpenClaw/ZeroClaw analysis via OpenAI-compatible chat endpoint
## Run locally
```bash
bun install
bun run db:generate
bun run db:migrate
bun run dev
```
Open [http://localhost:3000](http://localhost:3000).
Better Auth requires PostgreSQL. Set `DATABASE_URL`, `BETTER_AUTH_SECRET`, and `BETTER_AUTH_BASE_URL` in `.env.local`.
Generate/apply schema migrations and set up the workflow world tables before starting the app:
```bash
bun run db:generate
bun run db:migrate
bun run workflow:setup
```
The default database path is `data/fiscal.sqlite` via `DATABASE_URL=file:data/fiscal.sqlite`.
## Production build
@@ -50,12 +45,32 @@ docker compose up --build -d
```
For local Docker, host port mapping comes from `docker-compose.override.yml` (default `http://localhost:3000`, configurable via `APP_PORT`).
The base Docker Compose now includes an internal PostgreSQL service (`postgres`) used by Better Auth by default.
On container startup, the app now applies Drizzle migrations automatically before launching Next.js.
For Coolify/remote Docker Compose, only app container port `3000` is exposed internally (no fixed host port bind), avoiding host port collisions.
If you use an external Postgres instance, set `DATABASE_URL` explicitly.
On container startup, the app applies Drizzle migrations automatically before launching Next.js.
The app stores SQLite data in Docker volume `fiscal_sqlite_data` (mounted to `/app/data`) and workflow local data in `fiscal_workflow_data` (mounted to `/app/.workflow-data`).
Workflow Local World uses filesystem state plus an in-memory queue. On container restart, queued in-flight jobs may be lost.
Docker images use Bun (`oven/bun:1.3.5-alpine`) for build and runtime.
## Coolify deployment
This compose setup is compatible with Coolify as-is (it uses named Docker volumes, not host bind mounts).
Required environment variables in Coolify:
- `DATABASE_URL=file:/app/data/fiscal.sqlite`
- `BETTER_AUTH_SECRET=<long-random-secret>`
- `BETTER_AUTH_BASE_URL=https://fiscal.b11studio.xyz`
- `BETTER_AUTH_TRUSTED_ORIGINS=https://fiscal.b11studio.xyz`
- `WORKFLOW_TARGET_WORLD=local`
- Optional: `WORKFLOW_LOCAL_DATA_DIR=/app/.workflow-data`
Operational constraints for Coolify:
- Keep this service to a single instance/replica. SQLite is file-based and not appropriate for multi-replica shared-write deployments.
- Ensure the two named volumes are persisted (`fiscal_sqlite_data`, `fiscal_workflow_data`).
- Workflow Local queueing is in-memory; in-flight queued jobs may be lost on restarts.
## Environment
Use root `.env` or root `.env.local`:
@@ -63,23 +78,19 @@ Use root `.env` or root `.env.local`:
```env
# leave blank for same-origin API
NEXT_PUBLIC_API_URL=
DATABASE_URL=postgres://postgres:postgres@localhost:5432/fiscal_clone
POSTGRES_DB=fiscal_clone
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
DATABASE_URL=file:data/fiscal.sqlite
BETTER_AUTH_SECRET=replace-with-a-long-random-secret
BETTER_AUTH_BASE_URL=http://localhost:3000
BETTER_AUTH_TRUSTED_ORIGINS=http://localhost:3000
BETTER_AUTH_BASE_URL=https://fiscal.b11studio.xyz
BETTER_AUTH_TRUSTED_ORIGINS=https://fiscal.b11studio.xyz
OPENCLAW_BASE_URL=http://localhost:4000
OPENCLAW_API_KEY=your_key
OPENCLAW_MODEL=zeroclaw
SEC_USER_AGENT=Fiscal Clone <support@fiscal.local>
WORKFLOW_TARGET_WORLD=@workflow/world-postgres
WORKFLOW_POSTGRES_URL=postgres://postgres:postgres@localhost:5432/fiscal_clone
WORKFLOW_POSTGRES_JOB_PREFIX=fiscal_clone
WORKFLOW_POSTGRES_WORKER_CONCURRENCY=10
WORKFLOW_TARGET_WORLD=local
WORKFLOW_LOCAL_DATA_DIR=.workflow-data
WORKFLOW_LOCAL_QUEUE_CONCURRENCY=100
```
If OpenClaw is unset, the app uses local fallback analysis so task workflows still run.

119
bun.lock
View File

@@ -6,8 +6,8 @@
"name": "fiscal-frontend",
"dependencies": {
"@elysiajs/eden": "^1.4.8",
"@libsql/client": "^0.17.0",
"@tailwindcss/postcss": "^4.2.1",
"@workflow/world-postgres": "^4.1.0-beta.36",
"better-auth": "^1.4.19",
"clsx": "^2.1.1",
"date-fns": "^4.1.0",
@@ -15,7 +15,6 @@
"elysia": "latest",
"lucide-react": "^0.575.0",
"next": "^16.1.6",
"pg": "^8.18.0",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"recharts": "^3.7.0",
@@ -23,10 +22,10 @@
},
"devDependencies": {
"@types/node": "^25.3.0",
"@types/pg": "^8.16.0",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"autoprefixer": "^10.4.24",
"bun-types": "^1.3.10",
"drizzle-kit": "^0.31.4",
"postcss": "^8.5.6",
"tailwindcss": "^4.2.1",
@@ -241,6 +240,32 @@
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
"@libsql/client": ["@libsql/client@0.17.0", "", { "dependencies": { "@libsql/core": "^0.17.0", "@libsql/hrana-client": "^0.9.0", "js-base64": "^3.7.5", "libsql": "^0.5.22", "promise-limit": "^2.7.0" } }, "sha512-TLjSU9Otdpq0SpKHl1tD1Nc9MKhrsZbCFGot3EbCxRa8m1E5R1mMwoOjKMMM31IyF7fr+hPNHLpYfwbMKNusmg=="],
"@libsql/core": ["@libsql/core@0.17.0", "", { "dependencies": { "js-base64": "^3.7.5" } }, "sha512-hnZRnJHiS+nrhHKLGYPoJbc78FE903MSDrFJTbftxo+e52X+E0Y0fHOCVYsKWcg6XgB7BbJYUrz/xEkVTSaipw=="],
"@libsql/darwin-arm64": ["@libsql/darwin-arm64@0.5.22", "", { "os": "darwin", "cpu": "arm64" }, "sha512-4B8ZlX3nIDPndfct7GNe0nI3Yw6ibocEicWdC4fvQbSs/jdq/RC2oCsoJxJ4NzXkvktX70C1J4FcmmoBy069UA=="],
"@libsql/darwin-x64": ["@libsql/darwin-x64@0.5.22", "", { "os": "darwin", "cpu": "x64" }, "sha512-ny2HYWt6lFSIdNFzUFIJ04uiW6finXfMNJ7wypkAD8Pqdm6nAByO+Fdqu8t7sD0sqJGeUCiOg480icjyQ2/8VA=="],
"@libsql/hrana-client": ["@libsql/hrana-client@0.9.0", "", { "dependencies": { "@libsql/isomorphic-ws": "^0.1.5", "cross-fetch": "^4.0.0", "js-base64": "^3.7.5", "node-fetch": "^3.3.2" } }, "sha512-pxQ1986AuWfPX4oXzBvLwBnfgKDE5OMhAdR/5cZmRaB4Ygz5MecQybvwZupnRz341r2CtFmbk/BhSu7k2Lm+Jw=="],
"@libsql/isomorphic-ws": ["@libsql/isomorphic-ws@0.1.5", "", { "dependencies": { "@types/ws": "^8.5.4", "ws": "^8.13.0" } }, "sha512-DtLWIH29onUYR00i0GlQ3UdcTRC6EP4u9w/h9LxpUZJWRMARk6dQwZ6Jkd+QdwVpuAOrdxt18v0K2uIYR3fwFg=="],
"@libsql/linux-arm-gnueabihf": ["@libsql/linux-arm-gnueabihf@0.5.22", "", { "os": "linux", "cpu": "arm" }, "sha512-3Uo3SoDPJe/zBnyZKosziRGtszXaEtv57raWrZIahtQDsjxBVjuzYQinCm9LRCJCUT5t2r5Z5nLDPJi2CwZVoA=="],
"@libsql/linux-arm-musleabihf": ["@libsql/linux-arm-musleabihf@0.5.22", "", { "os": "linux", "cpu": "arm" }, "sha512-LCsXh07jvSojTNJptT9CowOzwITznD+YFGGW+1XxUr7fS+7/ydUrpDfsMX7UqTqjm7xG17eq86VkWJgHJfvpNg=="],
"@libsql/linux-arm64-gnu": ["@libsql/linux-arm64-gnu@0.5.22", "", { "os": "linux", "cpu": "arm64" }, "sha512-KSdnOMy88c9mpOFKUEzPskSaF3VLflfSUCBwas/pn1/sV3pEhtMF6H8VUCd2rsedwoukeeCSEONqX7LLnQwRMA=="],
"@libsql/linux-arm64-musl": ["@libsql/linux-arm64-musl@0.5.22", "", { "os": "linux", "cpu": "arm64" }, "sha512-mCHSMAsDTLK5YH//lcV3eFEgiR23Ym0U9oEvgZA0667gqRZg/2px+7LshDvErEKv2XZ8ixzw3p1IrBzLQHGSsw=="],
"@libsql/linux-x64-gnu": ["@libsql/linux-x64-gnu@0.5.22", "", { "os": "linux", "cpu": "x64" }, "sha512-kNBHaIkSg78Y4BqAdgjcR2mBilZXs4HYkAmi58J+4GRwDQZh5fIUWbnQvB9f95DkWUIGVeenqLRFY2pcTmlsew=="],
"@libsql/linux-x64-musl": ["@libsql/linux-x64-musl@0.5.22", "", { "os": "linux", "cpu": "x64" }, "sha512-UZ4Xdxm4pu3pQXjvfJiyCzZop/9j/eA2JjmhMaAhe3EVLH2g11Fy4fwyUp9sT1QJYR1kpc2JLuybPM0kuXv/Tg=="],
"@libsql/win32-x64-msvc": ["@libsql/win32-x64-msvc@0.5.22", "", { "os": "win32", "cpu": "x64" }, "sha512-Fj0j8RnBpo43tVZUVoNK6BV/9AtDUM5S7DF3LB4qTYg1LMSZqi3yeCneUTLJD6XomQJlZzbI4mst89yspVSAnA=="],
"@lukeed/csprng": ["@lukeed/csprng@1.1.0", "", {}, "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA=="],
"@mjackson/node-fetch-server": ["@mjackson/node-fetch-server@0.2.0", "", {}, "sha512-EMlH1e30yzmTpGLQjlFmaDAjyOeZhng1/XCd7DExR8PNAnG/G1tyruZxEoUe11ClnwGhGrtsdnyyUx1frSzjng=="],
@@ -281,6 +306,8 @@
"@napi-rs/nice-win32-x64-msvc": ["@napi-rs/nice-win32-x64-msvc@1.1.1", "", { "os": "win32", "cpu": "x64" }, "sha512-vB+4G/jBQCAh0jelMTY3+kgFy00Hlx2f2/1zjMoH821IbplbWZOkLiTYXQkygNTzQJTq5cvwBDgn2ppHD+bglQ=="],
"@neon-rs/load": ["@neon-rs/load@0.0.4", "", {}, "sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw=="],
"@nestjs/common": ["@nestjs/common@11.1.14", "", { "dependencies": { "file-type": "21.3.0", "iterare": "1.2.1", "load-esm": "1.0.3", "tslib": "2.8.1", "uid": "2.0.2" }, "peerDependencies": { "class-transformer": ">=0.4.1", "class-validator": ">=0.13.2", "reflect-metadata": "^0.1.12 || ^0.2.0", "rxjs": "^7.1.0" }, "optionalPeers": ["class-transformer", "class-validator"] }, "sha512-IN/tlqd7Nl9gl6f0jsWEuOrQDaCI9vHzxv0fisHysfBQzfQIkqlv5A7w4Qge02BUQyczXT9HHPgHtWHCxhjRng=="],
"@nestjs/core": ["@nestjs/core@11.1.14", "", { "dependencies": { "@nuxt/opencollective": "0.4.1", "fast-safe-stringify": "2.1.1", "iterare": "1.2.1", "path-to-regexp": "8.3.0", "tslib": "2.8.1", "uid": "2.0.2" }, "peerDependencies": { "@nestjs/common": "^11.0.0", "@nestjs/microservices": "^11.0.0", "@nestjs/platform-express": "^11.0.0", "@nestjs/websockets": "^11.0.0", "reflect-metadata": "^0.1.12 || ^0.2.0", "rxjs": "^7.1.0" }, "optionalPeers": ["@nestjs/microservices", "@nestjs/platform-express", "@nestjs/websockets"] }, "sha512-7OXPPMoDr6z+5NkoQKu4hOhfjz/YYqM3bNilPqv1WVFWrzSmuNXxvhbX69YMmNmRYascPXiwESqf5jJdjKXEww=="],
@@ -513,11 +540,13 @@
"@types/use-sync-external-store": ["@types/use-sync-external-store@0.0.6", "", {}, "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg=="],
"@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
"@vercel/cli-auth": ["@vercel/cli-auth@0.0.1", "", { "dependencies": { "async-listen": "3.0.0", "open": "8.4.0", "xdg-app-paths": "5", "zod": "4.1.11" } }, "sha512-CnqiuMlZ4pjs2LCPYiR6aLKPPd3Xb8SBI1Y7eotXKgpx6qgrGNY+E7EIyUt5ErGHJGIrCZyGG5WEo4bHtVmz2Q=="],
"@vercel/functions": ["@vercel/functions@3.4.3", "", { "dependencies": { "@vercel/oidc": "3.2.0" }, "peerDependencies": { "@aws-sdk/credential-provider-web-identity": "*" }, "optionalPeers": ["@aws-sdk/credential-provider-web-identity"] }, "sha512-kA14KIUVgAY6VXbhZ5jjY+s0883cV3cZqIU3WhrSRxuJ9KvxatMjtmzl0K23HK59oOUjYl7HaE/eYMmhmqpZzw=="],
"@vercel/oidc": ["@vercel/oidc@3.2.0", "", {}, "sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug=="],
"@vercel/oidc": ["@vercel/oidc@3.0.5", "", {}, "sha512-fnYhv671l+eTTp48gB4zEsTW/YtRgRPnkI2nT7x6qw5rkI1Lq2hTmQIpHPgyThI0znLK+vX2n9XxKdXZ7BUbbw=="],
"@vercel/queue": ["@vercel/queue@0.0.0-alpha.38", "", { "dependencies": { "@vercel/oidc": "^3.0.5", "mixpart": "0.0.5-alpha.1" } }, "sha512-gSYpZrYy1LpzfXqDMUZX7xEIQxyhelvMDgthxijs495kHIxWC65S0C3vaAw5+3c1ujbPeJgLz8fn3SDTJspssw=="],
@@ -559,8 +588,6 @@
"@workflow/world-local": ["@workflow/world-local@4.1.0-beta.34", "", { "dependencies": { "@vercel/queue": "0.0.0-alpha.38", "@workflow/errors": "4.1.0-beta.16", "@workflow/utils": "4.1.0-beta.12", "@workflow/world": "4.1.0-beta.6", "async-sema": "3.1.1", "ulid": "3.0.1", "undici": "6.22.0", "zod": "4.1.11" }, "peerDependencies": { "@opentelemetry/api": "1" }, "optionalPeers": ["@opentelemetry/api"] }, "sha512-ZwIWBs4m/1HNy8PeP0h63Q47Sx1XragGzTF+saiHiCZP5sLZJz8veOrXhJ7tqdJBoIhp3pm5GsTes/NpGYXClg=="],
"@workflow/world-postgres": ["@workflow/world-postgres@4.1.0-beta.36", "", { "dependencies": { "@vercel/queue": "0.0.0-alpha.38", "@workflow/errors": "4.1.0-beta.16", "@workflow/world": "4.1.0-beta.6", "@workflow/world-local": "4.1.0-beta.34", "cbor-x": "1.6.0", "dotenv": "16.4.5", "drizzle-orm": "0.44.7", "pg-boss": "11.0.7", "postgres": "3.4.7", "ulid": "3.0.1", "zod": "4.1.11" }, "bin": { "workflow-postgres-setup": "bin/setup.js" } }, "sha512-Dwc6MRwS5FaLWUwApyzemqvGD3fIw1axnvpaQCwADs73GJ7s5LWvctK5kNobCgF04ZYyF4IhqbY1T+r8P04Uaw=="],
"@workflow/world-vercel": ["@workflow/world-vercel@4.1.0-beta.34", "", { "dependencies": { "@vercel/oidc": "3.0.5", "@vercel/queue": "0.0.0-alpha.38", "@workflow/errors": "4.1.0-beta.16", "@workflow/world": "4.1.0-beta.6", "cbor-x": "1.6.0", "zod": "4.1.11" }, "peerDependencies": { "@opentelemetry/api": "1" }, "optionalPeers": ["@opentelemetry/api"] }, "sha512-1BmBZ4MsuvcHCk0sOL1lX5JyxH3qs0oB1ZeWaKykJWhFn2Cl6t2fLBSl9lbBLDTwHMiK7caowGPnzH2Yxw4Etg=="],
"@xhmikosr/archive-type": ["@xhmikosr/archive-type@7.1.0", "", { "dependencies": { "file-type": "^20.5.0" } }, "sha512-xZEpnGplg1sNPyEgFh0zbHxqlw5dtYg6viplmWSxUj12+QjU9SKu3U/2G73a15pEjLaOqTefNSZ1fOPUOT4Xgg=="],
@@ -651,6 +678,8 @@
"builtin-modules": ["builtin-modules@5.0.0", "", {}, "sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg=="],
"bun-types": ["bun-types@1.3.10", "", { "dependencies": { "@types/node": "*" } }, "sha512-tcpfCCl6XWo6nCVnpcVrxQ+9AYN1iqMIzgrSKYMB/fjLtV2eyAVEg7AxQJuCq/26R6HpKWykQXuSOq/21RYcbg=="],
"bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="],
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
@@ -715,7 +744,7 @@
"cosmiconfig": ["cosmiconfig@9.0.0", "", { "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0" }, "peerDependencies": { "typescript": ">=4.9.5" }, "optionalPeers": ["typescript"] }, "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg=="],
"cron-parser": ["cron-parser@5.5.0", "", { "dependencies": { "luxon": "^3.7.1" } }, "sha512-oML4lKUXxizYswqmxuOCpgFS8BNUJpIu6k/2HVHyaL8Ynnf3wdf9tkns0yRdJLSIjkJ+b0DXHMZEHGpMwjnPww=="],
"cross-fetch": ["cross-fetch@4.1.0", "", { "dependencies": { "node-fetch": "^2.7.0" } }, "sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw=="],
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
@@ -743,6 +772,8 @@
"d3-timer": ["d3-timer@3.0.1", "", {}, "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="],
"data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="],
"date-fns": ["date-fns@4.1.0", "", {}, "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg=="],
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
@@ -769,13 +800,13 @@
"destroy": ["destroy@1.2.0", "", {}, "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="],
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
"detect-libc": ["detect-libc@2.0.2", "", {}, "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw=="],
"devalue": ["devalue@5.6.0", "", {}, "sha512-BaD1s81TFFqbD6Uknni42TrolvEWA1Ih5L+OiHWmi4OYMJVwAYPGtha61I9KxTf52OvVHozHyjPu8zljqdF3uA=="],
"dir-glob": ["dir-glob@3.0.1", "", { "dependencies": { "path-type": "^4.0.0" } }, "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA=="],
"dotenv": ["dotenv@16.4.5", "", {}, "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg=="],
"dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="],
"drizzle-kit": ["drizzle-kit@0.31.9", "", { "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", "esbuild": "^0.25.4", "esbuild-register": "^3.5.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-GViD3IgsXn7trFyBUUHyTFBpH/FsHTxYJ66qdbVggxef4UBPHRYxQaRzYLTuekYnk9i5FIEL9pbBIwMqX/Uwrg=="],
@@ -859,6 +890,8 @@
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
"fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="],
"fflate": ["fflate@0.8.2", "", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="],
"file-type": ["file-type@21.3.0", "", { "dependencies": { "@tokenizer/inflate": "^0.4.1", "strtok3": "^10.3.4", "token-types": "^6.1.1", "uint8array-extras": "^1.4.0" } }, "sha512-8kPJMIGz1Yt/aPEwOsrR97ZyZaD1Iqm8PClb1nYFclUCkBi0Ma5IsYNQzvSFS9ib51lWyIw5mIT9rWzI/xjpzA=="],
@@ -879,6 +912,8 @@
"form-data-encoder": ["form-data-encoder@2.1.4", "", {}, "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw=="],
"formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="],
"forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
"fraction.js": ["fraction.js@5.3.4", "", {}, "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ=="],
@@ -985,6 +1020,8 @@
"jose": ["jose@6.1.3", "", {}, "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ=="],
"js-base64": ["js-base64@3.7.8", "", {}, "sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow=="],
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
"js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
@@ -1007,6 +1044,8 @@
"kysely": ["kysely@0.28.11", "", {}, "sha512-zpGIFg0HuoC893rIjYX1BETkVWdDnzTzF5e0kWXJFg5lE0k1/LfNWBejrcnOFu8Q2Rfq/hTDTU7XLUM8QOrpzg=="],
"libsql": ["libsql@0.5.22", "", { "dependencies": { "@neon-rs/load": "^0.0.4", "detect-libc": "2.0.2" }, "optionalDependencies": { "@libsql/darwin-arm64": "0.5.22", "@libsql/darwin-x64": "0.5.22", "@libsql/linux-arm-gnueabihf": "0.5.22", "@libsql/linux-arm-musleabihf": "0.5.22", "@libsql/linux-arm64-gnu": "0.5.22", "@libsql/linux-arm64-musl": "0.5.22", "@libsql/linux-x64-gnu": "0.5.22", "@libsql/linux-x64-musl": "0.5.22", "@libsql/win32-x64-msvc": "0.5.22" }, "os": [ "linux", "win32", "darwin", ], "cpu": [ "arm", "x64", "arm64", ] }, "sha512-NscWthMQt7fpU8lqd7LXMvT9pi+KhhmTHAJWUB/Lj6MWa0MKFv0F2V4C6WKKpjCVZl0VwcDz4nOI3CyaT1DDiA=="],
"lightningcss": ["lightningcss@1.31.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.31.1", "lightningcss-darwin-arm64": "1.31.1", "lightningcss-darwin-x64": "1.31.1", "lightningcss-freebsd-x64": "1.31.1", "lightningcss-linux-arm-gnueabihf": "1.31.1", "lightningcss-linux-arm64-gnu": "1.31.1", "lightningcss-linux-arm64-musl": "1.31.1", "lightningcss-linux-x64-gnu": "1.31.1", "lightningcss-linux-x64-musl": "1.31.1", "lightningcss-win32-arm64-msvc": "1.31.1", "lightningcss-win32-x64-msvc": "1.31.1" } }, "sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ=="],
"lightningcss-android-arm64": ["lightningcss-android-arm64@1.31.1", "", { "os": "android", "cpu": "arm64" }, "sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg=="],
@@ -1043,8 +1082,6 @@
"lucide-react": ["lucide-react@0.575.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-VuXgKZrk0uiDlWjGGXmKV6MSk9Yy4l10qgVvzGn2AWBx1Ylt0iBexKOAoA6I7JO3m+M9oeovJd3yYENfkUbOeg=="],
"luxon": ["luxon@3.7.2", "", {}, "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew=="],
"magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
@@ -1077,7 +1114,7 @@
"minimatch": ["minimatch@9.0.8", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-reYkDYtj/b19TeqbNZCV4q9t+Yxylf/rYBsLb42SXJatTv4/ylq5lEiAmhA/IToxO7NI2UzNMghHoHuaqDkAjw=="],
"mixpart": ["mixpart@0.0.5-alpha.1", "", {}, "sha512-2ZfG/NO2SVE9HLk1/W+yOrIOA0d674ljZExLdievZQpYjbJYQjIdye8vNMR63yF7nN/NbO9q8mp16JUEYBCilg=="],
"mixpart": ["mixpart@0.0.4", "", {}, "sha512-RAoaOSXnMLrfUfmFbNynRYjeMru/bhgAYRy/GQVI8gmRq7vm9V9c2gGVYnYoQ008X6YTmRIu5b0397U7vb0bIA=="],
"mlly": ["mlly@1.8.0", "", { "dependencies": { "acorn": "^8.15.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.1" } }, "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g=="],
@@ -1091,6 +1128,10 @@
"next": ["next@16.1.6", "", { "dependencies": { "@next/env": "16.1.6", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.1.6", "@next/swc-darwin-x64": "16.1.6", "@next/swc-linux-arm64-gnu": "16.1.6", "@next/swc-linux-arm64-musl": "16.1.6", "@next/swc-linux-x64-gnu": "16.1.6", "@next/swc-linux-x64-musl": "16.1.6", "@next/swc-win32-arm64-msvc": "16.1.6", "@next/swc-win32-x64-msvc": "16.1.6", "sharp": "^0.34.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": "dist/bin/next" }, "sha512-hkyRkcu5x/41KoqnROkfTm2pZVbKxvbZRuNvKXLRXxs3VfyO0WhY50TQS40EuKO9SW3rBj/sF3WbVwDACeMZyw=="],
"node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
"node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
"node-fetch-native": ["node-fetch-native@1.6.7", "", {}, "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q=="],
"node-gyp-build-optional-packages": ["node-gyp-build-optional-packages@5.1.1", "", { "dependencies": { "detect-libc": "^2.0.1" }, "bin": { "node-gyp-build-optional-packages": "bin.js", "node-gyp-build-optional-packages-test": "build-test.js", "node-gyp-build-optional-packages-optional": "optional.js" } }, "sha512-+P72GAjVAbTxjjwUmwjVrqrdZROD4nf8KgpBoDxqXXTiYZZt/ud60dE5yvCSr9lRO8e8yv6kgJIC0K0PfZFVQw=="],
@@ -1147,8 +1188,6 @@
"pg": ["pg@8.18.0", "", { "dependencies": { "pg-connection-string": "^2.11.0", "pg-pool": "^3.11.0", "pg-protocol": "^1.11.0", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "optionalDependencies": { "pg-cloudflare": "^1.3.0" }, "peerDependencies": { "pg-native": ">=3.0.1" }, "optionalPeers": ["pg-native"] }, "sha512-xqrUDL1b9MbkydY/s+VZ6v+xiMUmOUk7SS9d/1kpyQxoJ6U9AO1oIJyUWVZojbfe5Cc/oluutcgFG4L9RDP1iQ=="],
"pg-boss": ["pg-boss@11.0.7", "", { "dependencies": { "cron-parser": "^5.4.0", "pg": "^8.16.3", "serialize-error": "^8.1.0" } }, "sha512-UaCAE4u1bHBwV3yDl8q3qSPKglyRCz/e9wKSbrYsyqzxIkCQ3rNeUYHqX4DAPLxW96RCMifxtMOvq093IZNVug=="],
"pg-cloudflare": ["pg-cloudflare@1.3.0", "", {}, "sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ=="],
"pg-connection-string": ["pg-connection-string@2.11.0", "", {}, "sha512-kecgoJwhOpxYU21rZjULrmrBJ698U2RxXofKVzOn5UDj61BPj/qMb7diYUR1nLScCDbrztQFl1TaQZT0t1EtzQ=="],
@@ -1185,6 +1224,8 @@
"postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="],
"promise-limit": ["promise-limit@2.7.0", "", {}, "sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw=="],
"proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
"qs": ["qs@6.14.2", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q=="],
@@ -1261,8 +1302,6 @@
"send": ["send@0.19.2", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "~0.5.2", "http-errors": "~2.0.1", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "~2.4.1", "range-parser": "~1.2.1", "statuses": "~2.0.2" } }, "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg=="],
"serialize-error": ["serialize-error@8.1.0", "", { "dependencies": { "type-fest": "^0.20.2" } }, "sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ=="],
"serve-static": ["serve-static@1.16.3", "", { "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "~0.19.1" } }, "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA=="],
"set-cookie-parser": ["set-cookie-parser@2.7.2", "", {}, "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw=="],
@@ -1347,9 +1386,11 @@
"token-types": ["token-types@6.1.2", "", { "dependencies": { "@borewit/text-codec": "^0.2.1", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww=="],
"tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"type-fest": ["type-fest@0.20.2", "", {}, "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ=="],
"type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="],
"type-is": ["type-is@1.6.18", "", { "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" } }, "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g=="],
@@ -1395,8 +1436,14 @@
"wcwidth": ["wcwidth@1.0.1", "", { "dependencies": { "defaults": "^1.0.3" } }, "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg=="],
"web-streams-polyfill": ["web-streams-polyfill@3.3.3", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="],
"webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
"webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="],
"whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="],
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
"widest-line": ["widest-line@3.1.0", "", { "dependencies": { "string-width": "^4.0.0" } }, "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg=="],
@@ -1407,6 +1454,8 @@
"wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
"ws": ["ws@8.19.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="],
"wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="],
"xdg-app-paths": ["xdg-app-paths@5.1.0", "", { "dependencies": { "xdg-portable": "^7.0.0" } }, "sha512-RAQ3WkPf4KTU1A8RtFx3gWywzVKe00tfOPFfl2NDGqbIFENQO4kqAJp7mhQjNj/33W5x5hiWWUdyfPq/5SU3QA=="],
@@ -1419,7 +1468,7 @@
"yocto-queue": ["yocto-queue@1.2.2", "", {}, "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ=="],
"zod": ["zod@4.1.11", "", {}, "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg=="],
"zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
"@aws-crypto/sha256-browser/@aws-sdk/types": ["@aws-sdk/types@3.973.3", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-tma6D8/xHZHJEUqmr6ksZjZ0onyIUqKDQLyp50ttZJmS0IwFYzxBgp5CxFvpYAnah52V3UtgrqGA6E83gtT7NQ=="],
@@ -1531,8 +1580,6 @@
"@aws-sdk/xml-builder/@smithy/types": ["@smithy/types@4.13.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-COuLsZILbbQsdrwKQpkkpyep7lCsByxwj7m0Mg5v66/ZTyenlfBc40/QFQ5chO0YN/PNEH1Bi3fGtfXPnYNeDw=="],
"@better-auth/core/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
"@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="],
"@nuxt/kit/tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
@@ -1607,23 +1654,33 @@
"@vercel/cli-auth/open": ["open@8.4.0", "", { "dependencies": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", "is-wsl": "^2.2.0" } }, "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q=="],
"@workflow/builders/enhanced-resolve": ["enhanced-resolve@5.18.2", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ=="],
"@vercel/cli-auth/zod": ["zod@4.1.11", "", {}, "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg=="],
"@workflow/cli/dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="],
"@vercel/functions/@vercel/oidc": ["@vercel/oidc@3.2.0", "", {}, "sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug=="],
"@vercel/queue/@vercel/oidc": ["@vercel/oidc@3.2.0", "", {}, "sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug=="],
"@vercel/queue/mixpart": ["mixpart@0.0.5-alpha.1", "", {}, "sha512-2ZfG/NO2SVE9HLk1/W+yOrIOA0d674ljZExLdievZQpYjbJYQjIdye8vNMR63yF7nN/NbO9q8mp16JUEYBCilg=="],
"@workflow/builders/enhanced-resolve": ["enhanced-resolve@5.18.2", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ=="],
"@workflow/cli/enhanced-resolve": ["enhanced-resolve@5.18.2", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ=="],
"@workflow/cli/mixpart": ["mixpart@0.0.4", "", {}, "sha512-RAoaOSXnMLrfUfmFbNynRYjeMru/bhgAYRy/GQVI8gmRq7vm9V9c2gGVYnYoQ008X6YTmRIu5b0397U7vb0bIA=="],
"@workflow/cli/zod": ["zod@4.1.11", "", {}, "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg=="],
"@workflow/core/@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="],
"@workflow/core/nanoid": ["nanoid@5.1.6", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg=="],
"@workflow/core/zod": ["zod@4.1.11", "", {}, "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg=="],
"@workflow/next/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
"@workflow/world-postgres/drizzle-orm": ["drizzle-orm@0.44.7", "", { "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" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@upstash/redis", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-quIpnYznjU9lHshEOAYLoZ9s3jweleHlZIAWR/jX9gAWNg/JhQ1wj0KGRf7/Zm+obRrYd9GjPVJg790QY9N5AQ=="],
"@workflow/world/zod": ["zod@4.1.11", "", {}, "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg=="],
"@workflow/world-vercel/@vercel/oidc": ["@vercel/oidc@3.0.5", "", {}, "sha512-fnYhv671l+eTTp48gB4zEsTW/YtRgRPnkI2nT7x6qw5rkI1Lq2hTmQIpHPgyThI0znLK+vX2n9XxKdXZ7BUbbw=="],
"@workflow/world-local/zod": ["zod@4.1.11", "", {}, "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg=="],
"@workflow/world-vercel/zod": ["zod@4.1.11", "", {}, "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg=="],
"@xhmikosr/archive-type/file-type": ["file-type@20.5.0", "", { "dependencies": { "@tokenizer/inflate": "^0.2.6", "strtok3": "^10.2.0", "token-types": "^6.0.0", "uint8array-extras": "^1.4.0" } }, "sha512-BfHZtG/l9iMm4Ecianu7P8HRD2tBHLtjXinm4X62XBOYzi7CYA7jyqfJzOvXHqzVrVPYqBo2/GvbARMaaJkKVg=="],
@@ -1641,14 +1698,10 @@
"ansi-escapes/type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="],
"better-auth/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
"body-parser/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
"boxen/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
"boxen/type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="],
"boxen/widest-line": ["widest-line@5.0.0", "", { "dependencies": { "string-width": "^7.0.0" } }, "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA=="],
"boxen/wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="],
@@ -1659,6 +1712,8 @@
"c12/exsolve": ["exsolve@1.0.8", "", {}, "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA=="],
"cross-fetch/node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
"decompress-response/mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="],
"execa/onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="],
@@ -1679,6 +1734,8 @@
"is-inside-container/is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="],
"lightningcss/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
"log-symbols/is-unicode-supported": ["is-unicode-supported@1.3.0", "", {}, "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ=="],
"micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
@@ -1687,6 +1744,8 @@
"next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],
"node-gyp-build-optional-packages/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
"nypm/citty": ["citty@0.2.1", "", {}, "sha512-kEV95lFBhQgtogAPlQfJJ0WGVSokvLr/UEoFPiKKOXF7pl98HfUVUD0ejsuTCld/9xH9vogSywZ5KqHzXrZpqg=="],
"ora/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
@@ -1695,6 +1754,8 @@
"send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
"sharp/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
"source-map-support/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
"string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],

View File

@@ -13,22 +13,21 @@ services:
PORT: 3000
HOSTNAME: 0.0.0.0
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-}
DATABASE_URL: ${DATABASE_URL:-postgres://postgres:postgres@postgres:5432/fiscal_clone}
DATABASE_URL: ${DATABASE_URL:-file:/app/data/fiscal.sqlite}
BETTER_AUTH_SECRET: ${BETTER_AUTH_SECRET:-}
BETTER_AUTH_BASE_URL: ${BETTER_AUTH_BASE_URL:-}
BETTER_AUTH_BASE_URL: ${BETTER_AUTH_BASE_URL:-https://fiscal.b11studio.xyz}
BETTER_AUTH_ADMIN_USER_IDS: ${BETTER_AUTH_ADMIN_USER_IDS:-}
BETTER_AUTH_TRUSTED_ORIGINS: ${BETTER_AUTH_TRUSTED_ORIGINS:-}
BETTER_AUTH_TRUSTED_ORIGINS: ${BETTER_AUTH_TRUSTED_ORIGINS:-https://fiscal.b11studio.xyz}
OPENCLAW_BASE_URL: ${OPENCLAW_BASE_URL:-}
OPENCLAW_API_KEY: ${OPENCLAW_API_KEY:-}
OPENCLAW_MODEL: ${OPENCLAW_MODEL:-zeroclaw}
SEC_USER_AGENT: ${SEC_USER_AGENT:-Fiscal Clone <support@fiscal.local>}
WORKFLOW_TARGET_WORLD: ${WORKFLOW_TARGET_WORLD:-@workflow/world-postgres}
WORKFLOW_POSTGRES_URL: ${WORKFLOW_POSTGRES_URL:-postgres://postgres:postgres@postgres:5432/fiscal_clone}
WORKFLOW_POSTGRES_JOB_PREFIX: ${WORKFLOW_POSTGRES_JOB_PREFIX:-fiscal_clone}
WORKFLOW_POSTGRES_WORKER_CONCURRENCY: ${WORKFLOW_POSTGRES_WORKER_CONCURRENCY:-10}
depends_on:
postgres:
condition: service_healthy
WORKFLOW_TARGET_WORLD: ${WORKFLOW_TARGET_WORLD:-local}
WORKFLOW_LOCAL_DATA_DIR: ${WORKFLOW_LOCAL_DATA_DIR:-/app/.workflow-data}
WORKFLOW_LOCAL_QUEUE_CONCURRENCY: ${WORKFLOW_LOCAL_QUEUE_CONCURRENCY:-100}
volumes:
- fiscal_sqlite_data:/app/data
- fiscal_workflow_data:/app/.workflow-data
healthcheck:
test: ["CMD-SHELL", "wget -q --spider http://127.0.0.1:3000/api/health || exit 1"]
interval: 30s
@@ -36,20 +35,7 @@ services:
retries: 3
expose:
- "3000"
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_DB: ${POSTGRES_DB:-fiscal_clone}
POSTGRES_USER: ${POSTGRES_USER:-postgres}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-postgres} -d ${POSTGRES_DB:-fiscal_clone}"]
interval: 10s
timeout: 5s
retries: 5
volumes:
postgres_data:
fiscal_sqlite_data:
fiscal_workflow_data:

View File

@@ -1,13 +1,9 @@
const databaseUrl = process.env.DATABASE_URL?.trim();
if (!databaseUrl) {
throw new Error('DATABASE_URL is required to run Drizzle migrations.');
}
const databaseUrl = process.env.DATABASE_URL?.trim() || 'file:data/fiscal.sqlite';
export default {
schema: './lib/server/db/schema.ts',
out: './drizzle',
dialect: 'postgresql',
dialect: 'sqlite',
dbCredentials: {
url: databaseUrl
},

View File

@@ -0,0 +1,190 @@
CREATE TABLE `account` (
`id` text PRIMARY KEY NOT NULL,
`accountId` text NOT NULL,
`providerId` text NOT NULL,
`userId` text NOT NULL,
`accessToken` text,
`refreshToken` text,
`idToken` text,
`accessTokenExpiresAt` integer,
`refreshTokenExpiresAt` integer,
`scope` text,
`password` text,
`createdAt` integer NOT NULL,
`updatedAt` integer NOT NULL,
FOREIGN KEY (`userId`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE INDEX `account_userId_idx` ON `account` (`userId`);--> statement-breakpoint
CREATE TABLE `filing` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`ticker` text NOT NULL,
`filing_type` text NOT NULL,
`filing_date` text NOT NULL,
`accession_number` text NOT NULL,
`cik` text NOT NULL,
`company_name` text NOT NULL,
`filing_url` text,
`submission_url` text,
`primary_document` text,
`metrics` text,
`analysis` text,
`created_at` text NOT NULL,
`updated_at` text NOT NULL
);
--> statement-breakpoint
CREATE UNIQUE INDEX `filing_accession_uidx` ON `filing` (`accession_number`);--> statement-breakpoint
CREATE INDEX `filing_ticker_date_idx` ON `filing` (`ticker`,`filing_date`);--> statement-breakpoint
CREATE INDEX `filing_date_idx` ON `filing` (`filing_date`);--> statement-breakpoint
CREATE TABLE `filing_link` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`filing_id` integer NOT NULL,
`link_type` text NOT NULL,
`url` text NOT NULL,
`source` text DEFAULT 'sec' NOT NULL,
`created_at` text NOT NULL,
FOREIGN KEY (`filing_id`) REFERENCES `filing`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE UNIQUE INDEX `filing_link_unique_uidx` ON `filing_link` (`filing_id`,`url`);--> statement-breakpoint
CREATE INDEX `filing_link_filing_idx` ON `filing_link` (`filing_id`);--> statement-breakpoint
CREATE TABLE `holding` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`user_id` text NOT NULL,
`ticker` text NOT NULL,
`shares` numeric NOT NULL,
`avg_cost` numeric NOT NULL,
`current_price` numeric,
`market_value` numeric NOT NULL,
`gain_loss` numeric NOT NULL,
`gain_loss_pct` numeric NOT NULL,
`last_price_at` text,
`created_at` text NOT NULL,
`updated_at` text NOT NULL,
FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE UNIQUE INDEX `holding_user_ticker_uidx` ON `holding` (`user_id`,`ticker`);--> statement-breakpoint
CREATE INDEX `holding_user_idx` ON `holding` (`user_id`);--> statement-breakpoint
CREATE TABLE `invitation` (
`id` text PRIMARY KEY NOT NULL,
`organizationId` text NOT NULL,
`email` text NOT NULL,
`role` text,
`status` text DEFAULT 'pending' NOT NULL,
`expiresAt` integer NOT NULL,
`createdAt` integer NOT NULL,
`inviterId` text NOT NULL,
FOREIGN KEY (`organizationId`) REFERENCES `organization`(`id`) ON UPDATE no action ON DELETE cascade,
FOREIGN KEY (`inviterId`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE INDEX `invitation_organizationId_idx` ON `invitation` (`organizationId`);--> statement-breakpoint
CREATE INDEX `invitation_email_idx` ON `invitation` (`email`);--> statement-breakpoint
CREATE TABLE `member` (
`id` text PRIMARY KEY NOT NULL,
`organizationId` text NOT NULL,
`userId` text NOT NULL,
`role` text DEFAULT 'member' NOT NULL,
`createdAt` integer NOT NULL,
FOREIGN KEY (`organizationId`) REFERENCES `organization`(`id`) ON UPDATE no action ON DELETE cascade,
FOREIGN KEY (`userId`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE INDEX `member_organizationId_idx` ON `member` (`organizationId`);--> statement-breakpoint
CREATE INDEX `member_userId_idx` ON `member` (`userId`);--> statement-breakpoint
CREATE TABLE `organization` (
`id` text PRIMARY KEY NOT NULL,
`name` text NOT NULL,
`slug` text NOT NULL,
`logo` text,
`createdAt` integer NOT NULL,
`metadata` text
);
--> statement-breakpoint
CREATE UNIQUE INDEX `organization_slug_uidx` ON `organization` (`slug`);--> statement-breakpoint
CREATE TABLE `portfolio_insight` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`user_id` text NOT NULL,
`provider` text NOT NULL,
`model` text NOT NULL,
`content` text NOT NULL,
`created_at` text NOT NULL,
FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE INDEX `insight_user_created_idx` ON `portfolio_insight` (`user_id`,`created_at`);--> statement-breakpoint
CREATE TABLE `session` (
`id` text PRIMARY KEY NOT NULL,
`expiresAt` integer NOT NULL,
`token` text NOT NULL,
`createdAt` integer NOT NULL,
`updatedAt` integer NOT NULL,
`ipAddress` text,
`userAgent` text,
`userId` text NOT NULL,
`impersonatedBy` text,
`activeOrganizationId` text,
FOREIGN KEY (`userId`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE UNIQUE INDEX `session_token_uidx` ON `session` (`token`);--> statement-breakpoint
CREATE INDEX `session_userId_idx` ON `session` (`userId`);--> statement-breakpoint
CREATE TABLE `task_run` (
`id` text PRIMARY KEY NOT NULL,
`user_id` text NOT NULL,
`task_type` text NOT NULL,
`status` text NOT NULL,
`priority` integer NOT NULL,
`payload` text NOT NULL,
`result` text,
`error` text,
`attempts` integer NOT NULL,
`max_attempts` integer NOT NULL,
`workflow_run_id` text,
`created_at` text NOT NULL,
`updated_at` text NOT NULL,
`finished_at` text,
FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE INDEX `task_user_created_idx` ON `task_run` (`user_id`,`created_at`);--> statement-breakpoint
CREATE INDEX `task_status_idx` ON `task_run` (`status`);--> statement-breakpoint
CREATE UNIQUE INDEX `task_workflow_run_uidx` ON `task_run` (`workflow_run_id`);--> statement-breakpoint
CREATE TABLE `user` (
`id` text PRIMARY KEY NOT NULL,
`name` text NOT NULL,
`email` text NOT NULL,
`emailVerified` integer DEFAULT false NOT NULL,
`image` text,
`createdAt` integer NOT NULL,
`updatedAt` integer NOT NULL,
`role` text,
`banned` integer DEFAULT false,
`banReason` text,
`banExpires` integer
);
--> statement-breakpoint
CREATE UNIQUE INDEX `user_email_uidx` ON `user` (`email`);--> statement-breakpoint
CREATE TABLE `verification` (
`id` text PRIMARY KEY NOT NULL,
`identifier` text NOT NULL,
`value` text NOT NULL,
`expiresAt` integer NOT NULL,
`createdAt` integer NOT NULL,
`updatedAt` integer NOT NULL
);
--> statement-breakpoint
CREATE INDEX `verification_identifier_idx` ON `verification` (`identifier`);--> statement-breakpoint
CREATE TABLE `watchlist_item` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`user_id` text NOT NULL,
`ticker` text NOT NULL,
`company_name` text NOT NULL,
`sector` text,
`created_at` text NOT NULL,
FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE UNIQUE INDEX `watchlist_user_ticker_uidx` ON `watchlist_item` (`user_id`,`ticker`);--> statement-breakpoint
CREATE INDEX `watchlist_user_created_idx` ON `watchlist_item` (`user_id`,`created_at`);

View File

@@ -1,199 +0,0 @@
CREATE TABLE IF NOT EXISTS "account" (
"id" text PRIMARY KEY NOT NULL,
"accountId" text NOT NULL,
"providerId" text NOT NULL,
"userId" text NOT NULL,
"accessToken" text,
"refreshToken" text,
"idToken" text,
"accessTokenExpiresAt" timestamp with time zone,
"refreshTokenExpiresAt" timestamp with time zone,
"scope" text,
"password" text,
"createdAt" timestamp with time zone NOT NULL,
"updatedAt" timestamp with time zone NOT NULL
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "invitation" (
"id" text PRIMARY KEY NOT NULL,
"organizationId" text NOT NULL,
"email" text NOT NULL,
"role" text,
"status" text DEFAULT 'pending' NOT NULL,
"expiresAt" timestamp with time zone NOT NULL,
"createdAt" timestamp with time zone NOT NULL,
"inviterId" text NOT NULL
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "member" (
"id" text PRIMARY KEY NOT NULL,
"organizationId" text NOT NULL,
"userId" text NOT NULL,
"role" text DEFAULT 'member' NOT NULL,
"createdAt" timestamp with time zone NOT NULL
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "organization" (
"id" text PRIMARY KEY NOT NULL,
"name" text NOT NULL,
"slug" text NOT NULL,
"logo" text,
"createdAt" timestamp with time zone NOT NULL,
"metadata" text
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "session" (
"id" text PRIMARY KEY NOT NULL,
"expiresAt" timestamp with time zone NOT NULL,
"token" text NOT NULL,
"createdAt" timestamp with time zone NOT NULL,
"updatedAt" timestamp with time zone NOT NULL,
"ipAddress" text,
"userAgent" text,
"userId" text NOT NULL,
"impersonatedBy" text,
"activeOrganizationId" text
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "user" (
"id" text PRIMARY KEY NOT NULL,
"name" text NOT NULL,
"email" text NOT NULL,
"emailVerified" boolean DEFAULT false NOT NULL,
"image" text,
"createdAt" timestamp with time zone NOT NULL,
"updatedAt" timestamp with time zone NOT NULL,
"role" text,
"banned" boolean DEFAULT false,
"banReason" text,
"banExpires" timestamp with time zone
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "verification" (
"id" text PRIMARY KEY NOT NULL,
"identifier" text NOT NULL,
"value" text NOT NULL,
"expiresAt" timestamp with time zone NOT NULL,
"createdAt" timestamp with time zone NOT NULL,
"updatedAt" timestamp with time zone NOT NULL
);
--> statement-breakpoint
DO $$
DECLARE
m record;
BEGIN
FOR m IN (
SELECT * FROM (
VALUES
('user', 'email_verified', 'emailVerified'),
('user', 'emailverified', 'emailVerified'),
('user', 'created_at', 'createdAt'),
('user', 'createdat', 'createdAt'),
('user', 'updated_at', 'updatedAt'),
('user', 'updatedat', 'updatedAt'),
('user', 'ban_reason', 'banReason'),
('user', 'banreason', 'banReason'),
('user', 'ban_expires', 'banExpires'),
('user', 'banexpires', 'banExpires'),
('organization', 'created_at', 'createdAt'),
('organization', 'createdat', 'createdAt'),
('session', 'expires_at', 'expiresAt'),
('session', 'expiresat', 'expiresAt'),
('session', 'created_at', 'createdAt'),
('session', 'createdat', 'createdAt'),
('session', 'updated_at', 'updatedAt'),
('session', 'updatedat', 'updatedAt'),
('session', 'ip_address', 'ipAddress'),
('session', 'ipaddress', 'ipAddress'),
('session', 'user_agent', 'userAgent'),
('session', 'useragent', 'userAgent'),
('session', 'user_id', 'userId'),
('session', 'userid', 'userId'),
('session', 'impersonated_by', 'impersonatedBy'),
('session', 'impersonatedby', 'impersonatedBy'),
('session', 'active_organization_id', 'activeOrganizationId'),
('session', 'activeorganizationid', 'activeOrganizationId'),
('account', 'account_id', 'accountId'),
('account', 'accountid', 'accountId'),
('account', 'provider_id', 'providerId'),
('account', 'providerid', 'providerId'),
('account', 'user_id', 'userId'),
('account', 'userid', 'userId'),
('account', 'access_token', 'accessToken'),
('account', 'accesstoken', 'accessToken'),
('account', 'refresh_token', 'refreshToken'),
('account', 'refreshtoken', 'refreshToken'),
('account', 'id_token', 'idToken'),
('account', 'idtoken', 'idToken'),
('account', 'access_token_expires_at', 'accessTokenExpiresAt'),
('account', 'accesstokenexpiresat', 'accessTokenExpiresAt'),
('account', 'refresh_token_expires_at', 'refreshTokenExpiresAt'),
('account', 'refreshtokenexpiresat', 'refreshTokenExpiresAt'),
('account', 'created_at', 'createdAt'),
('account', 'createdat', 'createdAt'),
('account', 'updated_at', 'updatedAt'),
('account', 'updatedat', 'updatedAt'),
('verification', 'expires_at', 'expiresAt'),
('verification', 'expiresat', 'expiresAt'),
('verification', 'created_at', 'createdAt'),
('verification', 'createdat', 'createdAt'),
('verification', 'updated_at', 'updatedAt'),
('verification', 'updatedat', 'updatedAt'),
('member', 'organization_id', 'organizationId'),
('member', 'organizationid', 'organizationId'),
('member', 'user_id', 'userId'),
('member', 'userid', 'userId'),
('member', 'created_at', 'createdAt'),
('member', 'createdat', 'createdAt'),
('invitation', 'organization_id', 'organizationId'),
('invitation', 'organizationid', 'organizationId'),
('invitation', 'expires_at', 'expiresAt'),
('invitation', 'expiresat', 'expiresAt'),
('invitation', 'created_at', 'createdAt'),
('invitation', 'createdat', 'createdAt'),
('invitation', 'inviter_id', 'inviterId'),
('invitation', 'inviterid', 'inviterId')
) AS rename_map(table_name, old_column, new_column)
)
LOOP
IF EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = m.table_name
AND column_name = m.old_column
)
AND NOT EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = m.table_name
AND column_name = m.new_column
) THEN
EXECUTE format(
'ALTER TABLE %I.%I RENAME COLUMN %I TO %I',
'public',
m.table_name,
m.old_column,
m.new_column
);
END IF;
END LOOP;
END $$;
--> statement-breakpoint
DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'account_userId_user_id_fk') THEN ALTER TABLE "account" ADD CONSTRAINT "account_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; END IF; END $$;--> statement-breakpoint
DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'invitation_organizationId_organization_id_fk') THEN ALTER TABLE "invitation" ADD CONSTRAINT "invitation_organizationId_organization_id_fk" FOREIGN KEY ("organizationId") REFERENCES "public"."organization"("id") ON DELETE cascade ON UPDATE no action; END IF; END $$;--> statement-breakpoint
DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'invitation_inviterId_user_id_fk') THEN ALTER TABLE "invitation" ADD CONSTRAINT "invitation_inviterId_user_id_fk" FOREIGN KEY ("inviterId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; END IF; END $$;--> statement-breakpoint
DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'member_organizationId_organization_id_fk') THEN ALTER TABLE "member" ADD CONSTRAINT "member_organizationId_organization_id_fk" FOREIGN KEY ("organizationId") REFERENCES "public"."organization"("id") ON DELETE cascade ON UPDATE no action; END IF; END $$;--> statement-breakpoint
DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'member_userId_user_id_fk') THEN ALTER TABLE "member" ADD CONSTRAINT "member_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; END IF; END $$;--> statement-breakpoint
DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'session_userId_user_id_fk') THEN ALTER TABLE "session" ADD CONSTRAINT "session_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; END IF; END $$;--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "account_userId_idx" ON "account" USING btree ("userId");--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "invitation_organizationId_idx" ON "invitation" USING btree ("organizationId");--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "invitation_email_idx" ON "invitation" USING btree ("email");--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "member_organizationId_idx" ON "member" USING btree ("organizationId");--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "member_userId_idx" ON "member" USING btree ("userId");--> statement-breakpoint
CREATE UNIQUE INDEX IF NOT EXISTS "organization_slug_uidx" ON "organization" USING btree ("slug");--> statement-breakpoint
CREATE UNIQUE INDEX IF NOT EXISTS "session_token_uidx" ON "session" USING btree ("token");--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "session_userId_idx" ON "session" USING btree ("userId");--> statement-breakpoint
CREATE UNIQUE INDEX IF NOT EXISTS "user_email_uidx" ON "user" USING btree ("email");--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "verification_identifier_idx" ON "verification" USING btree ("identifier");

View File

@@ -1,94 +0,0 @@
CREATE TABLE "filing" (
"id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "filing_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1),
"ticker" text NOT NULL,
"filing_type" text NOT NULL,
"filing_date" text NOT NULL,
"accession_number" text NOT NULL,
"cik" text NOT NULL,
"company_name" text NOT NULL,
"filing_url" text,
"submission_url" text,
"primary_document" text,
"metrics" jsonb,
"analysis" jsonb,
"created_at" timestamp with time zone NOT NULL,
"updated_at" timestamp with time zone NOT NULL
);
--> statement-breakpoint
CREATE TABLE "filing_link" (
"id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "filing_link_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1),
"filing_id" integer NOT NULL,
"link_type" text NOT NULL,
"url" text NOT NULL,
"source" text DEFAULT 'sec' NOT NULL,
"created_at" timestamp with time zone NOT NULL
);
--> statement-breakpoint
CREATE TABLE "holding" (
"id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "holding_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1),
"user_id" text NOT NULL,
"ticker" text NOT NULL,
"shares" numeric(30, 6) NOT NULL,
"avg_cost" numeric(30, 6) NOT NULL,
"current_price" numeric(30, 6),
"market_value" numeric(30, 2) NOT NULL,
"gain_loss" numeric(30, 2) NOT NULL,
"gain_loss_pct" numeric(30, 2) NOT NULL,
"last_price_at" timestamp with time zone,
"created_at" timestamp with time zone NOT NULL,
"updated_at" timestamp with time zone NOT NULL
);
--> statement-breakpoint
CREATE TABLE "portfolio_insight" (
"id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "portfolio_insight_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1),
"user_id" text NOT NULL,
"provider" text NOT NULL,
"model" text NOT NULL,
"content" text NOT NULL,
"created_at" timestamp with time zone NOT NULL
);
--> statement-breakpoint
CREATE TABLE "task_run" (
"id" text PRIMARY KEY NOT NULL,
"user_id" text NOT NULL,
"task_type" text NOT NULL,
"status" text NOT NULL,
"priority" integer NOT NULL,
"payload" jsonb NOT NULL,
"result" jsonb,
"error" text,
"attempts" integer NOT NULL,
"max_attempts" integer NOT NULL,
"workflow_run_id" text,
"created_at" timestamp with time zone NOT NULL,
"updated_at" timestamp with time zone NOT NULL,
"finished_at" timestamp with time zone
);
--> statement-breakpoint
CREATE TABLE "watchlist_item" (
"id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "watchlist_item_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1),
"user_id" text NOT NULL,
"ticker" text NOT NULL,
"company_name" text NOT NULL,
"sector" text,
"created_at" timestamp with time zone NOT NULL
);
--> statement-breakpoint
ALTER TABLE "filing_link" ADD CONSTRAINT "filing_link_filing_id_filing_id_fk" FOREIGN KEY ("filing_id") REFERENCES "public"."filing"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "holding" ADD CONSTRAINT "holding_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "portfolio_insight" ADD CONSTRAINT "portfolio_insight_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "task_run" ADD CONSTRAINT "task_run_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "watchlist_item" ADD CONSTRAINT "watchlist_item_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
CREATE UNIQUE INDEX "filing_accession_uidx" ON "filing" USING btree ("accession_number");--> statement-breakpoint
CREATE INDEX "filing_ticker_date_idx" ON "filing" USING btree ("ticker","filing_date");--> statement-breakpoint
CREATE INDEX "filing_date_idx" ON "filing" USING btree ("filing_date");--> statement-breakpoint
CREATE UNIQUE INDEX "filing_link_unique_uidx" ON "filing_link" USING btree ("filing_id","url");--> statement-breakpoint
CREATE INDEX "filing_link_filing_idx" ON "filing_link" USING btree ("filing_id");--> statement-breakpoint
CREATE UNIQUE INDEX "holding_user_ticker_uidx" ON "holding" USING btree ("user_id","ticker");--> statement-breakpoint
CREATE INDEX "holding_user_idx" ON "holding" USING btree ("user_id");--> statement-breakpoint
CREATE INDEX "insight_user_created_idx" ON "portfolio_insight" USING btree ("user_id","created_at");--> statement-breakpoint
CREATE INDEX "task_user_created_idx" ON "task_run" USING btree ("user_id","created_at");--> statement-breakpoint
CREATE INDEX "task_status_idx" ON "task_run" USING btree ("status");--> statement-breakpoint
CREATE UNIQUE INDEX "task_workflow_run_uidx" ON "task_run" USING btree ("workflow_run_id");--> statement-breakpoint
CREATE UNIQUE INDEX "watchlist_user_ticker_uidx" ON "watchlist_item" USING btree ("user_id","ticker");--> statement-breakpoint
CREATE INDEX "watchlist_user_created_idx" ON "watchlist_item" USING btree ("user_id","created_at");

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,19 +1,12 @@
{
"version": "7",
"dialect": "postgresql",
"dialect": "sqlite",
"entries": [
{
"idx": 0,
"version": "7",
"when": 1771967961625,
"tag": "0000_tense_centennial",
"breakpoints": true
},
{
"idx": 1,
"version": "7",
"when": 1772076911227,
"tag": "0001_boring_toad",
"version": "6",
"when": 1772137733427,
"tag": "0000_cold_silver_centurion",
"breakpoints": true
}
]

View File

@@ -20,7 +20,7 @@ const secret = process.env.BETTER_AUTH_SECRET?.trim() || undefined;
export const auth = betterAuth({
database: drizzleAdapter(db, {
provider: 'pg',
provider: 'sqlite',
schema: authSchema
}),
baseURL,

View File

@@ -1,37 +1,54 @@
import { drizzle } from 'drizzle-orm/node-postgres';
import { Pool } from 'pg';
import { mkdirSync } from 'node:fs';
import { dirname } from 'node:path';
import { Database } from 'bun:sqlite';
import { drizzle } from 'drizzle-orm/bun-sqlite';
import { schema } from './schema';
type AppDrizzleDb = ReturnType<typeof createDb>;
declare global {
// eslint-disable-next-line no-var
var __fiscalPgPool: Pool | undefined;
var __fiscalSqliteClient: Database | undefined;
// eslint-disable-next-line no-var
var __fiscalDrizzleDb: AppDrizzleDb | undefined;
}
function getConnectionString() {
const connectionString = process.env.DATABASE_URL?.trim();
if (!connectionString) {
throw new Error('DATABASE_URL is required for PostgreSQL.');
function getDatabasePath() {
const raw = process.env.DATABASE_URL?.trim() || 'file:data/fiscal.sqlite';
let databasePath = raw.startsWith('file:') ? raw.slice(5) : raw;
if (databasePath.startsWith('///')) {
databasePath = databasePath.slice(2);
}
return connectionString;
if (!databasePath) {
throw new Error('DATABASE_URL must point to a SQLite file path.');
}
return databasePath;
}
export function getPool() {
if (!globalThis.__fiscalPgPool) {
globalThis.__fiscalPgPool = new Pool({
connectionString: getConnectionString()
});
export function getSqliteClient() {
if (!globalThis.__fiscalSqliteClient) {
const databasePath = getDatabasePath();
if (databasePath !== ':memory:') {
mkdirSync(dirname(databasePath), { recursive: true });
}
return globalThis.__fiscalPgPool;
const client = new Database(databasePath, { create: true });
client.exec('PRAGMA foreign_keys = ON;');
client.exec('PRAGMA journal_mode = WAL;');
client.exec('PRAGMA busy_timeout = 5000;');
globalThis.__fiscalSqliteClient = client;
}
return globalThis.__fiscalSqliteClient;
}
function createDb() {
return drizzle(getPool(), { schema });
return drizzle(getSqliteClient(), { schema });
}
export const db = globalThis.__fiscalDrizzleDb ?? createDb();

View File

@@ -1,14 +1,11 @@
import {
boolean,
index,
integer,
jsonb,
numeric,
pgTable,
sqliteTable,
text,
timestamp,
uniqueIndex
} from 'drizzle-orm/pg-core';
} from 'drizzle-orm/sqlite-core';
type FilingMetrics = {
revenue: number | null;
@@ -26,48 +23,42 @@ type FilingAnalysis = {
};
const authDateColumn = {
withTimezone: true,
mode: 'date'
mode: 'timestamp_ms'
} as const;
const appDateColumn = {
withTimezone: true,
mode: 'string'
} as const;
export const user = pgTable('user', {
export const user = sqliteTable('user', {
id: text('id').primaryKey().notNull(),
name: text('name').notNull(),
email: text('email').notNull(),
emailVerified: boolean('emailVerified').notNull().default(false),
emailVerified: integer('emailVerified', { mode: 'boolean' }).notNull().default(false),
image: text('image'),
createdAt: timestamp('createdAt', authDateColumn).notNull(),
updatedAt: timestamp('updatedAt', authDateColumn).notNull(),
createdAt: integer('createdAt', authDateColumn).notNull(),
updatedAt: integer('updatedAt', authDateColumn).notNull(),
role: text('role'),
banned: boolean('banned').default(false),
banned: integer('banned', { mode: 'boolean' }).default(false),
banReason: text('banReason'),
banExpires: timestamp('banExpires', authDateColumn)
banExpires: integer('banExpires', authDateColumn)
}, (table) => ({
userEmailUnique: uniqueIndex('user_email_uidx').on(table.email)
}));
export const organization = pgTable('organization', {
export const organization = sqliteTable('organization', {
id: text('id').primaryKey().notNull(),
name: text('name').notNull(),
slug: text('slug').notNull(),
logo: text('logo'),
createdAt: timestamp('createdAt', authDateColumn).notNull(),
createdAt: integer('createdAt', authDateColumn).notNull(),
metadata: text('metadata')
}, (table) => ({
organizationSlugUnique: uniqueIndex('organization_slug_uidx').on(table.slug)
}));
export const session = pgTable('session', {
export const session = sqliteTable('session', {
id: text('id').primaryKey().notNull(),
expiresAt: timestamp('expiresAt', authDateColumn).notNull(),
expiresAt: integer('expiresAt', authDateColumn).notNull(),
token: text('token').notNull(),
createdAt: timestamp('createdAt', authDateColumn).notNull(),
updatedAt: timestamp('updatedAt', authDateColumn).notNull(),
createdAt: integer('createdAt', authDateColumn).notNull(),
updatedAt: integer('updatedAt', authDateColumn).notNull(),
ipAddress: text('ipAddress'),
userAgent: text('userAgent'),
userId: text('userId').notNull().references(() => user.id, { onDelete: 'cascade' }),
@@ -78,7 +69,7 @@ export const session = pgTable('session', {
sessionUserIdIndex: index('session_userId_idx').on(table.userId)
}));
export const account = pgTable('account', {
export const account = sqliteTable('account', {
id: text('id').primaryKey().notNull(),
accountId: text('accountId').notNull(),
providerId: text('providerId').notNull(),
@@ -86,84 +77,84 @@ export const account = pgTable('account', {
accessToken: text('accessToken'),
refreshToken: text('refreshToken'),
idToken: text('idToken'),
accessTokenExpiresAt: timestamp('accessTokenExpiresAt', authDateColumn),
refreshTokenExpiresAt: timestamp('refreshTokenExpiresAt', authDateColumn),
accessTokenExpiresAt: integer('accessTokenExpiresAt', authDateColumn),
refreshTokenExpiresAt: integer('refreshTokenExpiresAt', authDateColumn),
scope: text('scope'),
password: text('password'),
createdAt: timestamp('createdAt', authDateColumn).notNull(),
updatedAt: timestamp('updatedAt', authDateColumn).notNull()
createdAt: integer('createdAt', authDateColumn).notNull(),
updatedAt: integer('updatedAt', authDateColumn).notNull()
}, (table) => ({
accountUserIdIndex: index('account_userId_idx').on(table.userId)
}));
export const verification = pgTable('verification', {
export const verification = sqliteTable('verification', {
id: text('id').primaryKey().notNull(),
identifier: text('identifier').notNull(),
value: text('value').notNull(),
expiresAt: timestamp('expiresAt', authDateColumn).notNull(),
createdAt: timestamp('createdAt', authDateColumn).notNull(),
updatedAt: timestamp('updatedAt', authDateColumn).notNull()
expiresAt: integer('expiresAt', authDateColumn).notNull(),
createdAt: integer('createdAt', authDateColumn).notNull(),
updatedAt: integer('updatedAt', authDateColumn).notNull()
}, (table) => ({
verificationIdentifierIndex: index('verification_identifier_idx').on(table.identifier)
}));
export const member = pgTable('member', {
export const member = sqliteTable('member', {
id: text('id').primaryKey().notNull(),
organizationId: text('organizationId').notNull().references(() => organization.id, { onDelete: 'cascade' }),
userId: text('userId').notNull().references(() => user.id, { onDelete: 'cascade' }),
role: text('role').notNull().default('member'),
createdAt: timestamp('createdAt', authDateColumn).notNull()
createdAt: integer('createdAt', authDateColumn).notNull()
}, (table) => ({
memberOrganizationIdIndex: index('member_organizationId_idx').on(table.organizationId),
memberUserIdIndex: index('member_userId_idx').on(table.userId)
}));
export const invitation = pgTable('invitation', {
export const invitation = sqliteTable('invitation', {
id: text('id').primaryKey().notNull(),
organizationId: text('organizationId').notNull().references(() => organization.id, { onDelete: 'cascade' }),
email: text('email').notNull(),
role: text('role'),
status: text('status').notNull().default('pending'),
expiresAt: timestamp('expiresAt', authDateColumn).notNull(),
createdAt: timestamp('createdAt', authDateColumn).notNull(),
expiresAt: integer('expiresAt', authDateColumn).notNull(),
createdAt: integer('createdAt', authDateColumn).notNull(),
inviterId: text('inviterId').notNull().references(() => user.id, { onDelete: 'cascade' })
}, (table) => ({
invitationOrganizationIdIndex: index('invitation_organizationId_idx').on(table.organizationId),
invitationEmailIndex: index('invitation_email_idx').on(table.email)
}));
export const watchlistItem = pgTable('watchlist_item', {
id: integer('id').generatedAlwaysAsIdentity().primaryKey(),
export const watchlistItem = sqliteTable('watchlist_item', {
id: integer('id').primaryKey({ autoIncrement: true }),
user_id: text('user_id').notNull().references(() => user.id, { onDelete: 'cascade' }),
ticker: text('ticker').notNull(),
company_name: text('company_name').notNull(),
sector: text('sector'),
created_at: timestamp('created_at', appDateColumn).notNull()
created_at: text('created_at').notNull()
}, (table) => ({
watchlistUserTickerUnique: uniqueIndex('watchlist_user_ticker_uidx').on(table.user_id, table.ticker),
watchlistUserCreatedIndex: index('watchlist_user_created_idx').on(table.user_id, table.created_at)
}));
export const holding = pgTable('holding', {
id: integer('id').generatedAlwaysAsIdentity().primaryKey(),
export const holding = sqliteTable('holding', {
id: integer('id').primaryKey({ autoIncrement: true }),
user_id: text('user_id').notNull().references(() => user.id, { onDelete: 'cascade' }),
ticker: text('ticker').notNull(),
shares: numeric('shares', { precision: 30, scale: 6 }).notNull(),
avg_cost: numeric('avg_cost', { precision: 30, scale: 6 }).notNull(),
current_price: numeric('current_price', { precision: 30, scale: 6 }),
market_value: numeric('market_value', { precision: 30, scale: 2 }).notNull(),
gain_loss: numeric('gain_loss', { precision: 30, scale: 2 }).notNull(),
gain_loss_pct: numeric('gain_loss_pct', { precision: 30, scale: 2 }).notNull(),
last_price_at: timestamp('last_price_at', appDateColumn),
created_at: timestamp('created_at', appDateColumn).notNull(),
updated_at: timestamp('updated_at', appDateColumn).notNull()
shares: numeric('shares').notNull(),
avg_cost: numeric('avg_cost').notNull(),
current_price: numeric('current_price'),
market_value: numeric('market_value').notNull(),
gain_loss: numeric('gain_loss').notNull(),
gain_loss_pct: numeric('gain_loss_pct').notNull(),
last_price_at: text('last_price_at'),
created_at: text('created_at').notNull(),
updated_at: text('updated_at').notNull()
}, (table) => ({
holdingUserTickerUnique: uniqueIndex('holding_user_ticker_uidx').on(table.user_id, table.ticker),
holdingUserIndex: index('holding_user_idx').on(table.user_id)
}));
export const filing = pgTable('filing', {
id: integer('id').generatedAlwaysAsIdentity().primaryKey(),
export const filing = sqliteTable('filing', {
id: integer('id').primaryKey({ autoIncrement: true }),
ticker: text('ticker').notNull(),
filing_type: text('filing_type').$type<'10-K' | '10-Q' | '8-K'>().notNull(),
filing_date: text('filing_date').notNull(),
@@ -173,56 +164,56 @@ export const filing = pgTable('filing', {
filing_url: text('filing_url'),
submission_url: text('submission_url'),
primary_document: text('primary_document'),
metrics: jsonb('metrics').$type<FilingMetrics | null>(),
analysis: jsonb('analysis').$type<FilingAnalysis | null>(),
created_at: timestamp('created_at', appDateColumn).notNull(),
updated_at: timestamp('updated_at', appDateColumn).notNull()
metrics: text('metrics', { mode: 'json' }).$type<FilingMetrics | null>(),
analysis: text('analysis', { mode: 'json' }).$type<FilingAnalysis | null>(),
created_at: text('created_at').notNull(),
updated_at: text('updated_at').notNull()
}, (table) => ({
filingAccessionUnique: uniqueIndex('filing_accession_uidx').on(table.accession_number),
filingTickerDateIndex: index('filing_ticker_date_idx').on(table.ticker, table.filing_date),
filingDateIndex: index('filing_date_idx').on(table.filing_date)
}));
export const filingLink = pgTable('filing_link', {
id: integer('id').generatedAlwaysAsIdentity().primaryKey(),
export const filingLink = sqliteTable('filing_link', {
id: integer('id').primaryKey({ autoIncrement: true }),
filing_id: integer('filing_id').notNull().references(() => filing.id, { onDelete: 'cascade' }),
link_type: text('link_type').notNull(),
url: text('url').notNull(),
source: text('source').notNull().default('sec'),
created_at: timestamp('created_at', appDateColumn).notNull()
created_at: text('created_at').notNull()
}, (table) => ({
filingLinkUnique: uniqueIndex('filing_link_unique_uidx').on(table.filing_id, table.url),
filingLinkFilingIndex: index('filing_link_filing_idx').on(table.filing_id)
}));
export const taskRun = pgTable('task_run', {
export const taskRun = sqliteTable('task_run', {
id: text('id').primaryKey().notNull(),
user_id: text('user_id').notNull().references(() => user.id, { onDelete: 'cascade' }),
task_type: text('task_type').$type<'sync_filings' | 'refresh_prices' | 'analyze_filing' | 'portfolio_insights'>().notNull(),
status: text('status').$type<'queued' | 'running' | 'completed' | 'failed'>().notNull(),
priority: integer('priority').notNull(),
payload: jsonb('payload').$type<Record<string, unknown>>().notNull(),
result: jsonb('result').$type<Record<string, unknown> | null>(),
payload: text('payload', { mode: 'json' }).$type<Record<string, unknown>>().notNull(),
result: text('result', { mode: 'json' }).$type<Record<string, unknown> | null>(),
error: text('error'),
attempts: integer('attempts').notNull(),
max_attempts: integer('max_attempts').notNull(),
workflow_run_id: text('workflow_run_id'),
created_at: timestamp('created_at', appDateColumn).notNull(),
updated_at: timestamp('updated_at', appDateColumn).notNull(),
finished_at: timestamp('finished_at', appDateColumn)
created_at: text('created_at').notNull(),
updated_at: text('updated_at').notNull(),
finished_at: text('finished_at')
}, (table) => ({
taskUserCreatedIndex: index('task_user_created_idx').on(table.user_id, table.created_at),
taskStatusIndex: index('task_status_idx').on(table.status),
taskWorkflowRunUnique: uniqueIndex('task_workflow_run_uidx').on(table.workflow_run_id)
}));
export const portfolioInsight = pgTable('portfolio_insight', {
id: integer('id').generatedAlwaysAsIdentity().primaryKey(),
export const portfolioInsight = sqliteTable('portfolio_insight', {
id: integer('id').primaryKey({ autoIncrement: true }),
user_id: text('user_id').notNull().references(() => user.id, { onDelete: 'cascade' }),
provider: text('provider').notNull(),
model: text('model').notNull(),
content: text('content').notNull(),
created_at: timestamp('created_at', appDateColumn).notNull()
created_at: text('created_at').notNull()
}, (table) => ({
insightUserCreatedIndex: index('insight_user_created_idx').on(table.user_id, table.created_at)
}));

View File

@@ -9,13 +9,12 @@
"start": "bun --bun next start",
"lint": "bun --bun tsc --noEmit",
"db:generate": "bun x drizzle-kit generate",
"db:migrate": "bun x drizzle-kit migrate",
"workflow:setup": "bun x workflow-postgres-setup"
"db:migrate": "bun x drizzle-kit migrate"
},
"dependencies": {
"@elysiajs/eden": "^1.4.8",
"@libsql/client": "^0.17.0",
"@tailwindcss/postcss": "^4.2.1",
"@workflow/world-postgres": "^4.1.0-beta.36",
"better-auth": "^1.4.19",
"clsx": "^2.1.1",
"date-fns": "^4.1.0",
@@ -23,7 +22,6 @@
"elysia": "latest",
"lucide-react": "^0.575.0",
"next": "^16.1.6",
"pg": "^8.18.0",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"recharts": "^3.7.0",
@@ -31,10 +29,10 @@
},
"devDependencies": {
"@types/node": "^25.3.0",
"@types/pg": "^8.16.0",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"autoprefixer": "^10.4.24",
"bun-types": "^1.3.10",
"drizzle-kit": "^0.31.4",
"postcss": "^8.5.6",
"tailwindcss": "^4.2.1",

View File

@@ -13,6 +13,9 @@
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"types": [
"bun-types"
],
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "react-jsx",