feat: scaffold AI Risk Index platform (research corpus, methodology v1 draft, monorepo)
- docs/research: 4-document research corpus grounding v1 (indices, data sources, rater API, landscape/evidence), compiled 2026-08-05 - docs/methodology: METHODOLOGY.md v1 draft, changelog, worked example - packages/scoring: pure deterministic engine (5 weighted dimensions, barriers inverted, exposure/substitution/augmentation sub-scores with CI bounds), snapshot + fast-check property tests - packages/db: Prisma schema with rating audit trail and immutable runs - apps/web: Next.js 14 site + public API v1 (health, methodology, occupations) - apps/worker: BullMQ multi-model rater pipeline (Message Batches, versioned rubric prompt v1) + score:recompute - apps/etl: Python skeleton with O*NET 30.3 downloader - infra: compose files, deploy.sh, ngrok.yml, env template Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Showing 74 changed files with +7,101 and −0
added
.gitignore
+33 −0
@@ -0,0 +1,33 @@ | ||
| 1 | +# deps / builds | |
| 2 | +node_modules/ | |
| 3 | +.next/ | |
| 4 | +dist/ | |
| 5 | +.turbo/ | |
| 6 | +*.tsbuildinfo | |
| 7 | + | |
| 8 | +# env & secrets — never commit (infra/.env.example is the template) | |
| 9 | +.env | |
| 10 | +.env.* | |
| 11 | +!.env.example | |
| 12 | + | |
| 13 | +# raw data is immutable and fetched by script — never committed (CLAUDE.md §5) | |
| 14 | +data/raw/* | |
| 15 | +!data/raw/.gitkeep | |
| 16 | + | |
| 17 | +# derived data: only manifests are committed, not payloads | |
| 18 | +data/derived/**/*.csv | |
| 19 | +data/derived/**/*.parquet | |
| 20 | +data/derived/**/*.jsonl | |
| 21 | +!data/derived/**/manifest.json | |
| 22 | + | |
| 23 | +# python (apps/etl) | |
| 24 | +__pycache__/ | |
| 25 | +.venv/ | |
| 26 | +*.egg-info/ | |
| 27 | +.pytest_cache/ | |
| 28 | +.ruff_cache/ | |
| 29 | + | |
| 30 | +# misc | |
| 31 | +.DS_Store | |
| 32 | +coverage/ | |
| 33 | +playwright-report/ | |
added
.prettierrc
+6 −0
@@ -0,0 +1,6 @@ | ||
| 1 | +{ | |
| 2 | + "semi": true, | |
| 3 | + "singleQuote": false, | |
| 4 | + "trailingComma": "all", | |
| 5 | + "printWidth": 100 | |
| 6 | +} | |
added
CLAUDE.md
+211 −0
@@ -0,0 +1,211 @@ | ||
| 1 | +# CLAUDE.md — AI Risk Index Platform (www.airiskindex.io) | |
| 2 | + | |
| 3 | +This file provides guidance to Claude Code when working in this repository. | |
| 4 | + | |
| 5 | +--- | |
| 6 | + | |
| 7 | +## 1. Project Overview | |
| 8 | + | |
| 9 | +**AI Risk Index** (airiskindex.io) is a public platform that scores occupations on their exposure to AI-driven automation, using a **task-based methodology** (not whole-occupation scoring). Every score must be transparent, versioned, and reproducible. | |
| 10 | + | |
| 11 | +**Core product promise:** "The most methodologically rigorous, fully transparent AI job-exposure index." | |
| 12 | + | |
| 13 | +### The three concepts (never confuse them) | |
| 14 | +- **Exposure** — AI is technically capable of performing the task | |
| 15 | +- **Substitution** — AI actually replaces the human performing it | |
| 16 | +- **Augmentation** — AI assists the human, increasing productivity | |
| 17 | + | |
| 18 | +The composite index reports all three as separate sub-scores. Never collapse them into a single undifferentiated "risk" number in code, UI copy, or API responses without also exposing the sub-scores. | |
| 19 | + | |
| 20 | +### Scoring model (v1) | |
| 21 | +Composite score 0–100 per occupation, computed from task-level scores weighted by task importance/frequency (O*NET weights). Dimensions: | |
| 22 | + | |
| 23 | +| Dimension | Key | Weight (v1) | | |
| 24 | +|---|---|---| | |
| 25 | +| Task automatability | `automatability` | 0.35 | | |
| 26 | +| Current technical feasibility | `feasibility` | 0.20 | | |
| 27 | +| Cost of substitution vs. wage | `cost_ratio` | 0.15 | | |
| 28 | +| Adoption barriers (regulation, liability, human-contact requirement) | `barriers` | 0.20 | | |
| 29 | +| Sector adoption velocity | `adoption_velocity` | 0.10 | | |
| 30 | + | |
| 31 | +- Weights live in `packages/scoring/src/weights.ts` — **never hardcode weights anywhere else**. | |
| 32 | +- Every scoring release gets a semver version (`INDEX_VERSION` in `packages/scoring/src/version.ts`) and a changelog entry in `docs/methodology/CHANGELOG.md`. | |
| 33 | +- Scores are stored with confidence intervals (`score_low`, `score`, `score_high`). UI must always be able to display uncertainty. | |
| 34 | + | |
| 35 | +### Data sources | |
| 36 | +- **O*NET 30.x** database dumps (occupations + tasks + importance ratings) → `data/raw/onet/` — 30.3 as of May 2026, 31.0 expected late Aug 2026. ⚠️ 30.x renamed *Technology Skills → Software Skills* and split *Skills* into Essential/Transferable; ETL loaders must target the 30.x schema. License CC BY 4.0 (attribution required). | |
| 37 | +- ESCO crosswalk for EU/France occupations (ROME codes) → `data/raw/esco/` | |
| 38 | +- LLM-as-evaluator task ratings (see §6) → `data/derived/ratings/` | |
| 39 | +- Expert panel (Delphi) validation overrides → `data/derived/expert_overrides/` | |
| 40 | + | |
| 41 | +Raw data is **never** edited in place. All transformations go through the ETL pipeline (`apps/etl`). | |
| 42 | + | |
| 43 | +--- | |
| 44 | + | |
| 45 | +## 2. Tech Stack | |
| 46 | + | |
| 47 | +- **Monorepo:** pnpm workspaces + Turborepo | |
| 48 | +- **Language:** TypeScript everywhere (strict mode). Python 3.12 only inside `apps/etl` for data science steps. | |
| 49 | +- **Frontend:** Next.js 14 (App Router), React 18, Tailwind CSS, shadcn/ui, Recharts for visualizations | |
| 50 | +- **API:** Next.js route handlers for public API (`/api/v1/*`) + tRPC for internal app calls | |
| 51 | +- **Database:** PostgreSQL 16 (Prisma ORM). Read-heavy → materialized views for score lookups. | |
| 52 | +- **Cache:** Redis (score lookups, rate limiting) | |
| 53 | +- **Jobs:** BullMQ workers in `apps/worker` (score recomputation, LLM rating batches) | |
| 54 | +- **Auth:** Auth.js (email magic link + OAuth). Public browsing requires no auth. | |
| 55 | +- **Testing:** Vitest (unit), Playwright (e2e), pytest (ETL) | |
| 56 | +- **Lint/format:** ESLint + Prettier, ruff for Python. CI fails on warnings. | |
| 57 | + | |
| 58 | +## 3. Repository Layout | |
| 59 | + | |
| 60 | +``` | |
| 61 | +airiskindex/ | |
| 62 | +├── apps/ | |
| 63 | +│ ├── web/ # Next.js app (site + public API routes) | |
| 64 | +│ ├── worker/ # BullMQ background workers | |
| 65 | +│ └── etl/ # Python data pipeline (raw → derived → DB) | |
| 66 | +├── packages/ | |
| 67 | +│ ├── scoring/ # Pure TS scoring engine — NO I/O, fully deterministic | |
| 68 | +│ ├── db/ # Prisma schema + client + seeds | |
| 69 | +│ ├── ui/ # Shared React components | |
| 70 | +│ └── config/ # Shared eslint/ts/tailwind configs | |
| 71 | +├── data/ | |
| 72 | +│ ├── raw/ # Immutable source dumps (gitignored, fetched by script) | |
| 73 | +│ └── derived/ # Pipeline outputs (versioned manifests committed) | |
| 74 | +├── docs/ | |
| 75 | +│ └── methodology/ # Public methodology doc, changelog, sensitivity analyses | |
| 76 | +├── infra/ # Deployment scripts, ngrok config, systemd units | |
| 77 | +└── CLAUDE.md | |
| 78 | +``` | |
| 79 | + | |
| 80 | +## 4. Commands | |
| 81 | + | |
| 82 | +```bash | |
| 83 | +pnpm install # install all workspaces | |
| 84 | +pnpm dev # run web app on :3000 (+ worker with --filter) | |
| 85 | +pnpm build # turbo build all | |
| 86 | +pnpm test # vitest across packages | |
| 87 | +pnpm test:e2e # playwright (requires pnpm dev running) | |
| 88 | +pnpm lint && pnpm typecheck # must pass before any commit | |
| 89 | +pnpm db:migrate # prisma migrate dev | |
| 90 | +pnpm db:seed # seed occupations + demo scores | |
| 91 | +pnpm score:recompute # full index recomputation (writes new INDEX_VERSION run) | |
| 92 | +cd apps/etl && make pipeline # full ETL: raw → derived → DB load | |
| 93 | +``` | |
| 94 | + | |
| 95 | +Single test file: `pnpm vitest run packages/scoring/src/composite.test.ts` | |
| 96 | + | |
| 97 | +## 5. Coding Conventions | |
| 98 | + | |
| 99 | +- `packages/scoring` must stay **pure and deterministic**: no network, no DB, no `Date.now()`, no randomness. It takes typed inputs and returns typed scores. This is what makes the methodology auditable. | |
| 100 | +- Every scoring function has property-based tests (fast-check) + snapshot tests against the published methodology examples in `docs/methodology/examples/`. | |
| 101 | +- All user-facing risk language follows the tone guide: the product frames results as **adaptation guidance, not doom**. Avoid copy like "your job will disappear"; prefer "X% of tasks in this occupation are highly exposed". | |
| 102 | +- API responses are versioned (`/api/v1/...`) and include `index_version` in every payload. | |
| 103 | +- Money/wages: store as integer cents + ISO currency. Percentages: store as 0–1 floats, format only at the UI layer. | |
| 104 | +- Never commit anything under `data/raw/`. Derived data commits only the manifest JSON (hashes + row counts), not the payloads. | |
| 105 | +- Migrations: additive-first. Destructive migrations require a `-- DESTRUCTIVE` comment and a manual approval in PR review. | |
| 106 | +- Accessibility: all charts need a data-table fallback (`<VisuallyHidden>` table) — this is a public-interest tool. | |
| 107 | + | |
| 108 | +## 6. LLM-as-Evaluator (task rating pipeline) | |
| 109 | + | |
| 110 | +Task automatability ratings are produced by an LLM rater in `apps/worker/src/raters/`, then validated by human experts. | |
| 111 | + | |
| 112 | +- Use the Anthropic API via the official SDK. Model names come from the `RATER_MODELS` env var (comma-separated list — **multi-model rating is required**: single-model LLM exposure ratings show up to 19× spread across frontier raters; see `docs/research/01-existing-indices.md` §7.2). Never hardcode model IDs in source. | |
| 113 | +- Newer models (Sonnet 5 / Opus 5) reject the `temperature` parameter — rating variance comes from the multi-model panel, not sampling temperature. | |
| 114 | +- Prompts live in `apps/worker/src/raters/prompts/*.md` and are versioned; a prompt change bumps `RATER_PROMPT_VERSION` and invalidates cached ratings. | |
| 115 | +- Every rating stores: model, prompt version, raw response, parsed score, timestamp. Full audit trail, always. | |
| 116 | +- Ratings are sampled (5%) for human review; disagreement > 1 point on the 5-point scale flags the task for the expert panel queue. | |
| 117 | +- Batch jobs must be idempotent and resumable (BullMQ job IDs = deterministic hash of task_id + prompt version). | |
| 118 | +- For current API details (batch endpoints, rate limits, model names), check https://platform.claude.com/docs (docs.claude.com redirects there) rather than relying on memory. A vetted snapshot lives in `docs/research/03-llm-rater-api.md`. | |
| 119 | + | |
| 120 | +## 7. Environments & Deployment | |
| 121 | + | |
| 122 | +### Environments | |
| 123 | +- `local` — developer machine, SQLite optional shortcut is **not** allowed; always Postgres via Docker (`infra/docker-compose.dev.yml`). | |
| 124 | +- `staging` — node **m3u96b**, exposed via **ngrok** (see below). | |
| 125 | +- `production` — airiskindex.io (target: VPS/managed later; staging setup is the current deployment). | |
| 126 | + | |
| 127 | +### Deploying to node m3u96b (staging/current prod) | |
| 128 | + | |
| 129 | +The app currently runs on the self-hosted node `m3u96b` and is exposed publicly through an ngrok tunnel mapped to `www.airiskindex.io`. | |
| 130 | + | |
| 131 | +**Process layout on m3u96b:** | |
| 132 | +- `airiskindex-web.service` (systemd) → `node apps/web/.next/standalone/server.js` on `127.0.0.1:3000` | |
| 133 | +- `airiskindex-worker.service` → BullMQ worker | |
| 134 | +- `postgres` + `redis` via Docker Compose (`infra/docker-compose.prod.yml`) | |
| 135 | +- `ngrok.service` → tunnel `127.0.0.1:3000` → public edge | |
| 136 | + | |
| 137 | +**Deploy procedure (scripted in `infra/deploy.sh`):** | |
| 138 | +```bash | |
| 139 | +ssh m3u96b | |
| 140 | +cd /srv/airiskindex | |
| 141 | +git pull --ff-only origin main | |
| 142 | +pnpm install --frozen-lockfile | |
| 143 | +pnpm build | |
| 144 | +pnpm db:migrate:deploy # prisma migrate deploy (no interactive) | |
| 145 | +sudo systemctl restart airiskindex-web airiskindex-worker | |
| 146 | +sudo systemctl status airiskindex-web --no-pager # verify healthy | |
| 147 | +curl -fsS http://127.0.0.1:3000/api/v1/health # must return {"ok":true,...} | |
| 148 | +``` | |
| 149 | + | |
| 150 | +**ngrok configuration (`infra/ngrok.yml`, copied to `/etc/ngrok/ngrok.yml` on the node):** | |
| 151 | +```yaml | |
| 152 | +version: 3 | |
| 153 | +agent: | |
| 154 | + authtoken: ${NGROK_AUTHTOKEN} # from env, never committed | |
| 155 | +endpoints: | |
| 156 | + - name: airiskindex | |
| 157 | + url: https://www.airiskindex.io # requires custom domain configured in ngrok dashboard + CNAME | |
| 158 | + upstream: | |
| 159 | + url: 3000 | |
| 160 | +``` | |
| 161 | +- The custom domain must be added in the ngrok dashboard and DNS `CNAME` for `www.airiskindex.io` pointed at the ngrok edge target they provide. Verify with `dig CNAME www.airiskindex.io`. | |
| 162 | +- ngrok runs as `ngrok.service` (systemd, `Restart=always`). Logs: `journalctl -u ngrok -f`. | |
| 163 | +- Health rule: after every deploy, hit the public URL, not just localhost — tunnel failures are the most common outage cause. | |
| 164 | +- Because the app sits behind ngrok, trust `X-Forwarded-*` headers: Next.js config already sets trusted proxy handling; do not remove it. Rate limiting keys off `x-forwarded-for` first IP. | |
| 165 | + | |
| 166 | +**Environment variables** (`/srv/airiskindex/.env`, template in `infra/.env.example`): | |
| 167 | +``` | |
| 168 | +DATABASE_URL=postgresql://... | |
| 169 | +REDIS_URL=redis://... | |
| 170 | +ANTHROPIC_API_KEY=... # rater pipeline | |
| 171 | +RATER_MODELS=... # comma-separated model IDs (multi-model rating panel) | |
| 172 | +NGROK_AUTHTOKEN=... | |
| 173 | +NEXTAUTH_URL=https://www.airiskindex.io | |
| 174 | +NEXTAUTH_SECRET=... | |
| 175 | +PUBLIC_BASE_URL=https://www.airiskindex.io | |
| 176 | +``` | |
| 177 | +Never print, log, or commit secrets. `infra/.env.example` lists keys with empty values only. | |
| 178 | + | |
| 179 | +### Rollback | |
| 180 | +```bash | |
| 181 | +ssh m3u96b | |
| 182 | +cd /srv/airiskindex | |
| 183 | +git checkout <previous-tag> | |
| 184 | +pnpm install --frozen-lockfile && pnpm build | |
| 185 | +sudo systemctl restart airiskindex-web airiskindex-worker | |
| 186 | +``` | |
| 187 | +DB rollbacks: only via forward-fix migrations. Nightly `pg_dump` to `/srv/backups` (retained 14 days) — verify the cron is alive when touching infra. | |
| 188 | + | |
| 189 | +## 8. Public API rules | |
| 190 | + | |
| 191 | +- `/api/v1/occupations` — list/search (paginated, cached 1h) | |
| 192 | +- `/api/v1/occupations/:code` — full score breakdown incl. sub-scores, CI bounds, task list, `index_version` | |
| 193 | +- `/api/v1/methodology` — machine-readable weights + version metadata | |
| 194 | +- Rate limit: 60 req/min unauthenticated, 600 with API key. Return `429` with `Retry-After`. | |
| 195 | +- Breaking changes require a new `/api/v2` — never mutate v1 response shapes. | |
| 196 | + | |
| 197 | +## 9. Methodology Integrity Rules (non-negotiable) | |
| 198 | + | |
| 199 | +1. Any change to weights, formulas, or rater prompts ⇒ bump `INDEX_VERSION`, add changelog entry, regenerate `docs/methodology/sensitivity/` outputs. | |
| 200 | +2. Scores shown anywhere must be traceable to a stored computation run (`score_runs` table) — no ad-hoc numbers. | |
| 201 | +3. Historical scores are immutable; recomputations create new runs, old runs remain queryable. | |
| 202 | +4. The public methodology doc (`docs/methodology/METHODOLOGY.md`) is the source of truth; code comments link to its section anchors. If code and doc disagree, stop and flag it — do not silently pick one. | |
| 203 | + | |
| 204 | +## 10. When Working in This Repo, Claude Should | |
| 205 | + | |
| 206 | +- Run `pnpm typecheck && pnpm lint && pnpm test` before declaring any task done. | |
| 207 | +- Touch `packages/scoring` only with accompanying tests and a methodology changelog note. | |
| 208 | +- Ask before: destructive migrations, changing index weights, editing ngrok/systemd config, or anything that alters public API shapes. | |
| 209 | +- Prefer small, reviewable commits with conventional-commit messages (`feat(scoring): ...`, `fix(api): ...`, `infra(deploy): ...`). | |
| 210 | +- Keep UI copy aligned with the "adaptation, not doom" tone guide. | |
| 211 | +- When uncertain about Anthropic API specifics (models, batch API, limits), consult the official docs instead of guessing. | |
added
README.md
+29 −0
@@ -0,0 +1,29 @@ | ||
| 1 | +# AI Risk Index — airiskindex.io | |
| 2 | + | |
| 3 | +The most methodologically rigorous, fully transparent AI job-exposure index. | |
| 4 | +Task-based scoring of occupations on AI-driven automation exposure, with three | |
| 5 | +separate sub-scores per occupation — **exposure**, **substitution**, **augmentation** — | |
| 6 | +each with confidence intervals, versioned methodology, and a public API. | |
| 7 | + | |
| 8 | +- Methodology (source of truth): [`docs/methodology/METHODOLOGY.md`](docs/methodology/METHODOLOGY.md) | |
| 9 | +- Research corpus grounding v1: [`docs/research/`](docs/research/README.md) | |
| 10 | +- Contributor rules: [`CLAUDE.md`](CLAUDE.md) | |
| 11 | + | |
| 12 | +## Quick start | |
| 13 | + | |
| 14 | +```bash | |
| 15 | +pnpm install | |
| 16 | +docker compose -f infra/docker-compose.dev.yml up -d # postgres 16 + redis | |
| 17 | +cp infra/.env.example .env # fill values | |
| 18 | +pnpm db:migrate && pnpm db:seed | |
| 19 | +pnpm dev # web on :3000 | |
| 20 | +``` | |
| 21 | + | |
| 22 | +Checks: `pnpm typecheck && pnpm lint && pnpm test` | |
| 23 | + | |
| 24 | +## Layout | |
| 25 | + | |
| 26 | +Monorepo (pnpm + Turborepo): `apps/web` (Next.js site + public API), `apps/worker` | |
| 27 | +(BullMQ jobs incl. the multi-model LLM rater), `apps/etl` (Python pipeline), | |
| 28 | +`packages/scoring` (pure, deterministic scoring engine), `packages/db` (Prisma), | |
| 29 | +`packages/ui`, `packages/config`. See CLAUDE.md §3. | |
added
apps/etl/Makefile
+21 −0
@@ -0,0 +1,21 @@ | ||
| 1 | +PYTHON ?= python3 | |
| 2 | +export PYTHONPATH := src | |
| 3 | + | |
| 4 | +.PHONY: pipeline download transform load test lint | |
| 5 | + | |
| 6 | +pipeline: download transform load | |
| 7 | + | |
| 8 | +download: | |
| 9 | + $(PYTHON) -m airiskindex_etl.download_onet | |
| 10 | + | |
| 11 | +transform: | |
| 12 | + @echo "TODO: raw -> derived transforms (task statements + importance ratings -> data/derived/)" | |
| 13 | + | |
| 14 | +load: | |
| 15 | + @echo "TODO: derived -> Postgres load (occupations + tasks via Prisma-compatible schema)" | |
| 16 | + | |
| 17 | +test: | |
| 18 | + $(PYTHON) -m pytest | |
| 19 | + | |
| 20 | +lint: | |
| 21 | + ruff check . | |
added
apps/etl/pyproject.toml
+19 −0
@@ -0,0 +1,19 @@ | ||
| 1 | +[project] | |
| 2 | +name = "airiskindex-etl" | |
| 3 | +version = "0.1.0" | |
| 4 | +description = "AI Risk Index data pipeline: raw source dumps -> derived artifacts -> Postgres" | |
| 5 | +requires-python = ">=3.12" | |
| 6 | +dependencies = [ | |
| 7 | + "requests>=2.32", | |
| 8 | + "pandas>=2.2", | |
| 9 | +] | |
| 10 | + | |
| 11 | +[project.optional-dependencies] | |
| 12 | +dev = ["pytest>=8", "ruff>=0.5"] | |
| 13 | + | |
| 14 | +[tool.ruff] | |
| 15 | +line-length = 100 | |
| 16 | +target-version = "py312" | |
| 17 | + | |
| 18 | +[tool.pytest.ini_options] | |
| 19 | +testpaths = ["tests"] | |
added
apps/etl/src/airiskindex_etl/__init__.py
+6 −0
@@ -0,0 +1,6 @@ | ||
| 1 | +"""AI Risk Index ETL: raw source dumps -> derived artifacts -> Postgres. | |
| 2 | + | |
| 3 | +Raw data is never edited in place (CLAUDE.md §1); every derived artifact gets a | |
| 4 | +manifest JSON (hashes + row counts) — only manifests are committed. | |
| 5 | +Source catalogue with verified URLs: docs/research/02-data-sources.md. | |
| 6 | +""" | |
added
apps/etl/src/airiskindex_etl/download_onet.py
+64 −0
@@ -0,0 +1,64 @@ | ||
| 1 | +"""Fetch the O*NET database text dump into data/raw/onet/ (immutable). | |
| 2 | + | |
| 3 | +O*NET 30.x — CC BY 4.0, attribution required. 30.x renamed | |
| 4 | +"Technology Skills" -> "Software Skills" and split Skills into | |
| 5 | +Essential/Transferable (see docs/research/02-data-sources.md §O*NET). | |
| 6 | +Check https://www.onetcenter.org/database.html for the current release | |
| 7 | +(31.0 expected late Aug 2026) before bumping ONET_VERSION. | |
| 8 | +""" | |
| 9 | + | |
| 10 | +from __future__ import annotations | |
| 11 | + | |
| 12 | +import hashlib | |
| 13 | +import json | |
| 14 | +import sys | |
| 15 | +import zipfile | |
| 16 | +from pathlib import Path | |
| 17 | + | |
| 18 | +import requests | |
| 19 | + | |
| 20 | +ONET_VERSION = "30_3" | |
| 21 | +ONET_URL = f"https://www.onetcenter.org/dl_files/database/db_{ONET_VERSION}_text.zip" | |
| 22 | + | |
| 23 | +# Some sources (notably BLS OEWS) block non-browser user agents; use a plain | |
| 24 | +# descriptive UA with contact info everywhere for consistency and courtesy. | |
| 25 | +USER_AGENT = "airiskindex-etl/0.1 (https://www.airiskindex.io; data pipeline)" | |
| 26 | + | |
| 27 | +REPO_ROOT = Path(__file__).resolve().parents[4] | |
| 28 | +RAW_DIR = REPO_ROOT / "data" / "raw" / "onet" | |
| 29 | + | |
| 30 | + | |
| 31 | +def download() -> Path: | |
| 32 | + RAW_DIR.mkdir(parents=True, exist_ok=True) | |
| 33 | + archive = RAW_DIR / f"db_{ONET_VERSION}_text.zip" | |
| 34 | + if archive.exists(): | |
| 35 | + print(f"already present: {archive}") | |
| 36 | + return archive | |
| 37 | + | |
| 38 | + print(f"downloading {ONET_URL}") | |
| 39 | + response = requests.get(ONET_URL, headers={"User-Agent": USER_AGENT}, timeout=300, stream=True) | |
| 40 | + response.raise_for_status() | |
| 41 | + with archive.open("wb") as fh: | |
| 42 | + for chunk in response.iter_content(chunk_size=1 << 20): | |
| 43 | + fh.write(chunk) | |
| 44 | + | |
| 45 | + with zipfile.ZipFile(archive) as zf: | |
| 46 | + zf.extractall(RAW_DIR) | |
| 47 | + | |
| 48 | + manifest = { | |
| 49 | + "source": ONET_URL, | |
| 50 | + "version": ONET_VERSION, | |
| 51 | + "sha256": hashlib.sha256(archive.read_bytes()).hexdigest(), | |
| 52 | + "files": sorted(p.name for p in RAW_DIR.iterdir()), | |
| 53 | + } | |
| 54 | + (RAW_DIR / "manifest.json").write_text(json.dumps(manifest, indent=2)) | |
| 55 | + print(f"downloaded and extracted to {RAW_DIR}") | |
| 56 | + return archive | |
| 57 | + | |
| 58 | + | |
| 59 | +if __name__ == "__main__": | |
| 60 | + try: | |
| 61 | + download() | |
| 62 | + except requests.RequestException as error: | |
| 63 | + print(f"download failed: {error}", file=sys.stderr) | |
| 64 | + sys.exit(1) | |
added
apps/etl/tests/test_smoke.py
+7 −0
@@ -0,0 +1,7 @@ | ||
| 1 | +from airiskindex_etl.download_onet import ONET_URL, ONET_VERSION, RAW_DIR | |
| 2 | + | |
| 3 | + | |
| 4 | +def test_onet_source_configuration() -> None: | |
| 5 | + assert ONET_VERSION.startswith("30_") | |
| 6 | + assert ONET_URL.endswith(f"db_{ONET_VERSION}_text.zip") | |
| 7 | + assert RAW_DIR.parts[-3:] == ("data", "raw", "onet") | |
added
apps/web/app/api/v1/health/route.ts
+11 −0
@@ -0,0 +1,11 @@ | ||
| 1 | +import { INDEX_VERSION } from "@airiskindex/scoring"; | |
| 2 | + | |
| 3 | +export const dynamic = "force-dynamic"; | |
| 4 | + | |
| 5 | +export function GET(): Response { | |
| 6 | + return Response.json({ | |
| 7 | + ok: true, | |
| 8 | + index_version: INDEX_VERSION, | |
| 9 | + time: new Date().toISOString(), | |
| 10 | + }); | |
| 11 | +} | |
added
apps/web/app/api/v1/methodology/route.ts
+27 −0
@@ -0,0 +1,27 @@ | ||
| 1 | +import { | |
| 2 | + DIMENSIONS, | |
| 3 | + EXPOSURE_DIMENSIONS, | |
| 4 | + HIGH_EXPOSURE_THRESHOLD, | |
| 5 | + INDEX_VERSION, | |
| 6 | + INVERTED_DIMENSIONS, | |
| 7 | + WEIGHTS, | |
| 8 | +} from "@airiskindex/scoring"; | |
| 9 | + | |
| 10 | +export function GET(): Response { | |
| 11 | + return Response.json( | |
| 12 | + { | |
| 13 | + index_version: INDEX_VERSION, | |
| 14 | + dimensions: DIMENSIONS, | |
| 15 | + weights: WEIGHTS, | |
| 16 | + inverted_dimensions: [...INVERTED_DIMENSIONS], | |
| 17 | + exposure_dimensions: EXPOSURE_DIMENSIONS, | |
| 18 | + high_exposure_threshold: HIGH_EXPOSURE_THRESHOLD, | |
| 19 | + scales: { | |
| 20 | + ratings: "1-5 per task and dimension, multi-model LLM panel with expert overrides", | |
| 21 | + scores: "0-100, each with low/score/high confidence bounds", | |
| 22 | + }, | |
| 23 | + documentation: "https://www.airiskindex.io/methodology", | |
| 24 | + }, | |
| 25 | + { headers: { "Cache-Control": "public, max-age=3600" } }, | |
| 26 | + ); | |
| 27 | +} | |
added
apps/web/app/api/v1/occupations/[code]/route.ts
+70 −0
@@ -0,0 +1,70 @@ | ||
| 1 | +import { prisma } from "@airiskindex/db"; | |
| 2 | +import { INDEX_VERSION } from "@airiskindex/scoring"; | |
| 3 | +import type { NextRequest } from "next/server"; | |
| 4 | + | |
| 5 | +export const dynamic = "force-dynamic"; | |
| 6 | + | |
| 7 | +export async function GET( | |
| 8 | + _request: NextRequest, | |
| 9 | + { params }: { params: { code: string } }, | |
| 10 | +): Promise<Response> { | |
| 11 | + try { | |
| 12 | + const occupation = await prisma.occupation.findUnique({ | |
| 13 | + where: { code: params.code }, | |
| 14 | + include: { | |
| 15 | + tasks: { | |
| 16 | + select: { id: true, statement: true, importance: true }, | |
| 17 | + orderBy: { id: "asc" }, | |
| 18 | + }, | |
| 19 | + }, | |
| 20 | + }); | |
| 21 | + if (!occupation) { | |
| 22 | + return Response.json({ error: "not_found" }, { status: 404 }); | |
| 23 | + } | |
| 24 | + | |
| 25 | + // Latest run's scores; historical runs stay queryable (CLAUDE.md §9). | |
| 26 | + const latest = await prisma.occupationScore.findFirst({ | |
| 27 | + where: { occupationCode: occupation.code }, | |
| 28 | + orderBy: { run: { createdAt: "desc" } }, | |
| 29 | + include: { run: true }, | |
| 30 | + }); | |
| 31 | + | |
| 32 | + return Response.json({ | |
| 33 | + index_version: latest?.run.indexVersion ?? INDEX_VERSION, | |
| 34 | + occupation: { | |
| 35 | + code: occupation.code, | |
| 36 | + title: occupation.title, | |
| 37 | + description: occupation.description, | |
| 38 | + esco_uri: occupation.escoUri, | |
| 39 | + rome_code: occupation.romeCode, | |
| 40 | + median_wage_cents: occupation.medianWageCents, | |
| 41 | + wage_currency: occupation.wageCurrency, | |
| 42 | + }, | |
| 43 | + scores: latest | |
| 44 | + ? { | |
| 45 | + run_id: latest.runId, | |
| 46 | + computed_at: latest.run.createdAt, | |
| 47 | + substitution: { | |
| 48 | + low: latest.substitutionLow, | |
| 49 | + score: latest.substitution, | |
| 50 | + high: latest.substitutionHigh, | |
| 51 | + }, | |
| 52 | + exposure: { | |
| 53 | + low: latest.exposureLow, | |
| 54 | + score: latest.exposure, | |
| 55 | + high: latest.exposureHigh, | |
| 56 | + }, | |
| 57 | + augmentation: { | |
| 58 | + low: latest.augmentationLow, | |
| 59 | + score: latest.augmentation, | |
| 60 | + high: latest.augmentationHigh, | |
| 61 | + }, | |
| 62 | + highly_exposed_task_share: latest.highlyExposedTaskShare, | |
| 63 | + } | |
| 64 | + : null, | |
| 65 | + tasks: occupation.tasks, | |
| 66 | + }); | |
| 67 | + } catch { | |
| 68 | + return Response.json({ error: "database_unavailable" }, { status: 503 }); | |
| 69 | + } | |
| 70 | +} | |
added
apps/web/app/api/v1/occupations/route.ts
+40 −0
@@ -0,0 +1,40 @@ | ||
| 1 | +import { prisma } from "@airiskindex/db"; | |
| 2 | +import { INDEX_VERSION } from "@airiskindex/scoring"; | |
| 3 | +import type { NextRequest } from "next/server"; | |
| 4 | + | |
| 5 | +export const dynamic = "force-dynamic"; | |
| 6 | + | |
| 7 | +export async function GET(request: NextRequest): Promise<Response> { | |
| 8 | + const { searchParams } = new URL(request.url); | |
| 9 | + const q = searchParams.get("q") ?? undefined; | |
| 10 | + const page = Math.max(1, Number(searchParams.get("page") ?? "1") || 1); | |
| 11 | + const perPage = Math.min(100, Math.max(1, Number(searchParams.get("per_page") ?? "25") || 25)); | |
| 12 | + | |
| 13 | + const where = q | |
| 14 | + ? { | |
| 15 | + OR: [ | |
| 16 | + { title: { contains: q, mode: "insensitive" as const } }, | |
| 17 | + { code: { startsWith: q } }, | |
| 18 | + ], | |
| 19 | + } | |
| 20 | + : {}; | |
| 21 | + | |
| 22 | + try { | |
| 23 | + const [total, items] = await Promise.all([ | |
| 24 | + prisma.occupation.count({ where }), | |
| 25 | + prisma.occupation.findMany({ | |
| 26 | + where, | |
| 27 | + orderBy: { code: "asc" }, | |
| 28 | + skip: (page - 1) * perPage, | |
| 29 | + take: perPage, | |
| 30 | + select: { code: true, title: true }, | |
| 31 | + }), | |
| 32 | + ]); | |
| 33 | + return Response.json( | |
| 34 | + { index_version: INDEX_VERSION, page, per_page: perPage, total, items }, | |
| 35 | + { headers: { "Cache-Control": "public, s-maxage=3600, stale-while-revalidate=600" } }, | |
| 36 | + ); | |
| 37 | + } catch { | |
| 38 | + return Response.json({ error: "database_unavailable" }, { status: 503 }); | |
| 39 | + } | |
| 40 | +} | |
added
apps/web/app/globals.css
+3 −0
@@ -0,0 +1,3 @@ | ||
| 1 | +@tailwind base; | |
| 2 | +@tailwind components; | |
| 3 | +@tailwind utilities; | |
added
apps/web/app/layout.tsx
+17 −0
@@ -0,0 +1,17 @@ | ||
| 1 | +import type { Metadata } from "next"; | |
| 2 | +import type { ReactNode } from "react"; | |
| 3 | +import "./globals.css"; | |
| 4 | + | |
| 5 | +export const metadata: Metadata = { | |
| 6 | + title: "AI Risk Index — task-based AI exposure scores for every occupation", | |
| 7 | + description: | |
| 8 | + "Transparent, versioned, task-based scores of how occupations are exposed to AI — with separate exposure, substitution and augmentation sub-scores and confidence intervals. Adaptation guidance, not doom.", | |
| 9 | +}; | |
| 10 | + | |
| 11 | +export default function RootLayout({ children }: { children: ReactNode }): JSX.Element { | |
| 12 | + return ( | |
| 13 | + <html lang="en"> | |
| 14 | + <body className="min-h-screen bg-white text-slate-900 antialiased">{children}</body> | |
| 15 | + </html> | |
| 16 | + ); | |
| 17 | +} | |
added
apps/web/app/page.tsx
+70 −0
@@ -0,0 +1,70 @@ | ||
| 1 | +import { ScoreBandPill } from "@airiskindex/ui"; | |
| 2 | +import { INDEX_VERSION } from "@airiskindex/scoring"; | |
| 3 | + | |
| 4 | +const CONCEPTS = [ | |
| 5 | + { | |
| 6 | + name: "Exposure", | |
| 7 | + description: | |
| 8 | + "AI is technically capable of performing the task. High exposure alone does not mean job loss — it means the occupation's tasks are changing.", | |
| 9 | + }, | |
| 10 | + { | |
| 11 | + name: "Substitution", | |
| 12 | + description: | |
| 13 | + "AI actually replaces the human performing the task, once cost, adoption and real-world barriers are accounted for.", | |
| 14 | + }, | |
| 15 | + { | |
| 16 | + name: "Augmentation", | |
| 17 | + description: | |
| 18 | + "AI assists the human, increasing productivity. For most occupations today, measured usage is augmentation, not replacement.", | |
| 19 | + }, | |
| 20 | +] as const; | |
| 21 | + | |
| 22 | +export default function HomePage(): JSX.Element { | |
| 23 | + return ( | |
| 24 | + <main className="mx-auto max-w-3xl px-6 py-16"> | |
| 25 | + <p className="text-sm font-medium uppercase tracking-wide text-slate-500"> | |
| 26 | + AI Risk Index · methodology {INDEX_VERSION} | |
| 27 | + </p> | |
| 28 | + <h1 className="mt-2 text-4xl font-bold tracking-tight"> | |
| 29 | + How is your occupation exposed to AI — task by task? | |
| 30 | + </h1> | |
| 31 | + <p className="mt-4 text-lg text-slate-600"> | |
| 32 | + We score occupations from their individual tasks, report three separate sub-scores with | |
| 33 | + confidence intervals, and publish every weight, prompt and formula. Built for adaptation | |
| 34 | + planning — not headlines. | |
| 35 | + </p> | |
| 36 | + | |
| 37 | + <div className="mt-8 flex flex-wrap gap-3"> | |
| 38 | + {/* Example occupation from the published methodology example */} | |
| 39 | + <ScoreBandPill label="Substitution" low={40.9} score={56.7} high={68.4} /> | |
| 40 | + <ScoreBandPill label="Exposure" low={42.0} score={57.1} high={74.1} /> | |
| 41 | + <ScoreBandPill label="Augmentation" low={46.9} score={71.9} high={84.4} /> | |
| 42 | + </div> | |
| 43 | + | |
| 44 | + <section className="mt-12 grid gap-6 sm:grid-cols-3"> | |
| 45 | + {CONCEPTS.map((concept) => ( | |
| 46 | + <div key={concept.name} className="rounded-lg border border-slate-200 p-5"> | |
| 47 | + <h2 className="font-semibold">{concept.name}</h2> | |
| 48 | + <p className="mt-2 text-sm text-slate-600">{concept.description}</p> | |
| 49 | + </div> | |
| 50 | + ))} | |
| 51 | + </section> | |
| 52 | + | |
| 53 | + <section className="mt-12 text-sm text-slate-600"> | |
| 54 | + <h2 className="text-base font-semibold text-slate-900">Public API</h2> | |
| 55 | + <ul className="mt-2 list-inside list-disc space-y-1"> | |
| 56 | + <li> | |
| 57 | + <code>/api/v1/occupations</code> — search occupations | |
| 58 | + </li> | |
| 59 | + <li> | |
| 60 | + <code>/api/v1/occupations/:code</code> — full score breakdown with sub-scores and CI | |
| 61 | + bounds | |
| 62 | + </li> | |
| 63 | + <li> | |
| 64 | + <code>/api/v1/methodology</code> — machine-readable weights and version metadata | |
| 65 | + </li> | |
| 66 | + </ul> | |
| 67 | + </section> | |
| 68 | + </main> | |
| 69 | + ); | |
| 70 | +} | |
added
apps/web/next-env.d.ts
+5 −0
@@ -0,0 +1,5 @@ | ||
| 1 | +/// <reference types="next" /> | |
| 2 | +/// <reference types="next/image-types/global" /> | |
| 3 | + | |
| 4 | +// NOTE: This file should not be edited | |
| 5 | +// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information. | |
added
apps/web/next.config.mjs
+9 −0
@@ -0,0 +1,9 @@ | ||
| 1 | +/** @type {import('next').NextConfig} */ | |
| 2 | +const nextConfig = { | |
| 3 | + // Workspace packages ship TypeScript sources directly. | |
| 4 | + transpilePackages: ["@airiskindex/scoring", "@airiskindex/db", "@airiskindex/ui"], | |
| 5 | + // Deployed as `node .next/standalone/server.js` behind ngrok (CLAUDE.md §7). | |
| 6 | + output: "standalone", | |
| 7 | +}; | |
| 8 | + | |
| 9 | +export default nextConfig; | |
added
apps/web/package.json
+28 −0
@@ -0,0 +1,28 @@ | ||
| 1 | +{ | |
| 2 | + "name": "@airiskindex/web", | |
| 3 | + "version": "0.0.0", | |
| 4 | + "private": true, | |
| 5 | + "scripts": { | |
| 6 | + "dev": "next dev", | |
| 7 | + "build": "next build", | |
| 8 | + "start": "next start", | |
| 9 | + "typecheck": "tsc --noEmit" | |
| 10 | + }, | |
| 11 | + "dependencies": { | |
| 12 | + "@airiskindex/db": "workspace:*", | |
| 13 | + "@airiskindex/scoring": "workspace:*", | |
| 14 | + "@airiskindex/ui": "workspace:*", | |
| 15 | + "next": "^14.2.5", | |
| 16 | + "react": "^18.3.1", | |
| 17 | + "react-dom": "^18.3.1" | |
| 18 | + }, | |
| 19 | + "devDependencies": { | |
| 20 | + "@types/node": "^20.14.11", | |
| 21 | + "@types/react": "^18.3.3", | |
| 22 | + "@types/react-dom": "^18.3.0", | |
| 23 | + "autoprefixer": "^10.4.19", | |
| 24 | + "postcss": "^8.4.39", | |
| 25 | + "tailwindcss": "^3.4.6", | |
| 26 | + "typescript": "^5.5.4" | |
| 27 | + } | |
| 28 | +} | |
added
apps/web/postcss.config.mjs
+6 −0
@@ -0,0 +1,6 @@ | ||
| 1 | +export default { | |
| 2 | + plugins: { | |
| 3 | + tailwindcss: {}, | |
| 4 | + autoprefixer: {}, | |
| 5 | + }, | |
| 6 | +}; | |
added
apps/web/tailwind.config.ts
+9 −0
@@ -0,0 +1,9 @@ | ||
| 1 | +import type { Config } from "tailwindcss"; | |
| 2 | + | |
| 3 | +export default { | |
| 4 | + content: ["./app/**/*.{ts,tsx}", "../../packages/ui/src/**/*.{ts,tsx}"], | |
| 5 | + theme: { | |
| 6 | + extend: {}, | |
| 7 | + }, | |
| 8 | + plugins: [], | |
| 9 | +} satisfies Config; | |
added
apps/web/tsconfig.json
+10 −0
@@ -0,0 +1,10 @@ | ||
| 1 | +{ | |
| 2 | + "extends": "../../packages/config/tsconfig/nextjs.json", | |
| 3 | + "compilerOptions": { | |
| 4 | + "paths": { | |
| 5 | + "@/*": ["./*"] | |
| 6 | + } | |
| 7 | + }, | |
| 8 | + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], | |
| 9 | + "exclude": ["node_modules"] | |
| 10 | +} | |
added
apps/worker/package.json
+25 −0
@@ -0,0 +1,25 @@ | ||
| 1 | +{ | |
| 2 | + "name": "@airiskindex/worker", | |
| 3 | + "version": "0.0.0", | |
| 4 | + "private": true, | |
| 5 | + "type": "module", | |
| 6 | + "scripts": { | |
| 7 | + "dev": "tsx watch src/index.ts", | |
| 8 | + "start": "tsx src/index.ts", | |
| 9 | + "score:recompute": "tsx src/scripts/recompute.ts", | |
| 10 | + "typecheck": "tsc --noEmit" | |
| 11 | + }, | |
| 12 | + "dependencies": { | |
| 13 | + "@airiskindex/db": "workspace:*", | |
| 14 | + "@airiskindex/scoring": "workspace:*", | |
| 15 | + "@anthropic-ai/sdk": ">=0.32.1 <1", | |
| 16 | + "bullmq": "^5.8.7", | |
| 17 | + "ioredis": "^5.4.1", | |
| 18 | + "zod": "^3.23.8" | |
| 19 | + }, | |
| 20 | + "devDependencies": { | |
| 21 | + "@types/node": "^20.14.11", | |
| 22 | + "tsx": "^4.16.2", | |
| 23 | + "typescript": "^5.5.4" | |
| 24 | + } | |
| 25 | +} | |
added
apps/worker/src/config.ts
+15 −0
@@ -0,0 +1,15 @@ | ||
| 1 | +/** | |
| 2 | + * Rater model panel — comma-separated model IDs from the environment, never | |
| 3 | + * hardcoded (CLAUDE.md §6). Multi-model rating is required: single-model | |
| 4 | + * exposure ratings show up to 19× spread across frontier raters | |
| 5 | + * (docs/research/01-existing-indices.md §7.2). | |
| 6 | + */ | |
| 7 | +export const RATER_MODELS: readonly string[] = (process.env.RATER_MODELS ?? "") | |
| 8 | + .split(",") | |
| 9 | + .map((value) => value.trim()) | |
| 10 | + .filter(Boolean); | |
| 11 | + | |
| 12 | +/** Bumping this invalidates cached ratings (CLAUDE.md §6). */ | |
| 13 | +export const RATER_PROMPT_VERSION = "v1"; | |
| 14 | + | |
| 15 | +export const REDIS_URL = process.env.REDIS_URL ?? "redis://127.0.0.1:6379"; | |
added
apps/worker/src/index.ts
+85 −0
@@ -0,0 +1,85 @@ | ||
| 1 | +import { Queue, Worker } from "bullmq"; | |
| 2 | +import IORedis from "ioredis"; | |
| 3 | +import { prisma } from "@airiskindex/db"; | |
| 4 | +import { RATER_MODELS, RATER_PROMPT_VERSION, REDIS_URL } from "./config"; | |
| 5 | +import { ingestBatchResults, isBatchComplete, submitRatingBatch, type RatingTask } from "./raters/batch"; | |
| 6 | +import { ratingJobId } from "./raters/job-id"; | |
| 7 | + | |
| 8 | +const connection = new IORedis(REDIS_URL, { maxRetriesPerRequest: null }); | |
| 9 | + | |
| 10 | +export const ratingQueue = new Queue("rating", { connection }); | |
| 11 | + | |
| 12 | +interface SubmitPayload { | |
| 13 | + model: string; | |
| 14 | +} | |
| 15 | + | |
| 16 | +interface PollPayload { | |
| 17 | + model: string; | |
| 18 | + batchId: string; | |
| 19 | + jobIdToTaskId: Record<string, string>; | |
| 20 | +} | |
| 21 | + | |
| 22 | +const worker = new Worker( | |
| 23 | + "rating", | |
| 24 | + async (job) => { | |
| 25 | + if (job.name === "submit") { | |
| 26 | + const { model } = job.data as SubmitPayload; | |
| 27 | + const tasks = await prisma.task.findMany({ | |
| 28 | + where: { | |
| 29 | + ratings: { none: { model, promptVersion: RATER_PROMPT_VERSION } }, | |
| 30 | + }, | |
| 31 | + include: { occupation: { select: { title: true } } }, | |
| 32 | + }); | |
| 33 | + if (tasks.length === 0) return { submitted: 0 }; | |
| 34 | + | |
| 35 | + const ratingTasks: RatingTask[] = tasks.map((task) => ({ | |
| 36 | + taskId: task.id, | |
| 37 | + occupationTitle: task.occupation.title, | |
| 38 | + statement: task.statement, | |
| 39 | + })); | |
| 40 | + const batchId = await submitRatingBatch(ratingTasks, model); | |
| 41 | + const jobIdToTaskId = Object.fromEntries( | |
| 42 | + ratingTasks.map((task) => [ratingJobId(task.taskId, model, RATER_PROMPT_VERSION), task.taskId]), | |
| 43 | + ); | |
| 44 | + await ratingQueue.add( | |
| 45 | + "poll", | |
| 46 | + { model, batchId, jobIdToTaskId } satisfies PollPayload, | |
| 47 | + { jobId: `poll:${batchId}`, delay: 60_000, attempts: 60, backoff: { type: "fixed", delay: 60_000 } }, | |
| 48 | + ); | |
| 49 | + return { submitted: ratingTasks.length, batchId }; | |
| 50 | + } | |
| 51 | + | |
| 52 | + if (job.name === "poll") { | |
| 53 | + const { model, batchId, jobIdToTaskId } = job.data as PollPayload; | |
| 54 | + if (!(await isBatchComplete(batchId))) { | |
| 55 | + throw new Error(`batch ${batchId} still processing`); // retried via backoff | |
| 56 | + } | |
| 57 | + return ingestBatchResults(batchId, model, new Map(Object.entries(jobIdToTaskId))); | |
| 58 | + } | |
| 59 | + | |
| 60 | + throw new Error(`unknown job ${job.name}`); | |
| 61 | + }, | |
| 62 | + { connection }, | |
| 63 | +); | |
| 64 | + | |
| 65 | +worker.on("failed", (job, error) => { | |
| 66 | + console.error(`[worker] job ${job?.name}:${job?.id} failed:`, error.message); | |
| 67 | +}); | |
| 68 | + | |
| 69 | +async function enqueueSubmitJobs(): Promise<void> { | |
| 70 | + if (RATER_MODELS.length < 2) { | |
| 71 | + console.warn( | |
| 72 | + "[worker] RATER_MODELS has fewer than 2 models — multi-model rating is required (CLAUDE.md §6).", | |
| 73 | + ); | |
| 74 | + } | |
| 75 | + for (const model of RATER_MODELS) { | |
| 76 | + await ratingQueue.add( | |
| 77 | + "submit", | |
| 78 | + { model } satisfies SubmitPayload, | |
| 79 | + { jobId: `submit:${model}:${RATER_PROMPT_VERSION}` }, | |
| 80 | + ); | |
| 81 | + } | |
| 82 | +} | |
| 83 | + | |
| 84 | +enqueueSubmitJobs().catch((error) => console.error("[worker] enqueue failed:", error)); | |
| 85 | +console.log(`[worker] rating worker up — prompt ${RATER_PROMPT_VERSION}, models: ${RATER_MODELS.join(", ") || "(none)"}`); | |
added
apps/worker/src/raters/batch.ts
+121 −0
@@ -0,0 +1,121 @@ | ||
| 1 | +import { readFileSync } from "node:fs"; | |
| 2 | +import Anthropic from "@anthropic-ai/sdk"; | |
| 3 | +import { prisma } from "@airiskindex/db"; | |
| 4 | +import { RATER_PROMPT_VERSION } from "../config"; | |
| 5 | +import { ratingJobId } from "./job-id"; | |
| 6 | +import { RATING_OUTPUT_JSON_SCHEMA, ratingResponseSchema } from "./schema"; | |
| 7 | + | |
| 8 | +// Batch rating flow (docs/research/03-llm-rater-api.md): one Message Batches | |
| 9 | +// request per task × model, custom_id = deterministic job ID, 1h-cached rubric | |
| 10 | +// in the system block, structured JSON output. 50% batch discount; results | |
| 11 | +// retrievable for 29 days. | |
| 12 | + | |
| 13 | +const anthropic = new Anthropic(); | |
| 14 | + | |
| 15 | +const rubric = readFileSync(new URL("./prompts/task-rating-v1.md", import.meta.url), "utf8"); | |
| 16 | + | |
| 17 | +export interface RatingTask { | |
| 18 | + taskId: string; | |
| 19 | + occupationTitle: string; | |
| 20 | + statement: string; | |
| 21 | +} | |
| 22 | + | |
| 23 | +export async function submitRatingBatch(tasks: RatingTask[], model: string): Promise<string> { | |
| 24 | + const batch = await anthropic.messages.batches.create({ | |
| 25 | + requests: tasks.map((task) => ({ | |
| 26 | + custom_id: ratingJobId(task.taskId, model, RATER_PROMPT_VERSION), | |
| 27 | + params: { | |
| 28 | + model, | |
| 29 | + max_tokens: 2048, | |
| 30 | + system: [ | |
| 31 | + { | |
| 32 | + type: "text" as const, | |
| 33 | + text: rubric, | |
| 34 | + cache_control: { type: "ephemeral" as const }, | |
| 35 | + }, | |
| 36 | + ], | |
| 37 | + messages: [ | |
| 38 | + { | |
| 39 | + role: "user" as const, | |
| 40 | + content: `Occupation: ${task.occupationTitle}\nTask statement: ${task.statement}\n\nRate this task per the rubric.`, | |
| 41 | + }, | |
| 42 | + ], | |
| 43 | + // Structured outputs (GA). Kept as an untyped extension so the code | |
| 44 | + // compiles across SDK versions; see docs/research/03-llm-rater-api.md. | |
| 45 | + ...({ | |
| 46 | + output_config: { | |
| 47 | + format: { type: "json_schema", schema: RATING_OUTPUT_JSON_SCHEMA }, | |
| 48 | + }, | |
| 49 | + } as Record<string, unknown>), | |
| 50 | + } as unknown as Anthropic.Messages.MessageCreateParamsNonStreaming, | |
| 51 | + })), | |
| 52 | + }); | |
| 53 | + return batch.id; | |
| 54 | +} | |
| 55 | + | |
| 56 | +export async function isBatchComplete(batchId: string): Promise<boolean> { | |
| 57 | + const batch = await anthropic.messages.batches.retrieve(batchId); | |
| 58 | + return batch.processing_status === "ended"; | |
| 59 | +} | |
| 60 | + | |
| 61 | +/** | |
| 62 | + * Ingest batch results: store the raw response and the parsed per-dimension | |
| 63 | + * scores (full audit trail, CLAUDE.md §6). Idempotent via the unique | |
| 64 | + * (taskId, dimension, model, promptVersion, sampleIndex) constraint. | |
| 65 | + */ | |
| 66 | +export async function ingestBatchResults( | |
| 67 | + batchId: string, | |
| 68 | + model: string, | |
| 69 | + jobIdToTaskId: ReadonlyMap<string, string>, | |
| 70 | +): Promise<{ ingested: number; failed: number }> { | |
| 71 | + let ingested = 0; | |
| 72 | + let failed = 0; | |
| 73 | + | |
| 74 | + for await (const entry of await anthropic.messages.batches.results(batchId)) { | |
| 75 | + const taskId = jobIdToTaskId.get(entry.custom_id); | |
| 76 | + if (!taskId || entry.result.type !== "succeeded") { | |
| 77 | + failed += 1; | |
| 78 | + continue; | |
| 79 | + } | |
| 80 | + const message = entry.result.message; | |
| 81 | + const textBlock = message.content.find((block) => block.type === "text"); | |
| 82 | + if (!textBlock || textBlock.type !== "text") { | |
| 83 | + failed += 1; | |
| 84 | + continue; | |
| 85 | + } | |
| 86 | + | |
| 87 | + const parsed = ratingResponseSchema.safeParse(JSON.parse(textBlock.text)); | |
| 88 | + if (!parsed.success) { | |
| 89 | + failed += 1; | |
| 90 | + continue; | |
| 91 | + } | |
| 92 | + | |
| 93 | + for (const [dimension, value] of Object.entries(parsed.data)) { | |
| 94 | + await prisma.taskRating.upsert({ | |
| 95 | + where: { | |
| 96 | + taskId_dimension_model_promptVersion_sampleIndex: { | |
| 97 | + taskId, | |
| 98 | + dimension, | |
| 99 | + model, | |
| 100 | + promptVersion: RATER_PROMPT_VERSION, | |
| 101 | + sampleIndex: 0, | |
| 102 | + }, | |
| 103 | + }, | |
| 104 | + update: {}, | |
| 105 | + create: { | |
| 106 | + taskId, | |
| 107 | + dimension, | |
| 108 | + model, | |
| 109 | + promptVersion: RATER_PROMPT_VERSION, | |
| 110 | + sampleIndex: 0, | |
| 111 | + rating: value.rating, | |
| 112 | + rationale: value.rationale, | |
| 113 | + rawResponse: JSON.parse(JSON.stringify(message)), | |
| 114 | + }, | |
| 115 | + }); | |
| 116 | + ingested += 1; | |
| 117 | + } | |
| 118 | + } | |
| 119 | + | |
| 120 | + return { ingested, failed }; | |
| 121 | +} | |
added
apps/worker/src/raters/job-id.ts
+14 −0
@@ -0,0 +1,14 @@ | ||
| 1 | +import { createHash } from "node:crypto"; | |
| 2 | + | |
| 3 | +/** | |
| 4 | + * Deterministic ID used both as the BullMQ job ID and the Message Batches | |
| 5 | + * `custom_id`, so retries and batch reconciliation are idempotent | |
| 6 | + * (CLAUDE.md §6; docs/research/03-llm-rater-api.md). One request rates all | |
| 7 | + * dimensions of one task with one model. | |
| 8 | + */ | |
| 9 | +export function ratingJobId(taskId: string, model: string, promptVersion: string): string { | |
| 10 | + return createHash("sha256") | |
| 11 | + .update(`${taskId}:${model}:${promptVersion}`) | |
| 12 | + .digest("hex") | |
| 13 | + .slice(0, 32); | |
| 14 | +} | |
added
apps/worker/src/raters/prompts/task-rating-v1.md
+70 −0
@@ -0,0 +1,70 @@ | ||
| 1 | +# Task rating rubric — v1 | |
| 2 | + | |
| 3 | +You are an expert rater for the AI Risk Index (airiskindex.io). You rate one | |
| 4 | +occupational task statement (from O*NET) on six dimensions, each on a 1–5 | |
| 5 | +integer scale. Be calibrated and conservative: rate what current, generally | |
| 6 | +available AI systems (including tool-using agents) can do **today**, not what | |
| 7 | +might be possible soon. Justify every rating in one or two sentences grounded | |
| 8 | +in the task statement itself. | |
| 9 | + | |
| 10 | +## Dimensions | |
| 11 | + | |
| 12 | +### automatability (1–5) | |
| 13 | +Could current AI perform this task end-to-end with **at least 50% time saving | |
| 14 | +at equal quality** (Eloundou et al. threshold)? | |
| 15 | +- 1 — No meaningful part of the task can be automated today. | |
| 16 | +- 3 — Roughly half of the task could be automated with significant setup. | |
| 17 | +- 5 — The full task meets the ≥50%-time-saving-at-equal-quality bar with off-the-shelf systems. | |
| 18 | + | |
| 19 | +### feasibility (1–5) | |
| 20 | +Do **deployed products demonstrably perform this task reliably today**? Distinguish | |
| 21 | +conceivable from deployable: benchmark results and demos rate lower than | |
| 22 | +production systems in real organizations. | |
| 23 | +- 1 — No product does this; research-stage only. | |
| 24 | +- 3 — Products exist but with material error rates or narrow scope. | |
| 25 | +- 5 — Mature products perform this reliably in production at scale. | |
| 26 | + | |
| 27 | +### cost_ratio (1–5) | |
| 28 | +Compare the AI cost per task-equivalent (inference + integration + oversight) | |
| 29 | +to the loaded human wage for the same output. | |
| 30 | +- 1 — AI is more expensive than the human, all-in. | |
| 31 | +- 3 — Roughly comparable cost. | |
| 32 | +- 5 — AI is at least an order of magnitude cheaper. | |
| 33 | + | |
| 34 | +### barriers (1–5) — NOTE: higher = MORE protected | |
| 35 | +Strength of adoption barriers: licensing/authorization requirements, liability | |
| 36 | +and error-cost asymmetry, regulatory coverage of the automation itself, | |
| 37 | +human-contact requirement, organizational friction. | |
| 38 | +- 1 — No meaningful barriers; nothing prevents substitution. | |
| 39 | +- 3 — Some friction (oversight requirements, customer preference for humans). | |
| 40 | +- 5 — Hard barriers: a licensed human must legally perform or sign off on the task. | |
| 41 | + | |
| 42 | +### adoption_velocity (1–5) | |
| 43 | +How fast and deep are the sectors where this task occurs actually adopting AI | |
| 44 | +(agents in production, measured displacement), per public adoption data? | |
| 45 | +- 1 — Laggard sectors (small firms, physical, low digitization). | |
| 46 | +- 3 — Middling adoption, pilots common, production rare. | |
| 47 | +- 5 — Fast, deep adoption (information, finance, professional services patterns). | |
| 48 | + | |
| 49 | +### augmentation (1–5) | |
| 50 | +Independently of replacement: does AI **assist** a human doing this task, | |
| 51 | +raising their productivity? High augmentation and low automatability can | |
| 52 | +coexist (assistive drafting for a task requiring human judgment). | |
| 53 | +- 1 — AI offers no meaningful assistance. | |
| 54 | +- 3 — Useful assistance on parts of the task. | |
| 55 | +- 5 — AI transforms productivity on this task while the human stays in the loop. | |
| 56 | + | |
| 57 | +## Output | |
| 58 | + | |
| 59 | +Return ONLY a JSON object of this shape (no prose outside JSON): | |
| 60 | + | |
| 61 | +```json | |
| 62 | +{ | |
| 63 | + "automatability": { "rating": 1, "rationale": "..." }, | |
| 64 | + "feasibility": { "rating": 1, "rationale": "..." }, | |
| 65 | + "cost_ratio": { "rating": 1, "rationale": "..." }, | |
| 66 | + "barriers": { "rating": 1, "rationale": "..." }, | |
| 67 | + "adoption_velocity": { "rating": 1, "rationale": "..." }, | |
| 68 | + "augmentation": { "rating": 1, "rationale": "..." } | |
| 69 | +} | |
| 70 | +``` | |
added
apps/worker/src/raters/schema.ts
+45 −0
@@ -0,0 +1,45 @@ | ||
| 1 | +import { z } from "zod"; | |
| 2 | +import { DIMENSIONS } from "@airiskindex/scoring"; | |
| 3 | + | |
| 4 | +const RATED_DIMENSIONS = [...DIMENSIONS, "augmentation"] as const; | |
| 5 | + | |
| 6 | +const dimensionRating = z.object({ | |
| 7 | + rating: z.number().int().min(1).max(5), | |
| 8 | + rationale: z.string(), | |
| 9 | +}); | |
| 10 | + | |
| 11 | +/** Parsed shape of one rater response (all six dimensions of one task). */ | |
| 12 | +export const ratingResponseSchema = z.object( | |
| 13 | + Object.fromEntries(RATED_DIMENSIONS.map((dimension) => [dimension, dimensionRating])) as Record< | |
| 14 | + (typeof RATED_DIMENSIONS)[number], | |
| 15 | + typeof dimensionRating | |
| 16 | + >, | |
| 17 | +); | |
| 18 | + | |
| 19 | +export type RatingResponse = z.infer<typeof ratingResponseSchema>; | |
| 20 | + | |
| 21 | +/** | |
| 22 | + * JSON schema for the structured-outputs feature (score as enum — numeric | |
| 23 | + * min/max is unsupported there; docs/research/03-llm-rater-api.md). | |
| 24 | + */ | |
| 25 | +export const RATING_OUTPUT_JSON_SCHEMA = { | |
| 26 | + type: "object", | |
| 27 | + additionalProperties: false, | |
| 28 | + required: [...RATED_DIMENSIONS], | |
| 29 | + properties: Object.fromEntries( | |
| 30 | + RATED_DIMENSIONS.map((dimension) => [ | |
| 31 | + dimension, | |
| 32 | + { | |
| 33 | + type: "object", | |
| 34 | + additionalProperties: false, | |
| 35 | + required: ["rating", "rationale"], | |
| 36 | + properties: { | |
| 37 | + rating: { enum: [1, 2, 3, 4, 5] }, | |
| 38 | + rationale: { type: "string" }, | |
| 39 | + }, | |
| 40 | + }, | |
| 41 | + ]), | |
| 42 | + ), | |
| 43 | +} as const; | |
| 44 | + | |
| 45 | +export { RATED_DIMENSIONS }; | |
added
apps/worker/src/scripts/recompute.ts
+136 −0
@@ -0,0 +1,136 @@ | ||
| 1 | +import { prisma } from "@airiskindex/db"; | |
| 2 | +import { | |
| 3 | + INDEX_VERSION, | |
| 4 | + scoreOccupation, | |
| 5 | + type RatingBand, | |
| 6 | + type TaskInput, | |
| 7 | + type TaskRatings, | |
| 8 | +} from "@airiskindex/scoring"; | |
| 9 | +import { RATED_DIMENSIONS } from "../raters/schema"; | |
| 10 | +import { RATER_MODELS, RATER_PROMPT_VERSION } from "../config"; | |
| 11 | + | |
| 12 | +// Full index recomputation (`pnpm score:recompute`): builds rating bands from | |
| 13 | +// the multi-model panel (min/mean/max across models — METHODOLOGY.md §3), | |
| 14 | +// applies expert overrides, scores every occupation, and writes ONE new | |
| 15 | +// immutable ScoreRun. Historical runs are never mutated (CLAUDE.md §9). | |
| 16 | + | |
| 17 | +function bandFromPanel(values: number[]): RatingBand { | |
| 18 | + const low = Math.min(...values); | |
| 19 | + const high = Math.max(...values); | |
| 20 | + const mid = values.reduce((sum, value) => sum + value, 0) / values.length; | |
| 21 | + return { low, mid, high }; | |
| 22 | +} | |
| 23 | + | |
| 24 | +async function main(): Promise<void> { | |
| 25 | + const occupations = await prisma.occupation.findMany({ | |
| 26 | + include: { | |
| 27 | + tasks: { | |
| 28 | + include: { | |
| 29 | + ratings: { where: { promptVersion: RATER_PROMPT_VERSION } }, | |
| 30 | + overrides: true, | |
| 31 | + }, | |
| 32 | + }, | |
| 33 | + }, | |
| 34 | + }); | |
| 35 | + | |
| 36 | + const run = await prisma.scoreRun.create({ | |
| 37 | + data: { | |
| 38 | + indexVersion: INDEX_VERSION, | |
| 39 | + raterPromptVersion: RATER_PROMPT_VERSION, | |
| 40 | + raterModels: [...RATER_MODELS], | |
| 41 | + }, | |
| 42 | + }); | |
| 43 | + | |
| 44 | + let scoredOccupations = 0; | |
| 45 | + let skippedTasks = 0; | |
| 46 | + | |
| 47 | + for (const occupation of occupations) { | |
| 48 | + const inputs: TaskInput[] = []; | |
| 49 | + | |
| 50 | + for (const task of occupation.tasks) { | |
| 51 | + const bands: Partial<Record<(typeof RATED_DIMENSIONS)[number], RatingBand>> = {}; | |
| 52 | + let complete = true; | |
| 53 | + | |
| 54 | + for (const dimension of RATED_DIMENSIONS) { | |
| 55 | + const override = task.overrides.find((entry) => entry.dimension === dimension); | |
| 56 | + if (override) { | |
| 57 | + bands[dimension] = { | |
| 58 | + low: override.ratingLow, | |
| 59 | + mid: override.ratingMid, | |
| 60 | + high: override.ratingHigh, | |
| 61 | + }; | |
| 62 | + continue; | |
| 63 | + } | |
| 64 | + const values = task.ratings | |
| 65 | + .filter((entry) => entry.dimension === dimension) | |
| 66 | + .map((entry) => entry.rating); | |
| 67 | + if (values.length === 0) { | |
| 68 | + complete = false; | |
| 69 | + break; | |
| 70 | + } | |
| 71 | + bands[dimension] = bandFromPanel(values); | |
| 72 | + } | |
| 73 | + | |
| 74 | + if (!complete) { | |
| 75 | + skippedTasks += 1; | |
| 76 | + continue; | |
| 77 | + } | |
| 78 | + inputs.push({ | |
| 79 | + taskId: task.id, | |
| 80 | + importance: task.importance ?? undefined, | |
| 81 | + ratings: bands as TaskRatings, | |
| 82 | + }); | |
| 83 | + } | |
| 84 | + | |
| 85 | + if (inputs.length === 0) continue; | |
| 86 | + | |
| 87 | + const scores = scoreOccupation(inputs); | |
| 88 | + await prisma.$transaction([ | |
| 89 | + prisma.occupationScore.create({ | |
| 90 | + data: { | |
| 91 | + runId: run.id, | |
| 92 | + occupationCode: occupation.code, | |
| 93 | + substitutionLow: scores.substitution.low, | |
| 94 | + substitution: scores.substitution.score, | |
| 95 | + substitutionHigh: scores.substitution.high, | |
| 96 | + exposureLow: scores.exposure.low, | |
| 97 | + exposure: scores.exposure.score, | |
| 98 | + exposureHigh: scores.exposure.high, | |
| 99 | + augmentationLow: scores.augmentation.low, | |
| 100 | + augmentation: scores.augmentation.score, | |
| 101 | + augmentationHigh: scores.augmentation.high, | |
| 102 | + highlyExposedTaskShare: scores.highlyExposedTaskShare, | |
| 103 | + }, | |
| 104 | + }), | |
| 105 | + ...scores.tasks.map((task) => | |
| 106 | + prisma.taskScore.create({ | |
| 107 | + data: { | |
| 108 | + runId: run.id, | |
| 109 | + taskId: task.taskId, | |
| 110 | + substitutionLow: task.substitution.low, | |
| 111 | + substitution: task.substitution.score, | |
| 112 | + substitutionHigh: task.substitution.high, | |
| 113 | + exposureLow: task.exposure.low, | |
| 114 | + exposure: task.exposure.score, | |
| 115 | + exposureHigh: task.exposure.high, | |
| 116 | + augmentationLow: task.augmentation.low, | |
| 117 | + augmentation: task.augmentation.score, | |
| 118 | + augmentationHigh: task.augmentation.high, | |
| 119 | + }, | |
| 120 | + }), | |
| 121 | + ), | |
| 122 | + ]); | |
| 123 | + scoredOccupations += 1; | |
| 124 | + } | |
| 125 | + | |
| 126 | + console.log( | |
| 127 | + `Run ${run.id} (index ${INDEX_VERSION}, prompt ${RATER_PROMPT_VERSION}): scored ${scoredOccupations}/${occupations.length} occupations; skipped ${skippedTasks} unrated tasks.`, | |
| 128 | + ); | |
| 129 | +} | |
| 130 | + | |
| 131 | +main() | |
| 132 | + .catch((error) => { | |
| 133 | + console.error(error); | |
| 134 | + process.exitCode = 1; | |
| 135 | + }) | |
| 136 | + .finally(() => prisma.$disconnect()); | |
added
apps/worker/tsconfig.json
+4 −0
@@ -0,0 +1,4 @@ | ||
| 1 | +{ | |
| 2 | + "extends": "../../packages/config/tsconfig/base.json", | |
| 3 | + "include": ["src"] | |
| 4 | +} | |
added
data/derived/expert_overrides/.gitkeep
+0 −0
added
data/derived/ratings/.gitkeep
+0 −0
added
data/raw/.gitkeep
+0 −0
added
docs/methodology/CHANGELOG.md
+19 −0
@@ -0,0 +1,19 @@ | ||
| 1 | +# Methodology Changelog | |
| 2 | + | |
| 3 | +All notable changes to the scoring methodology. Every entry corresponds to an | |
| 4 | +`INDEX_VERSION` (semver) in `packages/scoring/src/version.ts`. | |
| 5 | + | |
| 6 | +## [1.0.0] — UNRELEASED (draft) | |
| 7 | + | |
| 8 | +Initial methodology. | |
| 9 | + | |
| 10 | +- Task-based scoring on O*NET 30.x task statements, importance-weighted aggregation | |
| 11 | + to occupations (O*NET-SOC 2019). | |
| 12 | +- Five dimensions: automatability 0.35, feasibility 0.20, cost_ratio 0.15, | |
| 13 | + barriers 0.20 (inverted), adoption_velocity 0.10. | |
| 14 | +- Three sub-scores per occupation: exposure, substitution (headline composite), | |
| 15 | + augmentation — augmentation rated separately per task, outside the composite. | |
| 16 | +- Multi-model LLM rater panel (≥2 frontier models via `RATER_MODELS`); confidence | |
| 17 | + bounds (`score_low`/`score_high`) derived from rater disagreement envelopes. | |
| 18 | +- 5% human review sample; expert Delphi overrides replace LLM bands where triggered. | |
| 19 | +- Grounding research: `docs/research/01-…04-*.md` (compiled 2026-08-05). | |
added
docs/methodology/METHODOLOGY.md
+173 −0
@@ -0,0 +1,173 @@ | ||
| 1 | +# AI Risk Index — Methodology v1.0.0 (DRAFT) | |
| 2 | + | |
| 3 | +> Status: **draft, unreleased**. This document is the source of truth for the scoring | |
| 4 | +> methodology (see CLAUDE.md §9). Code in `packages/scoring` links to the section | |
| 5 | +> anchors below; if code and this document disagree, stop and flag it. | |
| 6 | +> | |
| 7 | +> Related work and evidence base: `docs/research/01-existing-indices.md` (indices), | |
| 8 | +> `02-data-sources.md` (data), `04-landscape-and-evidence.md` (adoption & barriers evidence). | |
| 9 | + | |
| 10 | +--- | |
| 11 | + | |
| 12 | +## 1. Principles <a name="principles"></a> | |
| 13 | + | |
| 14 | +1. **Task-based, not occupation-based.** Occupations are bundles of tasks with very | |
| 15 | + different AI exposure; whole-occupation scoring (Frey & Osborne 2013) has a poor | |
| 16 | + empirical record. Occupation scores are always *derived* from task scores | |
| 17 | + (Arntz, Gregory & Zierahn 2016; Eloundou et al. 2024). | |
| 18 | +2. **Three concepts, never collapsed** (see §5): | |
| 19 | + - **Exposure** — AI is technically capable of performing the task. | |
| 20 | + - **Substitution** — AI actually replaces the human performing it. This is the | |
| 21 | + headline composite. | |
| 22 | + - **Augmentation** — AI assists the human, increasing productivity. | |
| 23 | + Realized labor-market effects concentrate in *automation-classified* usage, not | |
| 24 | + augmentation (Brynjolfsson, Chandar & Chen 2025), so conflating the three is not | |
| 25 | + just imprecise — it is empirically wrong. | |
| 26 | +3. **Uncertainty is part of the score.** Every published score carries | |
| 27 | + `score_low / score / score_high`. Interval width is driven primarily by | |
| 28 | + disagreement between independent LLM raters (single-model ratings show up to a | |
| 29 | + 19× spread in headline statistics across frontier models — Yin, Vu & Persico 2026) | |
| 30 | + plus human-calibration error on the expert anchor set. | |
| 31 | +4. **Fully reproducible.** Weights, formulas, prompt versions, rater model IDs and | |
| 32 | + per-model ratings are all published. Every score traces to a stored | |
| 33 | + `score_runs` row; historical runs are immutable. | |
| 34 | +5. **Versioned.** Any change to weights, formulas, or rater prompts bumps | |
| 35 | + `INDEX_VERSION` (semver) with a changelog entry and regenerated sensitivity outputs. | |
| 36 | + | |
| 37 | +## 2. Data <a name="data"></a> | |
| 38 | + | |
| 39 | +| Input | Source | Role | | |
| 40 | +|---|---|---| | |
| 41 | +| Occupations & task statements | O*NET 30.x (O*NET-SOC 2019 taxonomy), CC BY 4.0 | Unit of analysis (~18k tasks, ~900 data-level occupations) | | |
| 42 | +| Task importance weights | O*NET Task Ratings (IM scale 1–5) | Aggregation weights (§6) | | |
| 43 | +| EU/France occupations | ESCO v1.2.x + official ESCO↔O*NET crosswalk; ROME 4.0 | Crosswalked scores (crosswalk loss documented per occupation) | | |
| 44 | +| Wages | BLS OEWS (latest May release); Eurostat SES; INSEE | `cost_ratio` denominator | | |
| 45 | +| Adoption data | Census BTOS AI supplement, Ramp AI Index, Anthropic Economic Index (Hugging Face), Challenger reports | `adoption_velocity` inputs (§4.5) | | |
| 46 | +| Human anchor ratings | Expert Delphi panel (`data/derived/expert_overrides/`) | LLM-rater calibration (§3) | | |
| 47 | + | |
| 48 | +Raw dumps are immutable (`data/raw/`, gitignored); all transformations go through | |
| 49 | +`apps/etl`; derived artifacts commit only manifests (hashes + row counts). | |
| 50 | + | |
| 51 | +## 3. Task rating (LLM-as-evaluator) <a name="task-rating"></a> | |
| 52 | + | |
| 53 | +Each O*NET task statement is rated on a **5-point scale** per dimension (§4) by a | |
| 54 | +**panel of ≥2 (target 3) frontier LLMs** (`RATER_MODELS`), using versioned rubric | |
| 55 | +prompts (`apps/worker/src/raters/prompts/`). Per task × dimension: | |
| 56 | + | |
| 57 | +- `rating_mid` = mean of panel ratings; | |
| 58 | +- `rating_low` / `rating_high` = min / max of panel ratings (rater-disagreement band). | |
| 59 | + | |
| 60 | +Rubric anchors follow the citable standards: automatability uses the Eloundou et al. | |
| 61 | +"≥50% time saving at equal quality" threshold, decomposed into named criteria | |
| 62 | +(SML-style multi-criterion rubric); ratings require structured justifications and are | |
| 63 | +stored with model ID, prompt version and raw response (full audit trail). | |
| 64 | + | |
| 65 | +**Calibration:** 5% of ratings are sampled for human review; panel-vs-human | |
| 66 | +disagreement > 1 point routes the task to the expert queue. Expert overrides replace | |
| 67 | +the LLM band for that task and are flagged in the API output. | |
| 68 | + | |
| 69 | +## 4. Dimensions <a name="dimensions"></a> | |
| 70 | + | |
| 71 | +Composite weights live in `packages/scoring/src/weights.ts` and are published at | |
| 72 | +`/api/v1/methodology`. Weights sum to 1.0. | |
| 73 | + | |
| 74 | +| Dimension | Key | Weight | Orientation | Rubric anchor (rating of 5 means…) | | |
| 75 | +|---|---|---|---|---| | |
| 76 | +| Task automatability | `automatability` | 0.35 | direct | Current AI (incl. tooling/agents) can do the task with ≥50% time saving at equal quality | | |
| 77 | +| Current technical feasibility | `feasibility` | 0.20 | direct | Deployed products demonstrably perform this task reliably today (not merely conceivable) | | |
| 78 | +| Cost of substitution vs. wage | `cost_ratio` | 0.15 | direct | AI cost per task-equivalent ≪ loaded human wage for the same output | | |
| 79 | +| Adoption barriers | `barriers` | 0.20 | **inverted** | Strong barriers: licensing/liability/regulation/human-contact requirements block substitution | | |
| 80 | +| Sector adoption velocity | `adoption_velocity` | 0.10 | direct | Occupation's dominant sectors adopt AI fast and deep (agents in production, measured displacement) | | |
| 81 | + | |
| 82 | +Orientation: a *direct* dimension's higher rating increases substitution pressure; an | |
| 83 | +*inverted* dimension's higher rating decreases it. `barriers` is the only inverted | |
| 84 | +dimension in v1. Orientation is encoded once, in `packages/scoring/src/weights.ts`, | |
| 85 | +next to the weights. | |
| 86 | + | |
| 87 | +Additionally, **`augmentation`** is rated per task on the same 5-point scale (does AI | |
| 88 | +assist the human on this task, raising productivity without replacing them?). It is | |
| 89 | +**not** part of the substitution composite; it feeds the augmentation sub-score (§5). | |
| 90 | + | |
| 91 | +### 4.4 `barriers` components <a name="barriers"></a> | |
| 92 | +Rated against five named criteria (see research doc 04): licensing/authorization | |
| 93 | +requirement (0.30), liability & error-cost asymmetry (0.25), regulatory-process | |
| 94 | +coverage (0.20, jurisdiction-specific), human-contact requirement (0.15), | |
| 95 | +organizational friction (0.10). Pre-2022 null results (OECD 2021; Acemoglu et al. | |
| 96 | +2022) show barriers dominate short-run outcomes — hence the 0.20 weight. | |
| 97 | + | |
| 98 | +### 4.5 `adoption_velocity` components <a name="adoption-velocity"></a> | |
| 99 | +Grounded in *measured* adoption, not forecasts: sector AI-use rate employment-weighted | |
| 100 | +(0.35), sector adoption momentum (0.20), agentic deployment depth (0.20), realized | |
| 101 | +displacement intensity (0.15), occupation-level usage intensity from the Anthropic | |
| 102 | +Economic Index (0.10). Refreshed each index release; sources are public and dated. | |
| 103 | + | |
| 104 | +## 5. Scoring formulas <a name="formulas"></a> | |
| 105 | + | |
| 106 | +All formulas are implemented, pure and deterministic, in `packages/scoring`. | |
| 107 | +Ratings `r ∈ [1,5]` normalize to pressure `p ∈ [0,1]`: | |
| 108 | + | |
| 109 | +``` | |
| 110 | +p = (r − 1) / 4 (direct dimensions) | |
| 111 | +p = 1 − (r − 1) / 4 (inverted dimensions) | |
| 112 | +``` | |
| 113 | + | |
| 114 | +Per task: | |
| 115 | + | |
| 116 | +``` | |
| 117 | +substitution_task = 100 · Σ_d w_d · p_d (all five dimensions) | |
| 118 | +exposure_task = 100 · (w_auto·p_auto + w_feas·p_feas) / (w_auto + w_feas) | |
| 119 | +augmentation_task = 100 · p_augmentation | |
| 120 | +``` | |
| 121 | + | |
| 122 | +Confidence bounds: `score_low` is computed with each dimension's | |
| 123 | +pressure-minimizing rating bound (for direct dimensions the low rating; for inverted | |
| 124 | +dimensions the **high** rating), `score_high` symmetrically. Bounds are therefore | |
| 125 | +worst/best-case envelopes over rater disagreement, and `low ≤ score ≤ high` always | |
| 126 | +holds. | |
| 127 | + | |
| 128 | +## 6. Aggregation to occupations <a name="aggregation"></a> | |
| 129 | + | |
| 130 | +Occupation scores are the **importance-weighted mean** of task scores, weights from | |
| 131 | +O*NET Task Ratings importance (IM, 1–5), normalized to sum to 1 within the | |
| 132 | +occupation. Applied identically to `low`, `score` and `high`. Tasks lacking | |
| 133 | +importance ratings receive the occupation-mean importance. | |
| 134 | + | |
| 135 | +The API additionally reports the share of tasks with `substitution_task ≥ 70` | |
| 136 | +("highly exposed task share") — this, not the composite alone, is the preferred | |
| 137 | +headline in UI copy ("X% of tasks in this occupation are highly exposed"). | |
| 138 | + | |
| 139 | +## 7. Versioning & runs <a name="versioning"></a> | |
| 140 | + | |
| 141 | +- `INDEX_VERSION` (semver) in `packages/scoring/src/version.ts`. | |
| 142 | +- MAJOR: formula/weight changes. MINOR: data-source version bumps (new O*NET release, | |
| 143 | + new adoption data). PATCH: recomputation with refreshed adoption inputs, prompt | |
| 144 | + clarifications that don't change the rubric semantics. | |
| 145 | +- Every computation writes a `score_runs` row (index version, prompt version, rater | |
| 146 | + models). Old runs stay queryable forever; the API serves the latest by default and | |
| 147 | + any run on request. | |
| 148 | + | |
| 149 | +## 8. Validation & sensitivity <a name="validation"></a> | |
| 150 | + | |
| 151 | +Published with every MAJOR/MINOR release under `docs/methodology/sensitivity/`: | |
| 152 | + | |
| 153 | +1. **Convergent validity:** Spearman correlation of our occupation scores against | |
| 154 | + Felten AIOE, Eloundou β, and ILO WP140 gradients (ρ ≈ 0.84 between independent | |
| 155 | + modern methodologies is the reference bar). | |
| 156 | +2. **Rater stability:** cross-model agreement distribution; occupations with the | |
| 157 | + widest bands flagged in-product. | |
| 158 | +3. **Outcome tracking:** correlation against the Stanford "Canaries" dashboard | |
| 159 | + (entry-level employment in exposed occupations) and AEI usage shares — exposure | |
| 160 | + indices individually explain <11% of realized unemployment risk (Frank, Ahn & | |
| 161 | + Moro 2025), so we report outcome tracking honestly rather than claiming prediction. | |
| 162 | +4. **Weight sensitivity:** composite rank stability under ±25% perturbation of each | |
| 163 | + weight. | |
| 164 | + | |
| 165 | +## 9. Known limitations <a name="limitations"></a> | |
| 166 | + | |
| 167 | +- LLM raters co-evolve with the technology they measure (the "ruler" problem); | |
| 168 | + multi-model panels bound but do not eliminate this. | |
| 169 | +- Sector-level adoption inputs are priors, corrected by occupation-level usage data. | |
| 170 | +- ESCO/ROME crosswalked scores inherit crosswalk loss; flagged per occupation. | |
| 171 | +- Scores describe *tasks as currently constituted*; occupations reorganize. | |
| 172 | +- This index measures exposure and substitution *pressure*, not certainty of job | |
| 173 | + loss. Product copy follows the adaptation-not-doom tone guide accordingly. | |
added
docs/methodology/examples/example-analyst.json
+74 −0
@@ -0,0 +1,74 @@ | ||
| 1 | +{ | |
| 2 | + "$comment": "Worked example for METHODOLOGY.md §5-6. Hand-computed with v1 weights (automatability .35, feasibility .20, cost_ratio .15, barriers .20 inverted, adoption_velocity .10). packages/scoring snapshot tests assert against `expected` to 3 decimal places. Any change here requires an INDEX_VERSION bump.", | |
| 3 | + "occupation": { | |
| 4 | + "code": "99-9999.00", | |
| 5 | + "title": "Example Analyst (synthetic)" | |
| 6 | + }, | |
| 7 | + "tasks": [ | |
| 8 | + { | |
| 9 | + "taskId": "T1", | |
| 10 | + "importance": 4, | |
| 11 | + "ratings": { | |
| 12 | + "automatability": { "low": 3, "mid": 4, "high": 5 }, | |
| 13 | + "feasibility": { "low": 4, "mid": 4, "high": 5 }, | |
| 14 | + "cost_ratio": { "low": 3, "mid": 3, "high": 4 }, | |
| 15 | + "barriers": { "low": 2, "mid": 2, "high": 3 }, | |
| 16 | + "adoption_velocity": { "low": 3, "mid": 4, "high": 4 }, | |
| 17 | + "augmentation": { "low": 4, "mid": 5, "high": 5 } | |
| 18 | + } | |
| 19 | + }, | |
| 20 | + { | |
| 21 | + "taskId": "T2", | |
| 22 | + "importance": 3, | |
| 23 | + "ratings": { | |
| 24 | + "automatability": { "low": 1, "mid": 2, "high": 2 }, | |
| 25 | + "feasibility": { "low": 2, "mid": 2, "high": 3 }, | |
| 26 | + "cost_ratio": { "low": 2, "mid": 2, "high": 2 }, | |
| 27 | + "barriers": { "low": 4, "mid": 4, "high": 5 }, | |
| 28 | + "adoption_velocity": { "low": 2, "mid": 3, "high": 3 }, | |
| 29 | + "augmentation": { "low": 2, "mid": 3, "high": 4 } | |
| 30 | + } | |
| 31 | + }, | |
| 32 | + { | |
| 33 | + "taskId": "T3", | |
| 34 | + "importance": 1, | |
| 35 | + "ratings": { | |
| 36 | + "automatability": { "low": 5, "mid": 5, "high": 5 }, | |
| 37 | + "feasibility": { "low": 2, "mid": 3, "high": 4 }, | |
| 38 | + "cost_ratio": { "low": 4, "mid": 4, "high": 5 }, | |
| 39 | + "barriers": { "low": 1, "mid": 1, "high": 2 }, | |
| 40 | + "adoption_velocity": { "low": 4, "mid": 5, "high": 5 }, | |
| 41 | + "augmentation": { "low": 1, "mid": 2, "high": 3 } | |
| 42 | + } | |
| 43 | + } | |
| 44 | + ], | |
| 45 | + "expected": { | |
| 46 | + "taskScores": [ | |
| 47 | + { | |
| 48 | + "taskId": "T1", | |
| 49 | + "substitution": { "low": 55.0, "score": 71.25, "high": 88.75 }, | |
| 50 | + "exposure": { "low": 59.0909, "score": 75.0, "high": 100.0 }, | |
| 51 | + "augmentation": { "low": 75.0, "score": 100.0, "high": 100.0 } | |
| 52 | + }, | |
| 53 | + { | |
| 54 | + "taskId": "T2", | |
| 55 | + "substitution": { "low": 11.25, "score": 27.5, "high": 32.5 }, | |
| 56 | + "exposure": { "low": 9.0909, "score": 25.0, "high": 34.0909 }, | |
| 57 | + "augmentation": { "low": 25.0, "score": 50.0, "high": 75.0 } | |
| 58 | + }, | |
| 59 | + { | |
| 60 | + "taskId": "T3", | |
| 61 | + "substitution": { "low": 73.75, "score": 86.25, "high": 95.0 }, | |
| 62 | + "exposure": { "low": 72.7273, "score": 81.8182, "high": 90.9091 }, | |
| 63 | + "augmentation": { "low": 0.0, "score": 25.0, "high": 50.0 } | |
| 64 | + } | |
| 65 | + ], | |
| 66 | + "occupation": { | |
| 67 | + "taskWeights": [0.5, 0.375, 0.125], | |
| 68 | + "substitution": { "low": 40.9375, "score": 56.71875, "high": 68.4375 }, | |
| 69 | + "exposure": { "low": 42.0455, "score": 57.1023, "high": 74.1477 }, | |
| 70 | + "augmentation": { "low": 46.875, "score": 71.875, "high": 84.375 }, | |
| 71 | + "highlyExposedTaskShare": 0.6667 | |
| 72 | + } | |
| 73 | + } | |
| 74 | +} | |
added
docs/research/01-existing-indices.md
+406 −0
@@ -0,0 +1,406 @@ | ||
| 1 | +# Existing AI Job-Exposure / Automation-Risk Indices and Methodologies | |
| 2 | + | |
| 3 | +**Research memo — AI Risk Index (airiskindex.io)** | |
| 4 | +**Date compiled:** 2026-08-05 (web research current to August 2026) | |
| 5 | +**Scope:** All major academic and industry indices measuring occupational exposure to AI/automation, their methodologies, criticisms, empirical validation, and lessons for the airiskindex.io v1 scoring model. | |
| 6 | + | |
| 7 | +--- | |
| 8 | + | |
| 9 | +## Table of Contents | |
| 10 | + | |
| 11 | +1. [First wave: pre-generative-AI automation risk (2013–2019)](#1-first-wave) | |
| 12 | +2. [Second wave: AI-specific exposure measures (2018–2021)](#2-second-wave) | |
| 13 | +3. [Third wave: LLM/generative-AI exposure (2023–2024)](#3-third-wave) | |
| 14 | +4. [Institutional indices: ILO, OECD, IMF (2023–2026)](#4-institutional-indices) | |
| 15 | +5. [Industry & consultancy estimates](#5-industry-estimates) | |
| 16 | +6. [Usage-based measures: Anthropic Economic Index & OpenAI (2025–2026)](#6-usage-based-measures) | |
| 17 | +7. [Fourth wave: 2025–2026 indices and meta-critiques](#7-fourth-wave) | |
| 18 | +8. [Empirical validation: do exposure scores predict real outcomes?](#8-empirical-validation) | |
| 19 | +9. [Master comparison table](#9-comparison-table) | |
| 20 | +10. [Implications for airiskindex.io v1 methodology](#10-implications) | |
| 21 | + | |
| 22 | +--- | |
| 23 | + | |
| 24 | +<a name="1-first-wave"></a> | |
| 25 | +## 1. First wave: pre-generative-AI automation risk (2013–2019) | |
| 26 | + | |
| 27 | +### 1.1 Frey & Osborne — "The Future of Employment" (2013 working paper; 2017 published) | |
| 28 | + | |
| 29 | +- **Authors/year:** Carl Benedikt Frey & Michael A. Osborne (Oxford Martin School). Working paper Sept 2013; published in *Technological Forecasting and Social Change* 114 (2017): 254–280. | |
| 30 | +- **Unit of analysis:** Whole occupations (702 SOC occupations). | |
| 31 | +- **Methodology:** | |
| 32 | + - ML experts hand-labeled ~70 occupations as automatable (1) or not (0) at a workshop. | |
| 33 | + - Identified three "engineering bottlenecks" to computerisation: **perception & manipulation**, **creative intelligence**, **social intelligence**, operationalized via 9 O*NET variables (e.g., finger dexterity, originality, social perceptiveness, persuasion, negotiation, assisting/caring for others, cramped work spaces). | |
| 34 | + - A **Gaussian process classifier** trained on the 70 labels extrapolated a "probability of computerisation" (0–1) to all 702 occupations. | |
| 35 | + - Occupations bucketed: high risk (p > 0.7), medium (0.3–0.7), low (p < 0.3). | |
| 36 | +- **Key numbers:** **47% of US employment** at "high risk" of computerisation "over the next decade or two" (i.e., roughly by 2030). Transportation, logistics, office/administrative support, and production occupations most at risk. | |
| 37 | +- **Criticisms (extensive):** | |
| 38 | + - **Occupation-level, not task-level:** treats occupations as monolithic. Arntz et al. (2016) showed within-occupation task heterogeneity slashes the estimate to ~9%. | |
| 39 | + - **Technical capability ≠ adoption:** no economics (cost, wages, regulation, preferences) in the model. | |
| 40 | + - **Subjective training labels** from a small expert workshop; only ~70 seed labels drive all 702 predictions. | |
| 41 | + - **Model-selection sensitivity:** "The Future of Employment Revisited" (arXiv:2104.13747) shows automation forecasts swing heavily with classifier choice on the same labels. | |
| 42 | + - **Poor ex-post predictive record:** occupations flagged high-risk did not experience differential employment declines through the late 2010s (see §8; Frank, Ahn & Moro 2025 find F&O scores explain <3% of unemployment-risk variation individually). | |
| 43 | +- **URLs:** | |
| 44 | + - Published paper: https://doi.org/10.1016/j.techfore.2016.08.019 (PDF mirror: http://reparti.free.fr/freyosborne17.pdf) | |
| 45 | + - Oxford Martin 2013 working paper: https://www.oxfordmartin.ox.ac.uk/downloads/academic/The_Future_of_Employment.pdf | |
| 46 | + - Critique (Melbourne Institute): https://melbourneinstitute.unimelb.edu.au/__data/assets/pdf_file/0005/3197111/wp2019n10.pdf | |
| 47 | + - Critique (model selection): https://arxiv.org/abs/2104.13747 | |
| 48 | + | |
| 49 | +### 1.2 Arntz, Gregory & Zierahn — OECD task-based critique (2016) | |
| 50 | + | |
| 51 | +- **Authors/year:** Melanie Arntz, Terry Gregory, Ulrich Zierahn (ZEW/OECD). *"The Risk of Automation for Jobs in OECD Countries"*, OECD Social, Employment and Migration Working Paper No. 189 (2016); follow-up "Revisiting the risk of automation" in *Economics Letters* 159 (2017). | |
| 52 | +- **Unit of analysis:** Individual workers' **task bundles** (PIAAC Survey of Adult Skills microdata), not occupation averages. | |
| 53 | +- **Methodology:** Transferred Frey–Osborne occupation-level risk to individual workers, then re-estimated risk as a function of each worker's *actual reported tasks* (PIAAC), allowing within-occupation heterogeneity. High risk = automatability > 70%. | |
| 54 | +- **Key numbers:** Only **~9% of jobs across 21 OECD countries** at high risk (US 9%, Germany 12%, Korea 6%) — versus 47% under F&O. | |
| 55 | +- **Significance:** Founded the **task-based paradigm** that every serious index since has adopted — including airiskindex.io. Key insight: workers in the "same" occupation do different task mixes; scoring must start at the task level. | |
| 56 | +- **Criticisms:** Still anchored on F&O's original subjective labels; PIAAC task self-reports are coarse; may *understate* risk if task bundles themselves adjust post-automation. | |
| 57 | +- **URLs:** | |
| 58 | + - OECD WP 189: https://doi.org/10.1787/5jlz9h56dvq7-en | |
| 59 | + - Economics Letters 2017: https://doi.org/10.1016/j.econlet.2017.07.001 | |
| 60 | + | |
| 61 | +### 1.3 Brynjolfsson, Mitchell & Rock — Suitability for Machine Learning (SML) (2017–2018) | |
| 62 | + | |
| 63 | +- **Authors/year:** Erik Brynjolfsson, Tom Mitchell, Daniel Rock. "What Can Machine Learning Do? Workforce Implications" (*Science*, 2017); "What Can Machines Learn, and What Does It Mean for Occupations and the Economy?" (*AEA Papers & Proceedings* 108, 2018: 43–47). | |
| 64 | +- **Unit of analysis:** O*NET tasks (18,156 tasks; ~2,069 Detailed Work Activities), aggregated to ~950 occupations. | |
| 65 | +- **Methodology:** | |
| 66 | + - A **23-question rubric** capturing what (then-current, supervised) ML can do — e.g., mapping well-defined inputs to outputs, tolerance for error, no long chains of reasoning, digital data availability, no need for detailed physical manipulation. | |
| 67 | + - Each task scored 1–5 per question; rubric validated by ML experts, then scaled via CrowdFlower crowd workers; aggregated to task SML then occupation SML (task-importance weighted). | |
| 68 | +- **Key findings:** (1) ML affects *different* occupations than earlier automation waves; (2) most occupations have at least some high-SML tasks; (3) **almost no occupation is fully automatable**; (4) capturing value requires **task re-bundling / job redesign**. | |
| 69 | +- **Criticisms:** Rubric was tuned to pre-LLM supervised ML (weak on generation, reasoning, dialogue); crowd ratings noisy; scores never strongly validated against outcomes (explains ~0–3% of unemployment-risk variation individually per Frank et al. 2025). | |
| 70 | +- **Relevance to us:** Direct methodological ancestor of a **multi-criterion task rubric** — our `automatability` + `feasibility` split echoes SML's separation of "could ML do it" from "is it practical". | |
| 71 | +- **URLs:** | |
| 72 | + - AEA P&P: https://www.aeaweb.org/articles?id=10.1257/pandp.20181019 | |
| 73 | + - SSRN: https://papers.ssrn.com/sol3/papers.cfm?abstract_id=3224100 | |
| 74 | + - Replication data: https://www.openicpsr.org/openicpsr/project/114436 | |
| 75 | + - WorldSML rubric (Stanford Digital Economy Lab, ongoing): https://digitaleconomy.stanford.edu/research/suitability-for-machine-learning-rubric-worldsml/ | |
| 76 | + | |
| 77 | +--- | |
| 78 | + | |
| 79 | +<a name="2-second-wave"></a> | |
| 80 | +## 2. Second wave: AI-specific exposure measures (2018–2021) | |
| 81 | + | |
| 82 | +### 2.1 Webb (2020) — Patent-based exposure | |
| 83 | + | |
| 84 | +- **Author/year:** Michael Webb (Stanford). "The Impact of Artificial Intelligence on the Labor Market" (SSRN 3482150, Nov 2019/2020; still unpublished but heavily cited). | |
| 85 | +- **Unit of analysis:** O*NET task descriptions × patent text; aggregated to occupations. | |
| 86 | +- **Methodology:** | |
| 87 | + - Selected AI patents (~16,400) by keyword; dependency-parsed titles to extract **verb–object pairs** (~8,000 pairs, e.g., "diagnose disease", "detect fraud"). | |
| 88 | + - Extracted verb–object pairs from O*NET task statements; scored each task by the frequency with which its verb-object pairs appear in AI patents. | |
| 89 | + - Occupation score = task-importance-weighted average; reported as **exposure percentiles**. | |
| 90 | + - **Built-in validation strategy:** applied the same method to *robots* and *software* patents and showed those historical exposure measures predicted realized employment/wage declines in exposed occupations — then applied it to AI. | |
| 91 | +- **Key findings:** AI (unlike robots/software) exposes **high-skilled, high-wage, older** workers most: e.g., clinical lab technicians, chemical engineers, optometrists, radiologic technicians. Robots hit low-skill physical work; software hit mid-skill routine work. | |
| 92 | +- **Criticisms:** Patents lag and imperfectly reflect deployable capability (pre-LLM corpus, so misses generative AI entirely); verb-object matching is crude semantics; no distinction between substitution and augmentation. | |
| 93 | +- **URLs:** | |
| 94 | + - Paper: https://papers.ssrn.com/sol3/papers.cfm?abstract_id=3482150 / https://www.michaelwebb.co/webb_ai.pdf | |
| 95 | + - Brookings explainer: https://www.brookings.edu/articles/how-patents-can-tell-us-what-jobs-ai-is-poised-to-disrupt/ | |
| 96 | + | |
| 97 | +### 2.2 Felten, Raj & Seamans — AI Occupational Exposure (AIOE) (2018, 2021, 2023) | |
| 98 | + | |
| 99 | +- **Authors/year:** Edward Felten (Princeton), Manav Raj (Wharton), Robert Seamans (NYU). "Occupational, Industry, and Geographic Exposure to Artificial Intelligence: A Novel Dataset and Its Potential Uses", *Strategic Management Journal* 42(12), 2021: 2195–2217. Generative-AI update: "Occupational Heterogeneity in Exposure to Generative AI" (SSRN 4414065, April 2023). | |
| 100 | +- **Unit of analysis:** 52 O*NET **abilities** (not tasks) × 10 AI application areas; aggregated to occupations (also industry AIIE and county-level geography). | |
| 101 | +- **Methodology:** | |
| 102 | + - 10 AI applications from the EFF AI Progress Measurement project (image recognition, language modeling, translation, speech recognition, abstract strategy games, etc.). | |
| 103 | + - Amazon Mechanical Turk crowd workers rated **relatedness** of each application to each of 52 O*NET abilities → ability-level exposure = sum of relatedness scores. | |
| 104 | + - Occupation AIOE = weighted sum of ability exposures using O*NET ability **importance and prevalence** weights. | |
| 105 | + - 2023 generative-AI variant re-weights toward language modeling and image generation: top exposed = telemarketers, then post-secondary teachers (languages, history, law), sociologists, judges. | |
| 106 | +- **Key properties:** Continuous z-scored index; famously **positively correlated with wages and education** (white-collar exposure) — opposite sign to Frey–Osborne. | |
| 107 | +- **Criticisms:** Ability-level (even further from tasks than occupations); MTurk relatedness judgments are lay opinions; "exposure" deliberately **neutral between substitution and augmentation** (authors are explicit about this); static. | |
| 108 | +- **Data:** Public GitHub (occupation-, industry-, geography-level scores): https://github.com/AIOE-Data/AIOE | |
| 109 | +- **Adoption:** The **IMF** (Cazzaniga et al. 2024) and Pew (2023) analyses build directly on AIOE; the ECB European work (Albanesi et al.) uses AIOE + Webb. | |
| 110 | +- **URLs:** | |
| 111 | + - SMJ paper: https://doi.org/10.1002/smj.3286 | |
| 112 | + - SSRN 2021: https://papers.ssrn.com/sol3/papers.cfm?abstract_id=3822412 | |
| 113 | + - GenAI variant: https://papers.ssrn.com/sol3/papers.cfm?abstract_id=4414065 | |
| 114 | + | |
| 115 | +### 2.3 Other second-wave measures (brief) | |
| 116 | + | |
| 117 | +- **Georgieff & Hyee (OECD 2021), "Artificial intelligence and employment: New cross-country evidence"** — applied Felten-style AIOE to PIAAC across 23 OECD countries; most-exposed: business professionals, managers, chief executives, science/engineering professionals. Found **no negative employment relationship 2012–2019**; in high-computer-use occupations, higher AI exposure correlated with *higher* employment growth. URL: https://doi.org/10.1787/c2c1d276-en | |
| 118 | +- **Lassébie & Quintini (OECD 2022), "What skills and abilities can automation technologies replicate?"** — expert survey on automatability of ~100 O*NET skills/abilities; basis for OECD Employment Outlook 2023 statement that occupations at highest risk of automation account for **~27% of OECD employment**. URL: https://doi.org/10.1787/646aad77-en ; Employment Outlook 2023 AI chapter: https://www.oecd.org/en/publications/oecd-employment-outlook-2023_08785bba-en.html | |
| 119 | +- **Tolan et al. (2021, JRC/EC)** — mapped AI research benchmarks to cognitive abilities to tasks; early "capability→ability→occupation" chain, precursor of the 2026 OECD capability-indicator approach. | |
| 120 | +- **Startup-based exposure ("Follow the money", arXiv 2024)** — measures exposure via commercial AI startup activity mapped to occupations; argues patent/ability measures miss commercialization. URL: https://arxiv.org/abs/2412.04924 | |
| 121 | + | |
| 122 | +--- | |
| 123 | + | |
| 124 | +<a name="3-third-wave"></a> | |
| 125 | +## 3. Third wave: LLM/generative-AI exposure (2023–2024) | |
| 126 | + | |
| 127 | +### 3.1 Eloundou, Manning, Mishkin & Rock — "GPTs are GPTs" (OpenAI/Wharton, 2023; *Science* 2024) | |
| 128 | + | |
| 129 | +**The single most influential template for airiskindex.io's LLM-as-evaluator pipeline.** | |
| 130 | + | |
| 131 | +- **Authors/year:** Tyna Eloundou, Sam Manning, Pamela Mishkin (OpenAI), Daniel Rock (Wharton). arXiv:2303.10130 (Mar 2023); published as "GPTs are GPTs: Labor market impact potential of LLMs", *Science* 384(6702), June 2024: 1306–1308. | |
| 132 | +- **Unit of analysis:** O*NET task/DWA level (19,265 tasks; 2,087 DWAs), aggregated to 1,016 occupations with task weights; combined with BLS employment/wage data. | |
| 133 | +- **Exposure rubric (the core innovation):** Exposure = "whether access to an LLM or LLM-powered system would reduce the time required for a human to perform a specific task **by at least 50% while maintaining quality**". Three levels: | |
| 134 | + - **E0** — no exposure. | |
| 135 | + - **E1** — direct exposure: LLM alone (via chat/API) achieves the 50% time reduction. | |
| 136 | + - **E2** — LLM+ exposure: achievable only with additional software/tooling built on the LLM (image input, retrieval, agents, etc.). | |
| 137 | + - Aggregates: **α = E1** (lower bound), **β = E1 + 0.5·E2** (expected), **ζ = E1 + E2** (upper bound). | |
| 138 | +- **Raters:** Both human annotators (OpenAI staff, trained on rubric) and **GPT-4 itself as a rater** with the rubric as prompt; human–GPT-4 agreement was high (occupation-level correlations ≈ 0.80), pioneering the LLM-as-evaluator design we plan to use. | |
| 139 | +- **Key numbers:** | |
| 140 | + - ~**80% of US workers** have ≥10% of tasks exposed (β); **~19% of workers** have ≥50% of tasks exposed. | |
| 141 | + - ~1.8% of jobs have >half their tasks E1-exposed; rises to **~46% of jobs** under ζ (with LLM-powered software). | |
| 142 | + - Exposure **increases with wage and education** (up to a point); science and critical-thinking-intensive skills correlate negatively; programming and writing positively. | |
| 143 | +- **Criticisms:** | |
| 144 | + - Measures *potential time savings*, not substitution vs augmentation, adoption, or net employment effect (authors are explicit). | |
| 145 | + - Rater instability: the flagship statistic is wildly model-dependent (see Yin et al. 2026, §7.2: 2.7%–51.5% across frontier raters). | |
| 146 | + - Static snapshot of March-2023 GPT-4 capability; the "E2 software will exist" counterfactual is speculative. | |
| 147 | + - 50%-time-saving threshold is arbitrary; binary-ish levels lose information. | |
| 148 | +- **URLs:** | |
| 149 | + - arXiv: https://arxiv.org/abs/2303.10130 | |
| 150 | + - Science: https://www.science.org/doi/10.1126/science.adj0998 | |
| 151 | + - OpenAI page: https://openai.com/index/gpts-are-gpts/ | |
| 152 | + - Follow-up "Extending GPTs Are GPTs to Firms" (AEA P&P 2025): https://www.aeaweb.org/articles?id=10.1257/pandp.20251045 | |
| 153 | + | |
| 154 | +### 3.2 Goldman Sachs — Briggs & Kodnani (March 2023) | |
| 155 | + | |
| 156 | +See §5.1. Methodologically an O*NET task-importance exercise inspired by Eloundou-style exposure; headline "300 million FTE jobs exposed" globally. | |
| 157 | + | |
| 158 | +--- | |
| 159 | + | |
| 160 | +<a name="4-institutional-indices"></a> | |
| 161 | +## 4. Institutional indices: ILO, OECD, IMF (2023–2026) | |
| 162 | + | |
| 163 | +### 4.1 ILO — Gmyrek, Berg & Bescond (2023): "Generative AI and Jobs: A Global Analysis" | |
| 164 | + | |
| 165 | +- **Authors/year:** Paweł Gmyrek, Janine Berg, David Bescond. ILO Working Paper 96, August 2023. | |
| 166 | +- **Methodology:** Scored **ISCO-08** occupation task lists (not O*NET) with **GPT-4 as rater** (multiple prompts, averaged), producing task-level automation-potential scores; distinguished **automation potential** vs **augmentation potential** at occupation level; mapped to global employment via ILO harmonized microdata for 100+ countries, by income group and sex. | |
| 167 | +- **Key numbers:** | |
| 168 | + - Only **clerical support work** is highly exposed as a group: **24% of clerical tasks highly exposed**, +58% medium exposure. Other occupational groups: 1–4% of tasks highly exposed. | |
| 169 | + - Globally, ~**2.3% of employment (~75M jobs)** in the top automation-potential bucket; **13.4% (~427M)** in augmentation potential. | |
| 170 | + - Exposure concentrated in **high/upper-middle-income countries** (more clerical employment) and **strongly gendered** (clerical work is female-dominated: in high-income countries, several times more female than male employment in the highest-exposure category). | |
| 171 | +- **Framing:** "Augmentation, not automation, is the most likely impact" — the origin of the transformation-over-replacement institutional narrative. | |
| 172 | +- **URLs:** | |
| 173 | + - WP96 PDF: https://www.ilo.org/sites/default/files/2024-07/WP96_web.pdf | |
| 174 | + - SSRN: https://papers.ssrn.com/sol3/papers.cfm?abstract_id=4584219 | |
| 175 | + - Policy companion "Generative AI and Jobs: Policies to Manage the Transition": https://www.ilo.org/publications/generative-ai-and-jobs-policies-manage-transition | |
| 176 | + | |
| 177 | +### 4.2 ILO 2024 interim work | |
| 178 | + | |
| 179 | +- **"Mind the AI Divide: Shaping a Global Perspective on the Future of Work"** (ILO & World Bank, Aug 2024) — applies the 2023 index with a digital-infrastructure overlay: poor countries are less *exposed* but also less able to *capture augmentation gains* (the "AI divide"). | |
| 180 | +- **Gmyrek, Winkler & Garganta (2024, ILO/World Bank)** — "Buffer or bottleneck? Employment exposure to generative AI and the digital divide in Latin America": 26–38% of LAC jobs exposed; digital access gates both risk and benefit. | |
| 181 | +- URL hub: https://www.ilo.org/publications (search "generative AI"); LAC paper: https://openknowledge.worldbank.org/handle/10986/41808 | |
| 182 | + | |
| 183 | +### 4.3 ILO–NASK (2025): "Generative AI and Jobs: A Refined Global Index of Occupational Exposure" — current institutional state of the art | |
| 184 | + | |
| 185 | +- **Authors/year:** Paweł Gmyrek, Janine Berg, K. Kamiński, F. Konopczyński, A. Ładna, B. Nafradi, K. Rosłaniec, M. Troszyński (ILO + Poland's NASK). ILO Working Paper 140, May 2025 + Research Brief "Generative AI and jobs: a 2025 update". | |
| 186 | +- **Methodology (major upgrade over 2023):** | |
| 187 | + - **Hybrid human+LLM pipeline:** 52,558 human judgments on automation potential of 2,861 tasks (representative sample of 29,753 tasks in the Polish occupational classification), from a survey of 1,640 people (workers/experts), used to calibrate and validate GPT-4o task scoring; then scaled to the full ISCO task universe. | |
| 188 | + - Replaced the binary automation/augmentation split with a **4-gradient exposure spectrum** (from marginal exposure to highest exposure), acknowledging most jobs are partially transformed. | |
| 189 | + - Occupation scores mapped to global employment microdata by country income group, sex, and region. | |
| 190 | +- **Key numbers:** | |
| 191 | + - **1 in 4 jobs worldwide (25% of global employment)** has measurable GenAI exposure; **34% in high-income countries**. | |
| 192 | + - Highest-gradient (transformation most likely) ≈ 3–4% of global employment, still concentrated in clerical work. | |
| 193 | + - Gender gap persists: in high-income countries, ~**9.6% of female employment** vs ~3.5% of male employment in the top exposure gradient. | |
| 194 | + - Headline framing: "**transformation, not replacement**". | |
| 195 | +- **Criticisms:** LLM-rater dependence (flagged by Yin et al. 2026); Polish task-survey generalizability; exposure ≠ adoption in low-connectivity countries (self-acknowledged). | |
| 196 | +- **URLs:** | |
| 197 | + - WP140 PDF: https://www.ilo.org/sites/default/files/2025-05/WP140_web.pdf | |
| 198 | + - WP140 interactive: https://webapps.ilo.org/static/english/intserv/working-papers/wp140/index.html | |
| 199 | + - 2025 update brief: https://www.ilo.org/publications/generative-ai-and-jobs-2025-update (PDF: https://www.ilo.org/sites/default/files/2025-05/Research%20brief_GenAI%202025%20Update.pdf) | |
| 200 | + - Press release: https://www.ilo.org/resource/news/one-four-jobs-risk-being-transformed-genai-new-ilo–nask-global-index-shows | |
| 201 | + | |
| 202 | +### 4.4 OECD (2023–2026) | |
| 203 | + | |
| 204 | +- **Employment Outlook 2023 (AI chapters):** using Lassébie–Quintini expert-based measure, occupations at highest automation risk = **~27% of employment** across OECD; "no signs of slowing labour demand (yet)" in AI-exposed occupations. URLs: https://www.oecd.org/en/publications/oecd-employment-outlook-2023_08785bba-en/full-report/artificial-intelligence-and-jobs-no-signs-of-slowing-labour-demand-yet_5aebe670.html | |
| 205 | +- **AI case studies & job quality (2023–2024):** firm case studies in finance/manufacturing across 8 countries: 23% of firms reported AI reduced employment in affected roles; wages mostly unchanged. Georgieff (2024), "Artificial intelligence and wage inequality": https://www.oecd.org/en/publications/artificial-intelligence-and-wage-inequality_bf98a45c-en.html | |
| 206 | +- **OECD AI Capability Indicators (2025):** 5-year effort, 50+ experts; 9 capability domains (Language; Social interaction; Problem solving; Creativity; Metacognition & critical thinking; Knowledge/learning/memory; Vision; Manipulation; Robotic intelligence) each on an ordinal capability scale. URL: https://www.oecd.org/en/publications/introducing-the-oecd-ai-capability-indicators_be745f04-en.html | |
| 207 | +- **The OECD AI Exposure Measure (2025/2026):** maps occupations' required capability *levels* in each of the 9 domains against AI's *current attained level* per the Capability Indicators → exposure = overlap. Explicitly designed to be **forward-looking, transparent, and updateable** as AI capability levels advance — the first institutional index architected for versioned re-scoring (same philosophy as our `INDEX_VERSION`). URL: https://www.oecd.org/en/publications/the-oecd-ai-exposure-measure_f3da0f0a-en.html | |
| 208 | +- **Skills in the AI age (OECD AI Papers No. 60, July 2026):** applies the exposure measure to skills demand. URL: https://www.oecd.org/content/dam/oecd/en/publications/reports/2026/07/skills-in-the-ai-age_e8d8c1e6/972bd15e-en.pdf | |
| 209 | + | |
| 210 | +### 4.5 IMF — Cazzaniga et al. (2024) and the AI Preparedness Index | |
| 211 | + | |
| 212 | +- **Authors/year:** Mauro Cazzaniga, Florence Jaumotte, Longji Li, Giovanni Melina, Augustus Panton, Carlo Pizzinelli, Emma Rockall, Marina M. Tavares. "Gen-AI: Artificial Intelligence and the Future of Work", IMF Staff Discussion Note SDN/2024/001, January 2024. | |
| 213 | +- **Methodology:** Takes Felten's **AIOE** and adds a **potential complementarity index (C-AIOE)** (from Pizzinelli et al. 2023, IMF WP/23/216): occupations scored on shielding factors — required physical presence, human interaction, legal/social responsibility (judges are exposed *and* complemented; telemarketers exposed and *not*). Splits employment into: high exposure + high complementarity (augmentation likely) vs high exposure + low complementarity (displacement risk) vs low exposure. | |
| 214 | +- **Key numbers:** **~40% of global employment exposed** to AI (**60% advanced economies, 40% emerging, 26% low-income**). In AEs, roughly half of exposed jobs are high-complementarity. Women and college-educated more exposed but better positioned for gains. | |
| 215 | +- **AI Preparedness Index (AIPI):** country-level (174 economies) readiness across digital infrastructure, human capital & labor policies, innovation & integration, regulation & ethics — the macro complement to occupational exposure. Dashboard: https://www.imf.org/external/datamapper/AIPI@AIPI | |
| 216 | +- **Criticisms:** Inherits all AIOE limitations; complementarity ratings are judgment calls; country mapping via ISCO crosswalks is coarse. | |
| 217 | +- **URLs:** | |
| 218 | + - SDN PDF: https://www.imf.org/-/media/files/publications/sdn/2024/english/sdnea2024001.pdf | |
| 219 | + - eLibrary: https://www.elibrary.imf.org/view/journals/006/2024/001/006.2024.issue-001-en.xml | |
| 220 | + - Pizzinelli et al. WP/23/216 (C-AIOE): https://www.imf.org/en/Publications/WP/Issues/2023/10/04/Labor-Market-Exposure-to-AI-Cross-country-Differences-and-Distributional-Implications-539656 | |
| 221 | + - Follow-up: "Exposure to Artificial Intelligence and Occupational Mobility" (WP/24/116): https://www.imf.org/-/media/files/publications/wp/2024/english/wpiea2024116-print-pdf.pdf | |
| 222 | + | |
| 223 | +--- | |
| 224 | + | |
| 225 | +<a name="5-industry-estimates"></a> | |
| 226 | +## 5. Industry & consultancy estimates | |
| 227 | + | |
| 228 | +### 5.1 Goldman Sachs (Briggs & Kodnani, March 2023) | |
| 229 | + | |
| 230 | +- "The Potentially Large Effects of Artificial Intelligence on Economic Growth". O*NET task-level judgment of automatable share per occupation (26 US, 24 European task categories importance/complexity weighted). | |
| 231 | +- **Key numbers:** ~**2/3 of US/European occupations partially exposed**; generative AI could substitute up to **25% of current work** = **300M FTE jobs** globally exposed; +7% global GDP over 10 years. Most exposed: office/admin support (46% of tasks automatable), legal (44%), architecture/engineering (37%). | |
| 232 | +- **Criticism:** binary "automatable share" judgments, no adoption model; the 300M number is routinely misquoted as "job losses". | |
| 233 | +- URLs: https://www.goldmansachs.com/insights/articles/generative-ai-could-raise-global-gdp-by-7-percent ; follow-up US labor analysis: https://www.goldmansachs.com/insights/articles/how-will-ai-affect-the-us-labor-market | |
| 234 | + | |
| 235 | +### 5.2 McKinsey Global Institute (June 2023, updated) | |
| 236 | + | |
| 237 | +- "The economic potential of generative AI: the next productivity frontier". Proprietary work-activity/capability model (~2,100 work activities, 850 occupations). | |
| 238 | +- **Key numbers:** GenAI + existing tech could automate activities absorbing **60–70% of employees' time**; genAI value $2.6–4.4T/yr; **half of today's work activities automated between 2030 and 2060 (midpoint ~2045)** — pulled forward ~a decade vs pre-genAI estimate. | |
| 239 | +- **Criticism:** proprietary/black-box capability ratings; "time automatable" ≠ jobs; adoption scenarios highly assumption-driven. | |
| 240 | +- URL: https://www.mckinsey.com/capabilities/tech-and-ai/our-insights/the-economic-potential-of-generative-ai-the-next-productivity-frontier | |
| 241 | + | |
| 242 | +### 5.3 Pew Research Center (Kochhar, July 2023) | |
| 243 | + | |
| 244 | +- "Which U.S. Workers Are More Exposed to AI on Their Jobs?" — Felten-style ability-importance approach on O*NET. | |
| 245 | +- **Key numbers:** **19% of US workers in most-exposed jobs** vs 23% in least-exposed (2022). Most-exposed jobs pay *more* ($33/hr vs $20/hr); exposure higher for women, Asian, college-educated workers. Notably, workers in exposed industries did **not** feel their jobs at risk. | |
| 246 | +- URL: https://www.pewresearch.org/social-trends/2023/07/26/which-u-s-workers-are-more-exposed-to-ai-on-their-jobs/ | |
| 247 | + | |
| 248 | +### 5.4 PwC Global AI Jobs Barometer (2024, 2025, 2026) | |
| 249 | + | |
| 250 | +- Analyzes ~**1 billion job ads** worldwide + firm financials. 2025 edition ("The Fearless Future"): industries most exposed to AI saw productivity growth nearly **4x** (7%→27%); **56% wage premium** for AI-skilled workers (up from 25%); skills in AI-exposed occupations changing **66% faster**; employment *still growing* even in highly automatable roles. 2026 edition: labor market splitting into "two distinct paths", rewarding human skills. | |
| 251 | +- **Value to us:** the best large-scale *demand-side* signal (vacancies), useful to calibrate `adoption_velocity`. | |
| 252 | +- URLs: https://www.pwc.com/gx/en/issues/artificial-intelligence/job-barometer/2025/report.pdf ; 2026 PR: https://www.pwc.com/gx/en/news-room/press-releases/2026/pwc-2026-ai-jobs-barometer.html | |
| 253 | + | |
| 254 | +--- | |
| 255 | + | |
| 256 | +<a name="6-usage-based-measures"></a> | |
| 257 | +## 6. Usage-based measures: Anthropic Economic Index & OpenAI (2025–2026) | |
| 258 | + | |
| 259 | +The decisive innovation of 2025–26: replacing *predicted* exposure with **observed AI usage** mapped to the same O*NET task taxonomy. This is the empirical anchor airiskindex.io should exploit for `adoption_velocity` and to validate `automatability`. | |
| 260 | + | |
| 261 | +### 6.1 Anthropic Economic Index (AEI) — all releases to August 2026 | |
| 262 | + | |
| 263 | +**Methodology (constant across releases):** Clio, a privacy-preserving analysis pipeline in which Claude classifies large samples of real Claude.ai/API conversations against the **O*NET task taxonomy (~20,000 tasks)** and SOC occupations, plus interaction-mode classification (**automation** = full delegation/directive; **augmentation** = iterative collaboration, learning, validation). All aggregated data released openly on Hugging Face. | |
| 264 | + | |
| 265 | +| Release | Report | Data & model | Headline findings | | |
| 266 | +|---|---|---|---| | |
| 267 | +| **Feb 10, 2025** (paper arXiv:2503.04761, Handa et al., "Which Economic Tasks are Performed with AI?") | Launch report | 4M+ Claude.ai conversations | **36% of occupations** used AI for ≥25% of their tasks; only ~4% for ≥75%. Usage concentrated in **software development & writing** (computer/math ≈ 37% of conversations); peaks in **mid-to-high-wage** occupations, low at both wage extremes. **57% augmentation / 43% automation**. | | |
| 268 | +| **Mar 27, 2025** | v2 (Claude 3.7 Sonnet) | New conversations + cluster-level data | Usage patterns stable; extended-thinking usage concentrated in technical tasks; released bottom-up task clusters. | | |
| 269 | +| **Sep 15, 2025** (arXiv:2511.15080) | "Uneven geographic and enterprise adoption" | 1P API + geographic breakdowns; **Anthropic AI Usage Index (AUI)** = country share of usage ÷ share of working-age population | US 21.6% of usage; per-capita leaders Israel, Singapore, Australia, NZ, S. Korea. **+1% GDP/capita ↔ +0.7% AUI** (US states: 1.8% elasticity). DC highest state AUI (3.82). **Automation rose 27%→39%** of conversations since Dec 2024, surpassing augmentation for the first time; API usage even more automation-heavy. | | |
| 270 | +| **Jan 15, 2026** | "New building blocks" (economic primitives) | 1M Claude.ai + 1M 1P API transcripts (Sonnet 4.5); Nov 2025 data | Five **primitives**: task complexity, human/AI skill level, use case, AI autonomy, task success. College-level tasks: **12x estimated speedup** but 66% success rate vs 70% for simpler tasks; revised aggregate productivity estimate **+1.2 pp/yr** (down from 1.8 after reliability adjustment). Augmentation back above automation on Claude.ai (52% vs 45%). | | |
| 271 | +| **Mar 24, 2026** | "Learning curves" | Feb 2026 data (Opus 4.5/4.6) | Claude.ai task mix **de-concentrating** (top-10 tasks 24%→19%) while API concentrates (28%→33%). **49% of jobs in sample** now see Claude used for ≥25% of tasks (up from 36% in Jan 2025). 6-month+ tenure users: +10% conversation success; usage value ≈ $48–49/hr wage-equivalent tasks. | | |
| 272 | +| **Apr 2026** | AEI **Survey** launched | 9,700 Claude users, linked usage+perceptions | See below. | | |
| 273 | +| **Jun 26, 2026** | "Cadences" | Apr–Jun 2026, hourly sampling; artifact classifier | 93% of conversations produce artifacts (explanations 17%, documents/reports 15%). Higher-wage occupations' conversations consume 2.07x tokens. Survey: >⅓ of users expect AI to handle most of their work tasks within 12 months; only 10% rate own job loss likely; heavier automation users are *more* optimistic. Women use Claude less in automated modes (−0.33 SD). | | |
| 274 | + | |
| 275 | +- **Data availability (all releases):** https://huggingface.co/datasets/Anthropic/EconomicIndex (per-release folders `release_2025_03_27/`, `release_2025_09_15/`, etc., with documentation + replication notebooks; R package `aieconindex` on CRAN). | |
| 276 | +- **Index hub:** https://www.anthropic.com/economic-index — reports: https://www.anthropic.com/news/the-anthropic-economic-index ; https://www.anthropic.com/research/economic-index-geography ; https://www.anthropic.com/research/economic-index-primitives ; https://www.anthropic.com/research/economic-index-march-2026-report ; https://www.anthropic.com/research/economic-index-june-2026-report | |
| 277 | +- **Limitations (self-acknowledged):** Claude users ≠ workforce (selection bias toward developers/knowledge workers); conversation ≠ completed work; O*NET classification by LLM inherits classifier error; per-provider view only. | |
| 278 | + | |
| 279 | +### 6.2 OpenAI — "How People Use ChatGPT" (Chatterji et al., NBER w34255, Sept 2025) | |
| 280 | + | |
| 281 | +- Aaron Chatterji, Tom Cunningham, David Deming, Zoë Hitzig, Christopher Ong, Carl Shan, Kevin Wadman. Privacy-preserving classification of a representative sample of ChatGPT consumer conversations, Nov 2022–Jul 2025 (~10% of world adult population using ChatGPT). | |
| 282 | +- **Findings:** non-work usage grew from 53%→>70% of messages; work usage concentrated in **decision support** (advice, writing, information) rather than task execution; work usage highest among educated, high-paid professionals. Three-quarters of work messages: writing, information seeking, decision support. | |
| 283 | +- **Relevance:** independent replication that *realized* usage is augmentation-tilted and knowledge-work-concentrated — cross-provider triangulation for `adoption_velocity`. | |
| 284 | +- URLs: https://www.nber.org/papers/w34255 (PDF: https://www.nber.org/system/files/working_papers/w34255.pdf) | |
| 285 | + | |
| 286 | +--- | |
| 287 | + | |
| 288 | +<a name="7-fourth-wave"></a> | |
| 289 | +## 7. Fourth wave: 2025–2026 indices and meta-critiques | |
| 290 | + | |
| 291 | +### 7.1 Stanford Digital Economy Lab — "Canaries in the Coal Mine?" (Brynjolfsson, Chandar & Chen, Aug/Nov 2025) | |
| 292 | + | |
| 293 | +- **The most important realized-effects paper to date.** Uses **ADP payroll microdata** (millions of workers, monthly) linked to occupational AI-exposure measures (Eloundou/GPTs-are-GPTs based, cross-checked with Anthropic Economic Index automation/augmentation shares). | |
| 294 | +- **Six facts**, headline: since late 2022, **early-career workers (22–25) in the most AI-exposed occupations saw a ~13–16% relative employment decline** (16% in the Nov 2025 revision, controlling for firm-level shocks), while older workers in the same occupations and less-exposed young workers kept growing. Adjustment happens via **employment, not wages**. Declines concentrated where AEI data says AI **automates** rather than augments. Entry-level hiring is the "canary". | |
| 295 | +- **Live monitoring:** "Canaries Dashboard" — https://digitaleconomy.stanford.edu/project/indicators/canaries-dashboard/ | |
| 296 | +- **URLs:** paper page https://digitaleconomy.stanford.edu/publications/canaries-in-the-coal-mine ; PDF (Nov 2025) https://digitaleconomy.stanford.edu/app/uploads/2025/11/CanariesintheCoalMine_Nov25.pdf ; SIEPR WP: https://siepr.stanford.edu/publications/working-paper/canaries-coal-mine-six-facts-about-recent-employment-effects-artificial | |
| 297 | + | |
| 298 | +### 7.2 Yin, Vu & Persico (2026) — multi-model instability of LLM-rated exposure ("When the ruler is made of the thing it measures") | |
| 299 | + | |
| 300 | +- NBER WP 35110, "How (un)stable are LLM occupational exposure scores? Evidence from multi-model replication"; VoxEU column May 2026. | |
| 301 | +- Replicated the Eloundou rubric with **four frontier models on identical O*NET data**: share of US occupations with >50% of tasks at high direct exposure = **2.7% (Gemini 2.5) … 3.8% (GPT-4) … 20.3% (GPT-5) … 51.5% (Claude 4.5)** — a **19x spread**. Management occupations: >80% high-exposure under Claude, <20% under Gemini. Downstream diff-in-diff employment estimates **flip sign** across raters. Bias is systematic per model, doesn't wash out with sample size, and co-evolves with the technology being measured (feedback channel). | |
| 302 | +- **Recommendation (directly applicable to us):** any LLM-rated exposure analysis must report results from **≥2–3 different frontier models**; convergence ⇒ robust, divergence ⇒ model artifact. | |
| 303 | +- URL: https://cepr.org/voxeu/columns/when-ruler-made-thing-it-measures-multi-model-evidence-ai-occupational-exposure | |
| 304 | + | |
| 305 | +### 7.3 "AI Exposure Scores: what they measure, what they miss, and what comes next" (Lund, Euyang, Munyikwa & Fadaee, arXiv June 2026) | |
| 306 | + | |
| 307 | +- Field review. Diagnoses a **structural gap** (static scores can't answer dynamic who/when/where policy questions) and a **coordination gap** (policy still cites static 2023 GPTs-are-GPTs numbers despite methodological advances). Surveys five successor families: **dynamic/benchmark-based measures, ensembles, task-framework extensions, worker-centered metrics, adoption/usage data**. Recommends moving "from prediction to preparedness". | |
| 308 | +- URL: https://arxiv.org/abs/2606.23633 | |
| 309 | + | |
| 310 | +### 7.4 Other notable 2025–2026 entries | |
| 311 | + | |
| 312 | +- **Iceberg Index (Chopra et al., MIT + Oak Ridge National Laboratory, arXiv 2510.25137, late 2025):** skills-centered simulation — 151M US workers, 923 occupations, 32,000+ skills, ~3,000 counties; catalogued 13,000+ AI tools; agent-based simulation (AgentTorch on Frontier supercomputer). Visible "surface" tech-sector exposure = 2.2% of wage bill (~$211B); full skill-overlap exposure = **11.7% of US wage bill (~$1.2T)**. Explicitly technical exposure, not displacement. URLs: https://arxiv.org/abs/2510.25137 ; https://iceberg.mit.edu/report.pdf | |
| 313 | +- **Yale Budget Lab — "Evaluating the Impact of AI on the Labor Market: Current State of Affairs" (Gimbel, Kinder, Kendall & Lee, Oct 2025, updated):** occupational-mix dissimilarity analysis; finds **no broad acceleration** in labor-market compositional change attributable to AI 33 months post-ChatGPT — important null-result counterweight to Canaries. URL: https://budgetlab.yale.edu/research/evaluating-impact-ai-labor-market-current-state-affairs | |
| 314 | +- **UK task-based GenAI exposure index (arXiv 2507.22748, 2025):** novel LLM-scored task index applied to UK SOC codes — example of the national-adaptation pattern relevant to our ESCO/ROME crosswalk. URL: https://arxiv.org/abs/2507.22748 | |
| 315 | +- **OAIES / capability-staged exposure (2025–2026):** scores O*NET task automatable share at discrete **AI capability stages** (pre-LLM ML → early LLMs → multimodal → reasoning → agentic), multi-model rated (GPT-4o + Claude 3.5); cross-methodology Spearman ρ = 0.84 against independent scores. Overview: https://www.emergentmind.com/topics/ai-exposed-occupations ; theory-based variant (Moravec-paradox index): https://arxiv.org/abs/2510.13369 | |
| 316 | +- **"AI and jobs: A review of theory, estimates, and evidence" (arXiv 2509.15265, 2025):** comprehensive literature review; useful bibliography. URL: https://arxiv.org/abs/2509.15265 | |
| 317 | +- **Agentic-AI exposure analyses (2026):** e.g., "Agentic AI and Occupational Displacement" (arXiv 2604.00186) extends task exposure to autonomous multi-step agents across regions. URL: https://arxiv.org/abs/2604.00186 | |
| 318 | +- **"The Jagged Global Economy" (arXiv 2607.05404, 2026):** frontier-AI benchmark performance mapped to national economies — capability-grounded, benchmark-updated exposure. URL: https://arxiv.org/abs/2607.05404 | |
| 319 | + | |
| 320 | +--- | |
| 321 | + | |
| 322 | +<a name="8-empirical-validation"></a> | |
| 323 | +## 8. Empirical validation: do exposure scores predict real outcomes? | |
| 324 | + | |
| 325 | +**Bottom line: individually, classic exposure scores are weak predictors; ensembles + adoption data + post-2022 windows perform much better. Realized effects so far are concentrated (entry-level, automation-tilted tasks, online freelancing), not economy-wide.** | |
| 326 | + | |
| 327 | +1. **Frank, Ahn & Moro (PNAS Nexus, April 2025), "AI exposure predicts unemployment risk"** — built occupation-level *unemployment risk* from US unemployment-insurance claims (2010–2020); tested 10 exposure scores. **Every individual score performs poorly** (best single: Arntz automation probability, R² = 0.107; most < 3%; Frey–Osborne, SML, Felten, Webb all weak alone). An **ensemble of all scores** explains 29.8% (75.5% with education/skill/region controls) — +18 pp over baseline. Lesson: **no single score suffices; combine dimensions**. URL: https://pmc.ncbi.nlm.nih.gov/articles/PMC11983276/ (arXiv:2308.02624) | |
| 328 | +2. **Acemoglu, Autor, Hazell & Restrepo (JOLE 2022), "AI and Jobs: Evidence from Online Vacancies"** — AI-exposed establishments (Burning Glass) post more AI vacancies and *reduce* non-AI hiring, but **no detectable aggregate occupation-level employment effects** through 2018. URL: https://jadhazell.github.io/website/AI_And_Jobs.pdf | |
| 329 | +3. **Brynjolfsson, Chandar & Chen (2025) "Canaries"** — first large-scale realized-effect finding: −13–16% relative employment for early-career workers in most-exposed occupations; effects load on **automation-classified** (AEI) usage, not augmentation. (§7.1) | |
| 330 | +4. **Hui, Reshef & Zhou (2024), "The Short-Term Effects of Generative AI on Online Labor Markets"** — after ChatGPT, exposed freelancers (writing-heavy) on a large platform saw ~2% fewer jobs and ~5% lower earnings; top performers not spared. VoxEU: https://cepr.org/voxeu/columns/artificial-intelligence-and-its-short-term-effects-employment | |
| 331 | +5. **Hampole, Papanikolaou, Schmidt & Seegmiller (NBER w33509, 2025), "Artificial Intelligence and the Labor Market"** — vacancy-based measure of firm AI adoption; AI adoption predicts declining demand for exposed occupations within adopting firms, with reallocation toward AI-complementary roles. URL: https://www.nber.org/papers/w33509 | |
| 332 | +6. **Humlum & Vestergaard (2025), "Large Language Models, Small Labor Market Effects" (Denmark, NBER w33777)** — despite rapid ChatGPT adoption among exposed workers, **no detectable effects on earnings or hours** in 2023–24 administrative data; average time savings ~3%. Counterweight showing adoption ≠ displacement in the short run. URL: https://www.nber.org/papers/w33777 | |
| 333 | +7. **Wage-growth cross-section (2019 vs 2023, arXiv 2312.04714 & follow-ups):** one-unit higher AI exposure ↔ **−6.5 pp wage growth**, explaining ~34% of cross-sectional variation post-ChatGPT; late-2022→early-2025 CPS analyses find high-exposure occupations losing 5.6–8.5 pp employment per 10-point exposure. URL: https://arxiv.org/abs/2312.04714 | |
| 334 | +8. **Georgieff & Hyee (OECD 2021) / Employment Outlook 2023:** 2012–2019 — no negative employment relationship; **exposure without adoption predicts nothing** pre-2022. (§2.3, §4.4) | |
| 335 | +9. **Yale Budget Lab (2025–2026):** aggregate occupational mix shifting no faster than historical benchmarks (§7.4) — realized effects are **cohort- and task-specific, not (yet) aggregate**. | |
| 336 | + | |
| 337 | +**Synthesis for validation design:** (a) validate at task/cohort level, not aggregate; (b) test against unemployment/UI-claim risk and entry-level hiring, not just employment stocks; (c) use ensembles; (d) treat pre-2022 null results as evidence about *adoption gating*, which is exactly what our `barriers` and `adoption_velocity` dimensions model. | |
| 338 | + | |
| 339 | +--- | |
| 340 | + | |
| 341 | +<a name="9-comparison-table"></a> | |
| 342 | +## 9. Master comparison table | |
| 343 | + | |
| 344 | +| Index / Study | Year | Approach | Unit of analysis | Scale / output | Substitution vs augmentation split? | Validation status | Data availability | | |
| 345 | +|---|---|---|---|---|---|---|---| | |
| 346 | +| **Frey & Osborne** | 2013/2017 | Expert labels + Gaussian process classifier on O*NET bottleneck variables | Occupation (702 SOC) | P(computerisation) 0–1 | No | Poor ex-post; explains <3% of unemployment risk alone | Scores in paper appendix (public) | | |
| 347 | +| **Arntz, Gregory & Zierahn (OECD)** | 2016/2017 | F&O risk re-estimated on individual PIAAC task bundles | Worker/task bundle | P(automation) 0–1 | No | Best single predictor in Frank et al. ensemble (R²=0.107) | PIAAC public; scores replicable | | |
| 348 | +| **Brynjolfsson, Mitchell & Rock (SML)** | 2017/2018 | 23-question rubric, crowd-rated | O*NET task (18k) → occupation | SML 1–5 | Implicit (redesign framing) | Weak alone | openICPSR replication archive | | |
| 349 | +| **Webb** | 2020 | Patent–task text overlap (verb–object pairs) | Task → occupation | Exposure percentile | No | Historical validation on robots/software; AI portion pre-LLM | Author site / SSRN | | |
| 350 | +| **Felten, Raj & Seamans (AIOE)** | 2021 (genAI 2023) | 10 AI apps × 52 abilities, MTurk relatedness, importance-weighted | Ability → occupation (+industry, county) | Continuous z-score | No (explicitly neutral) | Weak alone; base of IMF/Pew analyses | GitHub (AIOE-Data/AIOE) | | |
| 351 | +| **Eloundou et al. "GPTs are GPTs"** | 2023/2024 | Rubric (≥50% time saving), human + GPT-4 raters | O*NET task/DWA → occupation | E0/E1/E2; α, β, ζ shares 0–1 | No (time-savings only) | Predicts Canaries cohort effects; rater-unstable (19x across models) | arXiv appendix; rubric public | | |
| 352 | +| **Goldman Sachs (Briggs & Kodnani)** | 2023 | Task-importance share judged automatable | Occupation | % tasks automatable | Partial (25% substitution assumption) | n/a | Report only (proprietary) | | |
| 353 | +| **McKinsey MGI** | 2023 | Proprietary activity–capability model | Work activity (~2,100) | % of work time automatable; adoption scenarios | Partial | n/a | Report only (proprietary) | | |
| 354 | +| **Pew Research (Kochhar)** | 2023 | Felten-style ability importance | Occupation | High/medium/low exposure | No | n/a | Report + appendix | | |
| 355 | +| **ILO Gmyrek et al. WP96** | 2023 | GPT-4-rated ISCO task scores | ISCO task → occupation → global employment | Automation vs augmentation potential, 0–1 | **Yes** | n/a | Scores in WP annexes | | |
| 356 | +| **IMF Cazzaniga et al. (AIOE + C-AIOE)** | 2024 | AIOE + complementarity shielding index | Occupation → country employment | Exposure × complementarity quadrants | **Yes** (complementarity) | n/a | AIPI dashboard; WP data | | |
| 357 | +| **ILO–NASK refined index (WP140)** | 2025 | Hybrid: 52,558 human ratings calibrating GPT-4o, 4-gradient scale | Task → ISCO occupation → 100+ countries | 4 exposure gradients | **Yes** (gradient) | n/a | WP + interactive tool | | |
| 358 | +| **OECD AI Exposure Measure** | 2025/2026 | Occupation capability requirements vs OECD AI Capability Indicators (9 domains, expert-set levels) | Ability-domain → occupation | Capability-overlap exposure; versioned as AI levels advance | No | New | OECD publication + indicators | | |
| 359 | +| **PwC AI Jobs Barometer** | 2024–2026 | ~1B job ads; demand-side | Vacancy/occupation/industry | Growth, wage premium, skill-change rates | No | Is itself outcome data | Annual reports | | |
| 360 | +| **Anthropic Economic Index** | 2025–2026 (6 releases) | Observed Claude usage classified to O*NET tasks (Clio); AUI; primitives | Conversation → task → occupation, geo | Usage shares; automation vs augmentation %; complexity/success | **Yes** (measured, not predicted) | Is itself adoption data; used in Canaries | **Hugging Face (open)** | | |
| 361 | +| **OpenAI / Chatterji et al.** | 2025 | Observed ChatGPT usage classification | Message → task category | Usage shares by intent | Partial (Asking/Doing/Expressing) | Is itself adoption data | NBER paper (aggregates) | | |
| 362 | +| **Canaries in the Coal Mine (Stanford DEL)** | 2025 | ADP payroll × exposure scores (realized effects) | Worker-level panel | Employment effects by age × exposure | Uses AEI automation/augmentation | **Is the validation** | Dashboard public; ADP restricted | | |
| 363 | +| **Iceberg Index (MIT/ORNL)** | 2025 | Skill-level tool coverage + agent-based simulation | 32k skills → 923 occupations → counties | % of wage bill exposed ($) | No | New | iceberg.mit.edu; arXiv | | |
| 364 | +| **Yin, Vu & Persico (multi-model replication)** | 2026 | Meta: Eloundou rubric × 4 frontier raters | Task → occupation | Rater-dispersion bounds | n/a | Meta-validation | NBER WP 35110 | | |
| 365 | +| **Frank, Ahn & Moro (ensemble)** | 2025 | Ensemble of 10 exposure scores vs UI-claims risk | Occupation × state × month | Unemployment-risk R² | No | **Is the validation** | PNAS Nexus (open access) | | |
| 366 | + | |
| 367 | +--- | |
| 368 | + | |
| 369 | +<a name="10-implications"></a> | |
| 370 | +## 10. Implications for airiskindex.io v1 methodology | |
| 371 | + | |
| 372 | +Mapping the literature onto our five dimensions (weights from `packages/scoring/src/weights.ts`) and our exposure/substitution/augmentation triad. | |
| 373 | + | |
| 374 | +### Cross-cutting lessons (apply to the whole index) | |
| 375 | + | |
| 376 | +1. **Task-based is settled science** (Arntz 2016 → everyone since). Our O*NET task-level scoring with importance/frequency weights is the correct v1 backbone. Keep occupation scores as *derived*, never primary. | |
| 377 | +2. **Never collapse to one number without sub-scores.** Felten's "neutral exposure", ILO's automation/augmentation split, IMF's complementarity, and AEI's measured automation:augmentation ratio all show the field converging on our exposure/substitution/augmentation triad. This is a genuine differentiator — most indices still publish one headline number and get misquoted (Goldman's "300M jobs lost" problem). Our tone rule ("adaptation, not doom") is empirically supported: realized effects so far are cohort-specific task reallocation, not mass unemployment (Yale Budget Lab; Humlum & Vestergaard) — with real, measurable pain at the entry level (Canaries). | |
| 378 | +3. **LLM-as-evaluator is standard but fragile — multi-model rating is now table stakes.** Yin et al. (2026): 19x spread in headline statistics across frontier raters on identical data; Claude-family raters score exposure *highest* of all models. Concrete requirements for `apps/worker/src/raters/`: | |
| 379 | + - Rate every task with **≥2 (ideally 3) different frontier models** (`RATER_MODEL` must become a list or we add `RATER_MODEL_SECONDARY`); store per-model scores; publish cross-model agreement per occupation. | |
| 380 | + - Report **confidence intervals derived from rater disagreement** — this slots directly into our existing `score_low`/`score`/`score_high` schema. | |
| 381 | + - Calibrate LLM ratings against a **human-rated anchor set** (ILO–NASK's 52,558-judgment survey design is the gold standard; our expert Delphi overrides in `data/derived/expert_overrides/` serve this role — sample deliberately across the exposure spectrum, not just flagged disagreements). | |
| 382 | + - Prompt-version everything (we already do) and re-rate on model change — because the instrument co-evolves with the phenomenon (the "ruler" problem). | |
| 383 | +4. **Anchor thresholds in explicit, documented rubrics.** Eloundou's "≥50% time saving at equal quality" is the citable standard; SML's 23 questions show multi-criterion rubrics beat single judgments. Our prompts should decompose ratings into named criteria and require structured justifications (auditable per §6 of CLAUDE.md). | |
| 384 | +5. **Validate against outcomes, and say so publicly.** Frank et al. (2025): single scores explain <11% of unemployment risk; ensembles ~30–75%. We should (a) benchmark our composite against the public AEI usage data, PwC vacancy signals, and the Canaries dashboard; (b) publish a `docs/methodology/sensitivity/` correlation report against AIOE, GPTs-are-GPTs β, and ILO WP140 scores each release. Spearman ρ ≈ 0.84 between independent modern methodologies is the bar for "capturing the same signal". | |
| 385 | + | |
| 386 | +### Dimension-by-dimension | |
| 387 | + | |
| 388 | +| Dimension (weight) | Lessons from the literature | | |
| 389 | +|---|---| | |
| 390 | +| **`automatability` (0.35)** | This is Eloundou's E1/E2 construct + SML's rubric. Use a multi-criterion, multi-model LLM rubric with a time-savings-at-quality threshold; keep 5-point scale (matches ILO gradient practice and our expert-review disagreement rule). Distinguish "LLM alone" (E1) from "LLM + tooling/agents" (E2) — capability-staged variants (OAIES; OECD capability levels) show staging by AI generation makes scores updateable rather than obsolete. Critically: rate **automation vs augmentation potential separately per task** (ILO 2023/2025) so the composite's sub-scores are computed, not asserted. | | |
| 391 | +| **`feasibility` (0.20)** | Separating "conceivable" from "deployable now" is what F&O failed to do and what killed their forecast. Ground feasibility in *observed evidence*: AEI task-success rates (66–70% by complexity — Jan 2026 primitives), benchmark-linked measures (OECD AI Capability Indicators' 9 domains with attained levels; "Jagged Global Economy"), and Iceberg's tool-catalogue approach (is there an actual product performing this skill?). Feasibility should decay-adjust automatability: high automatability + low current success rate ⇒ wide CI, lower composite. | | |
| 392 | +| **`cost_ratio` (0.15)** | Least developed dimension in the literature — a genuine gap we can own. Only Webb (implicitly, via wages), Goldman (25% substitution assumption), and AEI's $/hr wage-equivalent task values touch it. Use O*NET-linked BLS wages (as AEI does: $48–49/hr average task value) vs API/inference cost per task-equivalent; store as integer cents per our conventions. Note Acemoglu's caution ("so-so automation"): low cost ratio can drive adoption even with mediocre quality — interact with feasibility. | | |
| 393 | +| **`barriers` (0.20)** | Directly validated by IMF's C-AIOE complementarity (physical presence, human contact, legal responsibility) — the reason judges are exposed but safe and telemarketers are not. Pre-2022 null results (OECD 2021; Acemoglu et al. 2022) prove barriers dominate short-run outcomes. Operationalize the IMF/Pizzinelli shielding factors at task level: regulation/licensing, liability, required physical presence, human-contact preference, data confidentiality. Humlum & Vestergaard (Denmark) show even *adoption* without workflow redesign yields ~3% time savings — organizational barriers belong here too. | | |
| 394 | +| **`adoption_velocity` (0.10)** | The 2025–26 revolution: use **measured** adoption, not guesses. Sources: AEI Hugging Face releases (task-level usage shares, automation:augmentation ratio, AUI by geography — open data, quarterly cadence), PwC Jobs Barometer (vacancy-side skill change 66% faster in exposed occupations), OpenAI usage paper. Sector velocity is empirically uneven (API vs consumer concentration diverging; GDP-elasticity of adoption 0.7) — justify per-sector velocity scores with these citations. Design for **time-series updates**: AEI shows adoption shares move 10+ pp in a year (automation 27%→39%→45–52% oscillation), so this dimension must re-score every `INDEX_VERSION`. | | |
| 395 | + | |
| 396 | +### Positioning / product implications | |
| 397 | + | |
| 398 | +- **Transparency is our moat and the field's known weakness:** McKinsey/Goldman are black boxes; even academic scores rarely ship rater-level data. We publish weights (`/api/v1/methodology`), prompts (versioned), per-model ratings, and CIs — no major index does all four. | |
| 399 | +- **Versioning is becoming an explicit norm** (OECD exposure measure designed to be updateable; AEI releases dated datasets). Our `INDEX_VERSION` + immutable `score_runs` architecture matches best practice; cite OECD/AEI precedent in METHODOLOGY.md. | |
| 400 | +- **CI bounds have empirical semantics now:** rater disagreement (Yin et al.) + human-LLM calibration error (ILO–NASK) + feasibility uncertainty (AEI success rates) are the three quantifiable components of `score_low`/`score_high`. | |
| 401 | +- **EU/France (ESCO/ROME) crosswalk:** ILO WP140 (ISCO-based) and the UK index (arXiv 2507.22748) are the reference patterns for adapting O*NET-trained scores to other taxonomies; document crosswalk loss explicitly. | |
| 402 | +- **Watch list for future versions:** agentic-AI exposure extensions (arXiv 2604.00186), benchmark-grounded dynamic scores (OECD capability levels; "Jagged Global Economy"), worker-centered metrics (Lund et al. taxonomy), and the Canaries dashboard as a rolling validation target. | |
| 403 | + | |
| 404 | +--- | |
| 405 | + | |
| 406 | +*Compiled via WebSearch, Tavily, WebFetch, and OpenAlex queries, 2026-08-05. All URLs verified live at compile time unless noted. This document feeds `docs/methodology/METHODOLOGY.md` §Related Work.* | |
added
docs/research/02-data-sources.md
+129 −0
@@ -0,0 +1,129 @@ | ||
| 1 | +# Raw Data Sources for the AI Risk Index ETL Pipeline | |
| 2 | + | |
| 3 | +**Research date:** 2026-08-05 · **Author:** Claude (web research pass) | |
| 4 | +**Scope:** every raw source `apps/etl` needs, with current versions, exact URLs, licensing, schema notes, and how each feeds `data/raw/` → `data/derived/`. | |
| 5 | + | |
| 6 | +> ⚠️ **Headline finding:** the project docs assume **O*NET 29.x**; the current production release is **O*NET 30.3 (May 2026)**, and **O*NET 31.0 lands in late August 2026** with a modernized Content Model. The 30.x series **renamed key files** (Technology Skills → *Software Skills*; Skills split into *Essential Skills* / *Transferable Skills*), which is a breaking change for any ETL written against 29.x file names. Pin one release per `INDEX_VERSION` and record it in the manifest. | |
| 7 | + | |
| 8 | +--- | |
| 9 | + | |
| 10 | +## Master table | |
| 11 | + | |
| 12 | +| # | Source | Current version / vintage | Primary URL | Format | License | Update cadence | Pipeline role | | |
| 13 | +|---|--------|---------------------------|-------------|--------|---------|----------------|---------------| | |
| 14 | +| 1 | O*NET Database | **30.3** (May 2026); 31.0 due late Aug 2026 | https://www.onetcenter.org/database.html | Excel/CSV/JSON, SQL (MySQL/SQL Server/Oracle), text | CC BY 4.0 (attribution: USDOL/ETA) | Quarterly (Feb/May/Aug/Nov-Dec) | Core: occupations, tasks, ratings → `data/raw/onet/` | | |
| 15 | +| 2 | O*NET Web Services | API v2.0 (serves 30.3) | https://services.onetcenter.org/ | REST/JSON | CC BY 4.0 + ToS | Always-current | Optional live lookups; not for bulk ETL | | |
| 16 | +| 3 | SOC 2018 taxonomy | 2018 (next revision ~2028) | https://www.bls.gov/soc/2018/ | XLSX/PDF | Public domain | ~Decennial | Occupation code spine (via O*NET-SOC 2019) | | |
| 17 | +| 4 | ESCO classification | **v1.2.1** (2025-12-10) | https://esco.ec.europa.eu/en/use-esco/download | CSV, SKOS/RDF, API | Free reuse (Decision 2011/833/EU) w/ attribution | ~Annual point releases | EU/FR occupation layer → `data/raw/esco/` | | |
| 18 | +| 5 | ESCO↔O*NET crosswalk (official) | 2022 report; CSV updated 2023-08 (ESCO v1.1 base) | https://esco.ec.europa.eu/en/use-esco/other-crosswalks | CSV | Same as ESCO | Irregular | Map O*NET-SOC → ESCO → `data/raw/esco/` | | |
| 19 | +| 6 | ROME 4.0 (France Travail) | Update of 2026-06-18; next Oct 2026 | https://www.data.gouv.fr/datasets/repertoire-operationnel-des-metiers-et-des-emplois-rome | CSV/XLSX (+ API) | Licence Ouverte 2.0 | ≥2×/year | FR occupation layer → `data/raw/rome/` | | |
| 20 | +| 7 | BLS OEWS wages+employment | **May 2025** (released 2026-05-15) | https://www.bls.gov/oes/tables.htm | XLSX in ZIP | Public domain | Annual (~May) | `cost_ratio` wages, employment weights → `data/raw/bls/oews/` | | |
| 21 | +| 8 | BLS Employment Projections | **2024–34** (released 2025-08-28); 2025–35 due ~Sep 2026 | https://www.bls.gov/emp/data/occupational-data.htm | XLSX | Public domain | Annual | Adoption-velocity / outlook context → `data/raw/bls/ep/` | | |
| 22 | +| 9 | Eurostat SES earnings | Ref. year **2022** (4-yearly; next 2026 ref, pub ~2028) | https://ec.europa.eu/eurostat/databrowser/view/earn_ses_hourly | TSV/SDMX API | CC BY 4.0 | Every 4 years | EU wages by ISCO-08 → `data/raw/eurostat/` | | |
| 23 | +| 10 | Eurostat LFS employment | Annual, latest 2025 | https://ec.europa.eu/eurostat/databrowser/view/lfsa_egai2d | TSV/SDMX API | CC BY 4.0 | Annual | EU employment by ISCO 2-digit → `data/raw/eurostat/` | | |
| 24 | +| 11 | INSEE salaires (France) | 2023 consolidated (Base Tous salariés, DSN) | https://www.insee.fr/fr/statistiques (Base Tous salariés) | XLSX/CSV | Open (Insee reuse) | Annual (~2-yr lag) | FR wages by PCS → `data/raw/insee/` | | |
| 25 | +| 12 | Census BTOS AI supplement | Collected 2025-11-17→2026-02-08; released **2026-04-23** | https://www.census.gov/hfp/btos/data_downloads | XLSX/CSV | Public domain (experimental) | Biweekly core; supplements episodic | `adoption_velocity` (US, by sector/state/size) → `data/raw/btos/` | | |
| 26 | +| 13 | Eurostat ICT-in-enterprises AI | Survey year **2025** (19.95% EU firms use AI) | Datasets `isoc_eb_ai`, `isoc_eb_ain2` | TSV/SDMX API | CC BY 4.0 | Annual (Dec/Jan) | `adoption_velocity` (EU, by NACE) → `data/raw/eurostat/` | | |
| 27 | +| 14 | Ramp AI Index | June 2026 (55.0% adoption) | https://ramp.com/data | CSV download + charts | Free; check Ramp terms | Monthly | High-frequency US adoption signal → `data/raw/ramp/` | | |
| 28 | +| 15 | Anthropic Economic Index | 6th release (**2026-06-26**, "Cadences") | https://huggingface.co/datasets/Anthropic/EconomicIndex | CSV (HF dataset) | Data CC-BY; code MIT | ~Quarterly | Observed task-level AI usage; rater calibration → `data/raw/aei/` | | |
| 29 | +| 16 | Stanford AI Index | **2026 edition** (9th, Apr 2026, 423 pp) | https://hai.stanford.edu/assets/files/ai_index_report_2026.pdf | PDF + public data appendix | Free (attribution) | Annual (~April) | Context stats for methodology doc / barriers | | |
| 30 | +| 17 | Felten AIOE | 2021 + GenAI variants | https://github.com/AIOE-Data/AIOE | XLSX | Free w/ citation | Static | Benchmark exposure scores → `data/raw/benchmarks/` | | |
| 31 | +| 18 | Eloundou "GPTs are GPTs" | Science 2024 replication | https://github.com/openai/GPTs-are-GPTs | CSV | MIT | Static | Benchmark exposure scores → `data/raw/benchmarks/` | | |
| 32 | +| 19 | Webb (2020) AI exposure | 2020 | https://www.michaelwebb.co (on request) | — | On request | Static | Optional benchmark (no public bulk file) | | |
| 33 | +| 20 | ILOSTAT | Continuous; incl. "employment by GenAI exposure" tables | https://ilostat.ilo.org | CSV/API | CC BY 4.0 | Continuous | Intl. harmonized employment; ILO GenAI exposure scores | | |
| 34 | + | |
| 35 | +--- | |
| 36 | + | |
| 37 | +## 1. O*NET Database (core input) | |
| 38 | + | |
| 39 | +- **Current release:** **O*NET 30.3**, May 2026 (source: https://www.onetcenter.org/db_releases.html). Release train since the project was scoped: 29.2 (Feb 2025) → 29.3 (May 2025) → 30.0 (Aug 2025) → 30.1 (Dec 2025) → 30.2 (Feb 2026, new four-level Job Zones) → 30.3 (May 2026, **modernized Content Model** + Specific Interests) → **31.0 expected late August 2026**. | |
| 40 | +- **Downloads:** https://www.onetcenter.org/database.html — formats: tabular (Excel/CSV/JSON), SQL loads for MySQL/PostgreSQL/MariaDB, SQL Server, Oracle, plus RDF. Full Excel archive: `https://www.onetcenter.org/dl_files/database/db_30_3_excel.zip` (individual files linked from the same page; per-format data dictionary at https://www.onetcenter.org/dictionary/30.3/excel/). | |
| 41 | +- **Files the ETL needs (30.3 row counts):** | |
| 42 | + - `Occupation Data` — 1,016 rows (O*NET-SOC code, title, description) | |
| 43 | + - `Task Statements` — 18,796 rows (task_id, task, task type, incumbents responding) | |
| 44 | + - `Task Ratings` — 161,559 rows (importance IM, relevance RL, frequency FT scales, with N, SE, CI bounds — feeds our task weights **and** our own CI propagation) | |
| 45 | + - `Tasks to DWAs`, `DWA Reference`, `IWA Reference` — task ↔ detailed/intermediate work activity links | |
| 46 | + - `Work Activities` — 73,308 rows (GWA ratings) | |
| 47 | + - `Abilities` — 92,976 · `Knowledge` — 59,004 · `Work Context` — 297,676 | |
| 48 | + - **Renamed in 30.x:** `Technology Skills` → **`Software Skills`** (31,821 rows); the old `Skills` file is now **`Essential Skills`** (17,880) + **`Transferable Skills`** (44,700). ETL loaders and any code referencing "Technology Skills" must be updated. | |
| 49 | +- **License:** **CC BY 4.0**. Required attribution: credit the "O*NET 30.3 Database by the U.S. Department of Labor, Employment and Training Administration (USDOL/ETA), used under the CC BY 4.0 license." Put this in the public methodology page and API `/api/v1/methodology` metadata. | |
| 50 | +- **Taxonomy:** O*NET-SOC **2019** taxonomy (built on SOC 2018): 1,016 occupation titles of which **923 are data-collection-level**; code format `XX-XXXX.XX` (SOC 6-digit + 2-digit O*NET suffix). ~891 occupations already updated in 2026 YTD. | |
| 51 | +- **O*NET Web Services:** register (free) at https://services.onetcenter.org/developer/signup; API v2.0 reference at https://services.onetcenter.org/reference. **No hard rate limit**, but ToS gives per-second/per-day guidance; on `429`, retry after ≥200 ms. Always serves the latest DB — good for spot checks, **not** for reproducible scoring (use pinned dumps). | |
| 52 | +- **SOC 2018:** 867 detailed occupations → 459 broad → 98 minor → 23 major groups; code `XX-XXXX`. Definitions/structure files at https://www.bls.gov/soc/2018/ (XLSX). Public domain. | |
| 53 | +- **Pipeline:** fetch script downloads the pinned release ZIP into `data/raw/onet/db_30_3/`, verifies SHA-256, records release + hash in the derived manifest. Never edit in place. | |
| 54 | + | |
| 55 | +## 2. ESCO + crosswalks + ROME (EU/France layer) | |
| 56 | + | |
| 57 | +- **ESCO v1.2.1** (last update 2025-12-10). Portal: https://esco.ec.europa.eu/en · downloads (per-language CSV + "language independent" files, SKOS/RDF, Local API): https://esco.ec.europa.eu/en/use-esco/download (free account/selection flow). ~3,000 occupations mapped to ISCO-08; **13,939 skills** in v1.2.1. Web API: https://esco.ec.europa.eu/en/use-esco (base `https://ec.europa.eu/esco/api`). | |
| 58 | +- **License:** free reuse under Commission Decision 2011/833/EU; must publish the ESCO acknowledgement statement (see FAQ: https://esco.ec.europa.eu/en/about-esco/faq). | |
| 59 | +- **ESCO↔O*NET official crosswalk** (co-created EC + USDOL, AI-assisted with human validation): | |
| 60 | + - Page: https://esco.ec.europa.eu/en/use-esco/other-crosswalks (also listed at https://www.onetcenter.org/crosswalks.html) | |
| 61 | + - CSV: `https://esco.ec.europa.eu/system/files/2023-08/ONET_%28Occupations%29_0_updated.csv` | |
| 62 | + - Two published variants: (a) exact/narrow/broad/close matches (QA'd, USDOL-validated); (b) same + "related" matches (**lower quality — not validated**; exclude from scoring joins by default). | |
| 63 | + - Technical report: `https://esco.ec.europa.eu/system/files/2022-12/ONET%20ESCO%20Technical%20Report.pdf`. Built against **ESCO v1.1 / O*NET-SOC 2019** (the O*NET Web Services crosswalk endpoint still states ESCO v1.1.0) — re-verify concept URIs against v1.2.1 during ETL; unmatched URIs go to a QA report. | |
| 64 | + - Bonus: **ESCO↔NACE crosswalk** now available: `https://esco.ec.europa.eu/system/files/2026-02/ESCO-NACE%20rev.%202.1%20crosswalk.xlsx` (useful for joining Eurostat sector-level AI adoption to occupations). | |
| 65 | +- **ESCO↔ROME:** no single public EC file. France Travail maintains ROME↔ESCO correspondence tables under the EURES obligation (each member state maps its national classification to ESCO); the ROME open-data bundle includes correspondence referentials, and EURES member-state mapping tables are listed on the ESCO portal ("EURES Countries Mapping Tables"). Validate coverage during ETL and fall back to ROME→ISCO-08→ESCO if a direct table is missing for some fiches. | |
| 66 | +- **ROME 4.0 (France Travail):** | |
| 67 | + - Open data: https://www.data.gouv.fr/datasets/repertoire-operationnel-des-metiers-et-des-emplois-rome — multiple referential files (arborescence, compétences, contextes, mobilité). **Licence Ouverte / Open Licence 2.0.** Last update 2026-06-18; **next update announced for Oct 2026**; ≥2 updates/year. | |
| 68 | + - Also via API on https://francetravail.io (ROME 4.0 APIs, OAuth key) and mirrored on https://www.francetravail.org/opendata/. | |
| 69 | + - Code format: 1 letter + 4 digits (e.g., `M1607`); ~600 fiches métiers organized by 14 domaines. | |
| 70 | + - Pipeline: `data/raw/rome/` with the data.gouv.fr resource URLs + version date in the manifest. | |
| 71 | + | |
| 72 | +## 3. Wage data | |
| 73 | + | |
| 74 | +- **BLS OEWS — May 2025** (released **2026-05-15**; next: May 2026 data in spring 2027). ~830 SOC occupations; employment, mean/median hourly & annual wages, wage percentiles (10/25/50/75/90), by nation/state/MSA/industry. | |
| 75 | + - Tables hub: https://www.bls.gov/oes/tables.htm → ZIPs `oesm25nat.zip` (national), `oesm25st.zip` (states), `oesm25ma.zip` (metro), national-by-industry files (served from `https://www.bls.gov/oes/special-requests/…`; BLS blocks non-browser user agents — use a browser UA in the fetch script). Field layout documented in each ZIP's `field_descriptions` sheet. | |
| 76 | + - License: US government work, public domain. Cadence: annual. | |
| 77 | + - Pipeline: national file feeds `cost_ratio` (wage denominator, stored as **integer cents + USD** per repo convention) and employment weights; join key = SOC 2018 6-digit → O*NET-SOC 2019 (strip `.XX` suffix / use O*NET-SOC↔SOC crosswalk). | |
| 78 | +- **Eurostat SES (Structure of Earnings Survey):** 4-yearly, latest reference year **2022** (published 2024–25; next ref-year 2026 published ~2028). Datasets: `earn_ses_hourly` (and monthly/annual variants) — mean/median hourly earnings by **ISCO-08 2-digit** × NACE × country. Databrowser: https://ec.europa.eu/eurostat/databrowser/view/earn_ses_hourly · bulk via SDMX API `https://ec.europa.eu/eurostat/api/dissemination/sdmx/2.1/data/earn_ses_hourly?format=TSV`. License CC BY 4.0. Occupation resolution is only 2-digit ISCO — EU `cost_ratio` will be coarser than US; flag in methodology. | |
| 79 | +- **France:** INSEE *Base Tous salariés* (from DSN, formerly DADS): salaire net **EQTP** by PCS (up to 4-digit), sector, sex; latest consolidated 2023 (see Insee Première n°1938 for 2021: https://www.insee.fr/fr/statistiques/6799523; séries longues: https://www.insee.fr/fr/statistiques/8660332). DARES publishes wage/employment "portraits statistiques des métiers" by FAP. Requires PCS↔ROME/ISCO crosswalk (INSEE publishes PCS↔ISCO tables) — France-specific wage joins are a v2 concern. | |
| 80 | + | |
| 81 | +## 4. Employment counts & projections | |
| 82 | + | |
| 83 | +- **OEWS employment** (same May 2025 files as §3) — primary US employment weights. | |
| 84 | +- **BLS Employment Projections 2024–34** (released 2025-08-28; **2025–35 edition expected ~Sept 2026** — recheck before ingesting): https://www.bls.gov/emp/data/occupational-data.htm — "All occupational tables in a single file (XLSX)", National Employment Matrix 2024/2034, occupational separations, plus the new **skills data tables** (importance of skills by occupation). Public domain. Feeds adoption-velocity priors and UI "outlook" context. State-level: https://projectionscentral.org/longterm (REST + download). | |
| 85 | +- **Eurostat LFS:** `lfsa_egai2d` — employed persons by detailed occupation (ISCO-08 2-digit), annual: https://ec.europa.eu/eurostat/databrowser/view/lfsa_egai2d (SDMX API as above). CC BY 4.0. | |
| 86 | +- **ILOSTAT:** https://ilostat.ilo.org — harmonized employment by ISCO level 2 (annual/quarterly) across countries, CSV bulk + API, CC BY 4.0. Notably now publishes **"Employment by sex and generative AI exposure"** tables (based on the ILO/Gmyrek GenAI occupational exposure scores) — both a benchmark and a ready-made employment-by-exposure aggregate. | |
| 87 | + | |
| 88 | +## 5. AI adoption data (`adoption_velocity` dimension) | |
| 89 | + | |
| 90 | +- **US Census BTOS AI supplement:** third AI supplement collected **2025-11-17 → 2026-02-08**, released **2026-04-23** (press: https://www.census.gov/newsroom/press-releases/2026/btos-apr-23.html). Measures firm AI use overall and — new this cycle — **by worker tasks and business functions**, split by NAICS sector, state, and firm size. Data hub: https://www.census.gov/hfp/btos/data (Downloads tab: https://www.census.gov/hfp/btos/data_downloads, incl. historical; API tab available). Core biweekly BTOS also carries a recurring "AI use in last two weeks" item (wording revised Nov 2025 — treat as a series break). ~1.2M-business sample; experimental data product; public domain. | |
| 91 | +- **Eurostat — AI in enterprises:** datasets **`isoc_eb_ai`** (enterprises using AI technologies) and **`isoc_eb_ain2`** (by purpose/technology × NACE), survey year **2025**: 19.95% of EU enterprises use AI (55.03% of large firms); Statistics Explained article (updated Dec 2025, next Dec 2026): https://ec.europa.eu/eurostat/statistics-explained/index.php?title=Use_of_artificial_intelligence_in_enterprises. Join to occupations via sector (NACE) using the ESCO-NACE crosswalk (§2). CC BY 4.0. | |
| 92 | +- **Ramp AI Index (Ramp Economics Lab):** https://ramp.com/data — monthly AI adoption among US firms from card/bill-pay spend of 70k+ businesses (June 2026: **55.0%**, +0.8 pp MoM), broken out by size and sector, downloadable; methodology: https://econlab.substack.com/p/how-ramp-data-works. Now shifting to *intensity* tracking. Free/open resource; confirm redistribution terms before committing derived aggregates. | |
| 93 | +- **Anthropic Economic Index:** HF dataset **`Anthropic/EconomicIndex`** (https://huggingface.co/datasets/Anthropic/EconomicIndex). Release folders: `release_2025_02_10` (initial O*NET task mappings, automation vs augmentation), `release_2025_03_27` (cluster-level, thinking-mode fractions per O*NET task), `release_2025_09_15` (geography + 1P API), `release_2026_01_15` ("economic primitives"), `release_2026_03_24` ("learning curves"), `release_2026_06_26` ("Cadences", monthly aggregates), plus `labor_market_impacts/`. **Data CC-BY (repo metadata lists MIT for code).** This is the single most direct empirical input for calibrating our LLM-rater `automatability`/`feasibility` scores against observed usage — it is keyed to **O*NET task statements**, same spine as ours. | |
| 94 | +- **Stanford AI Index 2026** (9th edition, April 2026, 423 pp): report PDF https://hai.stanford.edu/assets/files/ai_index_report_2026.pdf; public data appendix downloadable from the HAI AI Index page. Key 2026 stats: 53% population-level GenAI adoption; $172B est. US consumer surplus. Use for methodology narrative and `barriers`/`adoption_velocity` context, not row-level joins. Annual. | |
| 95 | + | |
| 96 | +## 6. Published occupation-level exposure benchmarks | |
| 97 | + | |
| 98 | +These are **validation benchmarks** (`data/raw/benchmarks/`), not scoring inputs — our methodology must remain independently reproducible. | |
| 99 | + | |
| 100 | +| Dataset | What it is | URL / file | License | | |
| 101 | +|---|---|---|---| | |
| 102 | +| **Felten–Raj–Seamans AIOE** | AI Occupational Exposure by 6-digit SOC (10 AI applications × 52 O*NET abilities); + Language-Modeling and Image-Generation GenAI variants, industry (AIIE) & geography | https://github.com/AIOE-Data/AIOE → `AIOE_DataAppendix.xlsx`, `Language Modeling AIOE and AIIE.xlsx`, `Image Generation AIOE and AIIE.xlsx` | Free; citation required (SMJ 42(12):2195–2217, 2021) | | |
| 103 | +| **Eloundou et al. "GPTs are GPTs"** (Science 2024) | Task- and occupation-level LLM exposure (α=E1, β=E1+0.5·E2, γ=E1+E2; human + GPT-4 ratings) on O*NET tasks | https://github.com/openai/GPTs-are-GPTs → `occ_level.csv` (occupation), task-level in `data/` | MIT | | |
| 104 | +| **Webb (2020)** | Patent-text-based AI exposure by SOC | https://www.michaelwebb.co/webb_ai.pdf — **data on request only** (no public bulk file) | On request | | |
| 105 | +| **Anthropic AEI task exposure** | Observed Claude usage mapped to O*NET tasks (see §5) | HF `Anthropic/EconomicIndex` | CC-BY | | |
| 106 | +| **ILO GenAI exposure (Gmyrek et al.)** | ISCO-08 occupation GenAI exposure gradients, used in ILOSTAT employment tables | via https://ilostat.ilo.org | CC BY 4.0 | | |
| 107 | +| Context | Yale Budget Lab compared 7 exposure metrics (large disagreement on *most*-exposed, agreement on *least*-exposed); Brookings (Mar 2026) methodology uses AIOE + OEWS | brookings.edu methods PDF | — | | |
| 108 | + | |
| 109 | +Use for the sensitivity analyses in `docs/methodology/sensitivity/` (rank correlations of our composite vs AIOE / GPTs-are-GPTs / AEI). | |
| 110 | + | |
| 111 | +--- | |
| 112 | + | |
| 113 | +## Ingestion order & manifest plan | |
| 114 | + | |
| 115 | +Order respects join dependencies (occupation spine first, then attributes, then joins): | |
| 116 | + | |
| 117 | +1. **SOC 2018 structure** (`data/raw/soc/`) — code spine, 867 detailed occupations. | |
| 118 | +2. **O*NET 30.3 full dump** (`data/raw/onet/db_30_3/`) — pin release; load order: Occupation Data → Task Statements → Task Ratings → Tasks-to-DWAs/IWAs → Work Activities → Abilities/Knowledge → Software/Essential/Transferable Skills → Work Context. *Decision needed before build:* stay on 30.3 or wait for 31.0 (late Aug 2026) — either way, `INDEX_VERSION` notes the O*NET release. | |
| 119 | +3. **OEWS May 2025** (`data/raw/bls/oews/`) — wages (→ integer cents) + employment; join on SOC 6-digit. | |
| 120 | +4. **BLS EP 2024–34** (`data/raw/bls/ep/`) — projections + skills tables. | |
| 121 | +5. **ESCO v1.2.1** (`data/raw/esco/classification/`) + **ESCO↔O*NET crosswalk** (`data/raw/esco/crosswalk/`) — validate URI coverage vs v1.2.1; exclude "related" matches from scoring joins. | |
| 122 | +6. **ROME 4.0** (`data/raw/rome/`) + ROME↔ESCO correspondence; fallback path ROME→ISCO-08→ESCO. | |
| 123 | +7. **Eurostat**: `earn_ses_hourly` (SES 2022), `lfsa_egai2d` (LFS), `isoc_eb_ai(n2)` (AI adoption) via SDMX API pulls (`data/raw/eurostat/`). | |
| 124 | +8. **Adoption signals**: BTOS AI supplement, Ramp AI Index, AEI releases (`data/raw/btos|ramp|aei/`). | |
| 125 | +9. **Benchmarks**: AIOE, GPTs-are-GPTs, ILO GenAI exposure (`data/raw/benchmarks/`) — validation only. | |
| 126 | + | |
| 127 | +**Manifest rules** (per repo convention — raw payloads are gitignored, manifests committed): for every fetched artifact record `{source, version_label, source_url, fetch_date, sha256, row_count, license, attribution_string}` in `data/derived/manifests/<source>.json`. The fetch script must fail loudly if a pinned URL 404s or the hash changes (BLS and O*NET replace files in place across releases). Re-verify before each `INDEX_VERSION` bump: O*NET quarterly page, OEWS annual page (mid-May), EP annual page (late Aug/Sep), ESCO portal (point releases), ROME (Oct 2026), BTOS supplement announcements, AEI HF repo. | |
| 128 | + | |
| 129 | +**Attribution block for the public methodology page:** O*NET (USDOL/ETA, CC BY 4.0) · ESCO (© European Union, ESCO acknowledgement statement) · ROME (France Travail, Licence Ouverte 2.0) · BLS/Census (public domain, cite program + vintage) · Eurostat (CC BY 4.0) · Anthropic Economic Index (CC-BY) · benchmark papers cited per their requirements. | |
added
docs/research/03-llm-rater-api.md
+329 −0
@@ -0,0 +1,329 @@ | ||
| 1 | +# LLM Rater — Anthropic API Reference (August 2026) | |
| 2 | + | |
| 3 | +Practical, current reference for the task-rating pipeline in `apps/worker/src/raters/` (CLAUDE.md §6). | |
| 4 | +Scope: model choice, Message Batches API, structured outputs, prompt caching, rate limits, TypeScript SDK | |
| 5 | +patterns, and a concrete cost estimate for rating ~18,000 O*NET task statements × 3 samples. | |
| 6 | + | |
| 7 | +All facts verified against the official docs on **2026-08-05**. Note: `https://docs.claude.com/en/api/overview` | |
| 8 | +(the URL in CLAUDE.md §6) now 301-redirects to `https://platform.claude.com/docs/en/api/overview` — the docs | |
| 9 | +moved to `platform.claude.com`. | |
| 10 | + | |
| 11 | +Sources: | |
| 12 | + | |
| 13 | +- Models overview — https://platform.claude.com/docs/en/about-claude/models/overview | |
| 14 | +- Pricing — https://platform.claude.com/docs/en/about-claude/pricing | |
| 15 | +- Batch processing — https://platform.claude.com/docs/en/build-with-claude/batch-processing | |
| 16 | +- Rate limits — https://platform.claude.com/docs/en/api/rate-limits | |
| 17 | +- Structured outputs — https://platform.claude.com/docs/en/build-with-claude/structured-outputs | |
| 18 | +- Prompt caching — https://platform.claude.com/docs/en/build-with-claude/prompt-caching | |
| 19 | + | |
| 20 | +--- | |
| 21 | + | |
| 22 | +## 1. Current model lineup and pricing (per MTok, standard API) | |
| 23 | + | |
| 24 | +| Model | ID (set via `RATER_MODEL`) | Context / max output | Input | Output | Batch input | Batch output | Cache read | | |
| 25 | +|---|---|---|---|---|---|---|---| | |
| 26 | +| Claude Fable 5 | `claude-fable-5` | 1M / 128K | $10 | $50 | $5 | $25 | $1.00 | | |
| 27 | +| Claude Opus 5 | `claude-opus-5` | 1M / 128K | $5 | $25 | $2.50 | $12.50 | $0.50 | | |
| 28 | +| Claude Opus 4.8 | `claude-opus-4-8` | 1M / 128K | $5 | $25 | $2.50 | $12.50 | $0.50 | | |
| 29 | +| Claude Sonnet 5 (intro, **through 2026-08-31**) | `claude-sonnet-5` | 1M / 128K | $2 | $10 | $1 | $5 | $0.20 | | |
| 30 | +| Claude Sonnet 5 (from 2026-09-01) | `claude-sonnet-5` | 1M / 128K | $3 | $15 | $1.50 | $7.50 | $0.30 | | |
| 31 | +| Claude Sonnet 4.6 | `claude-sonnet-4-6` | 1M / 128K | $3 | $15 | $1.50 | $7.50 | $0.30 | | |
| 32 | +| Claude Haiku 4.5 | `claude-haiku-4-5` | 200K / 64K | $1 | $5 | $0.50 | $2.50 | $0.10 | | |
| 33 | + | |
| 34 | +Cache writes: 1.25× base input (5-min TTL) or 2× base input (1-hour TTL). Cache multipliers **stack with the | |
| 35 | +batch discount** (confirmed in the pricing doc: "These multipliers stack with other pricing modifiers, | |
| 36 | +including the Batch API discount"). All model IDs from the 4.6 generation onward are dateless pinned | |
| 37 | +snapshots — no date suffix to append. | |
| 38 | + | |
| 39 | +Tokenizer note: models from Opus 4.7 onward (incl. Fable 5, Opus 5, Sonnet 5) use a tokenizer that yields | |
| 40 | +~30% more tokens for the same text than Sonnet 4.6/Haiku 4.5. Budget token estimates per model, not globally. | |
| 41 | + | |
| 42 | +### Which tier for large-scale 5-point task rating? | |
| 43 | + | |
| 44 | +For a structured, rubric-guided 5-point rating with a short rationale (a classification-plus-justification | |
| 45 | +task, not open-ended reasoning): | |
| 46 | + | |
| 47 | +- **Haiku 4.5** — cheapest ($0.50/$2.50 batch), fast, supports structured outputs. Likely adequate for the | |
| 48 | + bulk of clear-cut tasks, but weakest calibration on ambiguous tasks — expect more >1-point disagreements | |
| 49 | + flowing into the expert-panel queue (which costs human time). | |
| 50 | +- **Claude Sonnet 5** — **recommended default.** Near-Opus quality on judgment tasks; at the introductory | |
| 51 | + price ($1/$5 batch through Aug 31, 2026) the full 54k-rating job costs ~$160–250 (see §7) — the marginal | |
| 52 | + cost over Haiku is trivial relative to the human-review pipeline it feeds. Caveat: adaptive thinking is on | |
| 53 | + by default and thinking tokens bill as output; for this task set `thinking: {type: "disabled"}` (accepted | |
| 54 | + on Sonnet 5) or keep it on with `output_config: {effort: "low"}` and budget extra output tokens. | |
| 55 | +- **Opus 5 / Fable 5** — overkill for the volume run. Total quality of the index is bounded by the | |
| 56 | + methodology and human validation, not by Opus-vs-Sonnet deltas on a 5-point scale. Best use: (a) rate the | |
| 57 | + expert-panel calibration subset with Opus 5 as a second opinion, or (b) adjudicate flagged disagreements. | |
| 58 | + | |
| 59 | +Practical plan: pilot ~500 tasks on both Haiku 4.5 and Sonnet 5, compare agreement with the human 5% sample, | |
| 60 | +and pick per the disagreement rate. Sampling-variance note for the 3-samples design: **Sonnet 5, Opus 5, and | |
| 61 | +Opus 4.7+ reject non-default `temperature`/`top_p`/`top_k` (400 error)** — you cannot set temperature for | |
| 62 | +the 3 samples on those models; between-sample variance is whatever the model naturally produces. Haiku 4.5 | |
| 63 | +and Sonnet 4.6 still accept temperature. | |
| 64 | + | |
| 65 | +## 2. Message Batches API | |
| 66 | + | |
| 67 | +Docs: https://platform.claude.com/docs/en/build-with-claude/batch-processing | |
| 68 | + | |
| 69 | +- **Endpoints:** `POST /v1/messages/batches` (create), `GET /v1/messages/batches/{id}` (poll), | |
| 70 | + `GET <results_url>` (stream `.jsonl` results), `POST /v1/messages/batches/{id}/cancel`, `GET /v1/messages/batches` (list). | |
| 71 | +- **Discount:** flat **50% off both input and output tokens**, all models, all features (vision, tools, | |
| 72 | + structured outputs, prompt caching all supported inside batches). | |
| 73 | +- **Limits:** max **100,000 requests or 256 MB** per batch, whichever comes first. Each request needs a | |
| 74 | + `custom_id` matching `^[a-zA-Z0-9_-]{1,64}$`. `max_tokens` must be ≥ 1 (`max_tokens: 0` cache pre-warming | |
| 75 | + is rejected inside batches). | |
| 76 | +- **Timing:** most batches finish < 1 hour; hard **24-hour expiration** — requests not processed by then | |
| 77 | + come back as `expired` (not billed) and must be resubmitted. | |
| 78 | +- **Results:** available at `results_url` once `processing_status === "ended"`; delivered as JSONL, **in | |
| 79 | + arbitrary order — always key by `custom_id`, never by position**. Results are downloadable for **29 days** | |
| 80 | + after batch creation; persist them to `data/derived/ratings/` + DB immediately. | |
| 81 | +- **Per-request result types:** `succeeded` (has `.result.message`), `errored` (invalid request → fix and | |
| 82 | + resubmit; server error → safe to retry; not billed), `canceled` (not billed), `expired` (not billed — | |
| 83 | + resubmit). | |
| 84 | +- **Extended output:** Opus 5/4.8/4.7/4.6, Sonnet 5/4.6 support up to 300k output tokens in batches via the | |
| 85 | + `output-300k-2026-03-24` beta header (not needed for 500-token ratings). | |
| 86 | +- **No server-side idempotency key on batch create** — dedupe is your job (below). | |
| 87 | + | |
| 88 | +### Marrying batches with BullMQ | |
| 89 | + | |
| 90 | +Our invariant (CLAUDE.md §6): BullMQ job ID = deterministic hash of `task_id + prompt_version`. Extend to | |
| 91 | +`hash(task_id + prompt_version + sample_index)` since each task is rated 3×. Recommended architecture: | |
| 92 | + | |
| 93 | +1. **`rating-request` rows, not per-rating jobs.** Persist one DB row per (task, prompt_version, sample) | |
| 94 | + with status `pending`. A hex SHA-256 (truncated to 32–48 chars) of `taskId:promptVersion:sampleIdx` | |
| 95 | + satisfies both the BullMQ job-ID and the batch `custom_id` charset — **use the same string for both**, so | |
| 96 | + a batch result maps 1:1 to a job/row. | |
| 97 | +2. **`batch-submitter` job** (BullMQ, repeatable or triggered): collects up to 100k `pending` rows, calls | |
| 98 | + `batches.create()`, stores `batch_id` on the rows *before* flipping them to `submitted` — if the process | |
| 99 | + dies after create but before persist, on restart list recent batches and reconcile by `custom_id` rather | |
| 100 | + than re-creating (this is the idempotency seam; the API will happily accept duplicate custom_ids across | |
| 101 | + batches and bill you twice). | |
| 102 | +3. **`batch-poller` job** with BullMQ job ID = `poll:${batchId}` (deterministic → re-enqueue is a no-op), | |
| 103 | + repeat/delay ~60s until `processing_status === "ended"`. | |
| 104 | +4. **`batch-ingester`** streams results, and per result: store raw JSON response, parsed score, model, | |
| 105 | + prompt version, timestamp (full audit trail); `errored`(server)/`expired` → flip row back to `pending` | |
| 106 | + so the next submitter run resubmits; `errored`(invalid_request) → dead-letter for inspection. | |
| 107 | +5. A prompt-version bump changes every hash → new custom_ids → old cached rows are naturally invalidated, | |
| 108 | + exactly matching the CLAUDE.md rule. | |
| 109 | + | |
| 110 | +The whole 54,000-rating run fits in **one batch** (well under 100k requests and 256 MB), even at the Start | |
| 111 | +tier queue limit (200k requests in processing queue). | |
| 112 | + | |
| 113 | +## 3. Structured outputs for the 5-point rating | |
| 114 | + | |
| 115 | +**Structured outputs is GA** (no beta header; the old `structured-outputs-2025-11-13` header and top-level | |
| 116 | +`output_format` request param are deprecated transition shims). Two mechanisms: | |
| 117 | + | |
| 118 | +1. **JSON outputs** — `output_config: {format: {type: "json_schema", schema: {...}}}` constrains the | |
| 119 | + response text to schema-valid JSON. | |
| 120 | +2. **Strict tool use** — `strict: true` on a tool definition; guarantees `tool_use.input` validates. | |
| 121 | + | |
| 122 | +**Recommendation: use JSON outputs (`output_config.format`), not tool-forced JSON.** Rationale: | |
| 123 | + | |
| 124 | +- It's the purpose-built mechanism for "the response *is* the structured object" — no fake tool, no | |
| 125 | + `tool_choice` forcing, one fewer moving part in the audit trail. | |
| 126 | +- Guaranteed-valid JSON with `required` fields → the "parsed score" column can be extracted without retry | |
| 127 | + loops. | |
| 128 | +- Works with the **Batches API**, streaming, and thinking. (Incompatible with citations and prefilling — | |
| 129 | + neither is used here.) | |
| 130 | +- Schema limits that matter to us: `enum` is supported (use `"score": {"enum": [1,2,3,4,5]}` — do **not** | |
| 131 | + use `minimum`/`maximum`, numeric range constraints are unsupported); no `minLength`/`maxLength` on the | |
| 132 | + rationale string (enforce length in the prompt); `additionalProperties: false` is mandatory on every | |
| 133 | + object. | |
| 134 | +- First use of a schema pays a one-time grammar-compilation latency; compiled grammars are cached 24h — | |
| 135 | + irrelevant inside a batch run that reuses one schema 54,000×. Note that **changing `output_config.format` | |
| 136 | + invalidates the prompt cache**, so treat the schema like the rubric: versioned with `RATER_PROMPT_VERSION`. | |
| 137 | + | |
| 138 | +Suggested schema: | |
| 139 | + | |
| 140 | +```json | |
| 141 | +{ | |
| 142 | + "type": "object", | |
| 143 | + "properties": { | |
| 144 | + "score": { "type": "integer", "enum": [1, 2, 3, 4, 5] }, | |
| 145 | + "rationale": { "type": "string", "description": "2-3 sentence justification citing the rubric" }, | |
| 146 | + "confidence": { "type": "string", "enum": ["low", "medium", "high"] } | |
| 147 | + }, | |
| 148 | + "required": ["score", "rationale", "confidence"], | |
| 149 | + "additionalProperties": false | |
| 150 | +} | |
| 151 | +``` | |
| 152 | + | |
| 153 | +Structured outputs injects a system-prompt preamble explaining the format (small, fixed token overhead per | |
| 154 | +request). | |
| 155 | + | |
| 156 | +## 4. Prompt caching for the shared rubric | |
| 157 | + | |
| 158 | +Layout: `tools` → `system` → `messages` renders in that order and caching is a **byte-exact prefix match**. | |
| 159 | +Put the ~2k-token rubric in `system` with `cache_control` on its last block; the per-task variable content | |
| 160 | +(task statement, occupation context) goes in the user message, after the breakpoint: | |
| 161 | + | |
| 162 | +```ts | |
| 163 | +system: [ | |
| 164 | + { type: "text", text: RUBRIC_V3, // frozen per RATER_PROMPT_VERSION — no timestamps, no task data | |
| 165 | + cache_control: { type: "ephemeral", ttl: "1h" } }, | |
| 166 | +], | |
| 167 | +messages: [{ role: "user", content: `Occupation: ${occ}\nTask: ${taskStatement}\nRate this task.` }] | |
| 168 | +``` | |
| 169 | + | |
| 170 | +Key facts: | |
| 171 | + | |
| 172 | +- **Pricing:** cache read = 0.1× base input; write = 1.25× (5-min TTL) or 2× (1-hour TTL). **Stacks with the | |
| 173 | + batch discount** → a cached rubric token inside a batch costs 0.05× base input. | |
| 174 | +- **Inside batches use the 1-hour TTL** (official recommendation): batch requests process concurrently over | |
| 175 | + up to an hour, so 5-min entries can lapse between hits. Caveat: cache hits inside a batch are | |
| 176 | + best-effort — parallel workers may each miss; treat the §7 "with caching" numbers as the optimistic bound. | |
| 177 | +- **Minimum cacheable prefix is model-dependent and non-monotonic:** 512 tokens (Opus 5/Fable 5), 1,024 | |
| 178 | + (Opus 4.8, Sonnet 5, Sonnet 4.6), 2,048 (Opus 4.7), **4,096 (Haiku 4.5, Opus 4.6)**. **A 2k-token rubric | |
| 179 | + silently will not cache on Haiku 4.5** — no error, just `cache_creation_input_tokens: 0`. If Haiku is | |
| 180 | + chosen, either accept uncached input (still cheap) or grow the cached prefix ≥4,096 tokens (e.g. include | |
| 181 | + the scoring examples/anchors in the system block). | |
| 182 | +- Cost math for the rubric alone (Sonnet 5 intro, batch, 54k requests, 2,000 tokens): | |
| 183 | + uncached = 54,000 × 2,000 × $1/MTok = **$108**; cached (1 write + 54k reads at 0.05×) ≈ 108M × $0.10/MTok | |
| 184 | + ≈ **$10.80**. ~10× saving on the shared-prefix portion. | |
| 185 | +- Verify via `usage.cache_read_input_tokens` in each batch result; zero across the run means a silent | |
| 186 | + invalidator (non-deterministic serialization, per-request content above the breakpoint). | |
| 187 | + | |
| 188 | +## 5. Rate limits relevant to batch rating throughput | |
| 189 | + | |
| 190 | +Docs: https://platform.claude.com/docs/en/api/rate-limits — organizations sit on Start / Build / Scale / | |
| 191 | +Custom tiers (auto-assigned by usage history; monthly spend caps of $500 / $1,000 / $200,000). | |
| 192 | + | |
| 193 | +**Message Batches API has its own limits, shared across all models** (separate from Messages ITPM/OTPM): | |
| 194 | + | |
| 195 | +| Tier | API requests/min | Max batch requests in processing queue | Max requests per batch | | |
| 196 | +|---|---|---|---| | |
| 197 | +| Start | 1,000 | 200,000 | 100,000 | | |
| 198 | +| Build | 2,000 | 300,000 | 100,000 | | |
| 199 | +| Scale | 4,000 | 500,000 | 100,000 | | |
| 200 | + | |
| 201 | +Implications for us: 54,000 ratings fit in a single batch at any tier; even a full-index recompute with | |
| 202 | +several prompt versions in flight stays under the Start-tier queue (200k). Batch throughput inside the | |
| 203 | +queue is demand-based, not tier-based — under load, more requests may hit the 24h expiry; the ingester's | |
| 204 | +resubmit path (§2) handles that. | |
| 205 | + | |
| 206 | +For any **synchronous** rating path (e.g. on-demand re-rate of a single task): limits are per-model RPM + | |
| 207 | +ITPM/OTPM (e.g. Start tier, Sonnet 5: 1,000 RPM / 2M ITPM / 400k OTPM). **Cache reads do not count toward | |
| 208 | +ITPM** on current models, so the cached rubric also multiplies effective sync throughput. Opus 5 and | |
| 209 | +Sonnet 5 each have rate-limit buckets separate from the combined Opus 4.x / Sonnet 4.x pools. On 429, honor | |
| 210 | +`retry-after`; the SDK does this automatically (default 2 retries). | |
| 211 | + | |
| 212 | +## 6. TypeScript SDK (`@anthropic-ai/sdk`) patterns | |
| 213 | + | |
| 214 | +The SDK auto-retries 408/409/429/5xx with exponential backoff (`maxRetries` default 2; timeout default | |
| 215 | +10 min, in **milliseconds** on TS). Sketch of the worker pieces (model from `RATER_MODEL`, never hardcoded): | |
| 216 | + | |
| 217 | +```ts | |
| 218 | +import Anthropic from "@anthropic-ai/sdk"; | |
| 219 | +import { createHash } from "node:crypto"; | |
| 220 | + | |
| 221 | +const client = new Anthropic(); // ANTHROPIC_API_KEY from env | |
| 222 | +const MODEL = process.env.RATER_MODEL!; | |
| 223 | +const PROMPT_VERSION = process.env.RATER_PROMPT_VERSION!; | |
| 224 | + | |
| 225 | +export const ratingId = (taskId: string, sample: number) => | |
| 226 | + createHash("sha256").update(`${taskId}:${PROMPT_VERSION}:${sample}`).digest("hex").slice(0, 48); | |
| 227 | +// valid as BullMQ job ID *and* batch custom_id (^[a-zA-Z0-9_-]{1,64}$) | |
| 228 | + | |
| 229 | +// --- batch-submitter job --- | |
| 230 | +export async function submitBatch(rows: PendingRating[]) { | |
| 231 | + const batch = await client.messages.batches.create({ | |
| 232 | + requests: rows.map((r) => ({ | |
| 233 | + custom_id: r.id, // = ratingId(...) | |
| 234 | + params: { | |
| 235 | + model: MODEL, | |
| 236 | + max_tokens: 1024, // headroom over the ~500-token rating | |
| 237 | + system: [{ type: "text" as const, text: RUBRIC, | |
| 238 | + cache_control: { type: "ephemeral" as const, ttl: "1h" as const } }], | |
| 239 | + output_config: { format: { type: "json_schema", schema: RATING_SCHEMA } }, | |
| 240 | + messages: [{ role: "user" as const, content: r.taskPrompt }], | |
| 241 | + }, | |
| 242 | + })), | |
| 243 | + }); | |
| 244 | + await db.markSubmitted(rows.map((r) => r.id), batch.id); // persist batch_id BEFORE returning | |
| 245 | + return batch.id; | |
| 246 | +} | |
| 247 | + | |
| 248 | +// --- batch-poller job (BullMQ delayed/repeatable, jobId: `poll:${batchId}`) --- | |
| 249 | +export async function pollBatch(batchId: string): Promise<boolean> { | |
| 250 | + const batch = await client.messages.batches.retrieve(batchId); | |
| 251 | + return batch.processing_status === "ended"; // else re-schedule in ~60s | |
| 252 | +} | |
| 253 | + | |
| 254 | +// --- batch-ingester job --- | |
| 255 | +export async function ingestResults(batchId: string) { | |
| 256 | + for await (const result of await client.messages.batches.results(batchId)) { | |
| 257 | + switch (result.result.type) { | |
| 258 | + case "succeeded": { | |
| 259 | + const msg = result.result.message; | |
| 260 | + const text = msg.content.find((b) => b.type === "text")?.text ?? ""; | |
| 261 | + await db.storeRating({ | |
| 262 | + customId: result.custom_id, // → task_id + prompt_version + sample | |
| 263 | + model: msg.model, | |
| 264 | + promptVersion: PROMPT_VERSION, | |
| 265 | + rawResponse: JSON.stringify(msg), // full audit trail (CLAUDE.md §6) | |
| 266 | + parsed: JSON.parse(text), // schema-guaranteed {score, rationale, confidence} | |
| 267 | + usage: msg.usage, // incl. cache_read_input_tokens for cost telemetry | |
| 268 | + ratedAt: new Date().toISOString(), | |
| 269 | + }); | |
| 270 | + break; | |
| 271 | + } | |
| 272 | + case "errored": | |
| 273 | + if (result.result.error.type === "invalid_request") await db.deadLetter(result.custom_id, result.result); | |
| 274 | + else await db.markPending(result.custom_id); // server error — next submitter run retries | |
| 275 | + break; | |
| 276 | + case "expired": | |
| 277 | + case "canceled": | |
| 278 | + await db.markPending(result.custom_id); | |
| 279 | + break; | |
| 280 | + } | |
| 281 | + } | |
| 282 | +} | |
| 283 | +``` | |
| 284 | + | |
| 285 | +Notes: results arrive in arbitrary order — key everything by `custom_id`. For sync one-off calls, prefer | |
| 286 | +`client.messages.parse()` with `zodOutputFormat(...)` from `@anthropic-ai/sdk/helpers/zod` (typed | |
| 287 | +`parsed_output`); for batches, validate the JSON against the same Zod schema at ingest time. Handle errors | |
| 288 | +with typed classes (`Anthropic.RateLimitError`, `Anthropic.APIError`), never string-matching. | |
| 289 | + | |
| 290 | +## 7. Cost estimate: 18,000 tasks × 3 samples = 54,000 ratings | |
| 291 | + | |
| 292 | +Assumptions: rubric 2,000 tokens (shared, cacheable), per-task suffix ~300 tokens (statement + occupation | |
| 293 | +context + instruction), completion ~500 tokens. Totals: **input 124.2M tokens** (108M rubric + 16.2M | |
| 294 | +variable), **output 27M tokens**. "With caching" = 1h-TTL cache, optimistic ~100% hit rate (real batch runs | |
| 295 | +will land between the last two columns), cache read = 0.1× input, stacked with the 50% batch discount | |
| 296 | +(0.05× net). Prices per §1. | |
| 297 | + | |
| 298 | +| Model | Sync, no cache | Batch only (50%) | Batch + rubric caching | | |
| 299 | +|---|---:|---:|---:| | |
| 300 | +| Haiku 4.5 ($1/$5) | $259 | **$130** | ≈$130 (2k rubric **below Haiku's 4,096-token cache minimum** — won't cache; pad rubric ≥4k to reach ≈$92) | | |
| 301 | +| Sonnet 5 — intro thru 2026-08-31 ($2/$10) | $518 | $259 | **≈$162** ($10.80 cached rubric + $16.20 variable input + $135 output) | | |
| 302 | +| Sonnet 5 / Sonnet 4.6 — standard ($3/$15) | $778 | $389 | **≈$243** ($16.20 + $24.30 + $202.50) | | |
| 303 | +| Opus 5 ($5/$25) | $1,296 | $648 | **≈$405** ($27 + $40.50 + $337.50) | | |
| 304 | +| Fable 5 ($10/$50) | $2,592 | $1,296 | ≈$810 (not recommended for this workload) | | |
| 305 | + | |
| 306 | +Takeaways: | |
| 307 | + | |
| 308 | +- **Output tokens dominate** once caching is on (83% of the Sonnet cost). Keep rationales tight in the | |
| 309 | + prompt, and control thinking (Sonnet 5/Opus 5 think by default; thinking bills as output — disable it or | |
| 310 | + set `effort: "low"` or the 500-token completion assumption breaks). | |
| 311 | +- The entire volume run costs **$130–$405** depending on model — negligible against the human-expert | |
| 312 | + pipeline. This argues for Sonnet 5 (or even an Opus 5 second-pass on flagged tasks) over Haiku | |
| 313 | + penny-pinching; if the run happens before **2026-09-01**, Sonnet 5's intro pricing makes it ~$162. | |
| 314 | +- A full re-run per `INDEX_VERSION`/`RATER_PROMPT_VERSION` bump is affordable, which supports the | |
| 315 | + methodology-integrity rule that recomputations create new immutable runs. | |
| 316 | + | |
| 317 | +## Where the docs contradict / update CLAUDE.md assumptions | |
| 318 | + | |
| 319 | +1. **Docs URL moved:** CLAUDE.md §6 points to `https://docs.claude.com/en/api/overview`; that 301-redirects | |
| 320 | + to `https://platform.claude.com/docs/en/api/overview`. Update the reference. | |
| 321 | +2. **Temperature-based sampling is gone on current models:** if the "3 samples per task" design assumed | |
| 322 | + `temperature > 0` resampling, note that Sonnet 5 / Opus 5 / Opus 4.7+ **reject** non-default | |
| 323 | + `temperature`/`top_p`/`top_k` with a 400. Variance across samples is natural model stochasticity only | |
| 324 | + (or use Haiku 4.5 / Sonnet 4.6, which still accept temperature). | |
| 325 | +3. **`output_format` param is deprecated** — any prototype code using it should move to | |
| 326 | + `output_config: {format: ...}` (GA, no beta header). | |
| 327 | +4. No contradiction on the audit-trail/idempotency requirements — the Batch API's `custom_id` + | |
| 328 | + result-streaming model fits the deterministic-hash design directly; the only gap is that batch **create** | |
| 329 | + has no server-side idempotency key, so the submitter must persist `batch_id` transactionally (§2.2). | |
added
docs/research/04-landscape-and-evidence.md
+307 −0
@@ -0,0 +1,307 @@ | ||
| 1 | +# 04 — Competitive Landscape & Real-World Evidence (through August 2026) | |
| 2 | + | |
| 3 | +Research memo for the AI Risk Index (airiskindex.io). Compiled 2026-08-05 from live web research. | |
| 4 | +Purpose: (a) map existing public AI-job-risk tools, (b) assemble the 2024–2026 empirical record on AI's | |
| 5 | +labor-market effects, and (c) propose how this evidence parameterizes the `barriers` and | |
| 6 | +`adoption_velocity` scoring dimensions (weights 0.20 / 0.10 in v1, `packages/scoring/src/weights.ts`). | |
| 7 | + | |
| 8 | +--- | |
| 9 | + | |
| 10 | +## Part A — Competitive landscape of public AI-job-risk tools | |
| 11 | + | |
| 12 | +### A.1 willrobotstakemyjob.com (the incumbent) | |
| 13 | + | |
| 14 | +- **Since 2017.** Tagline: "Find out how likely your job is to be automated — based on real data and user votes." | |
| 15 | +- **Methodology:** Automation-risk probabilities produced "using a similar method" to **Frey & Osborne (2013)** | |
| 16 | + ("The Future of Employment", Gaussian process classifier over 702 occupations, the famous "47% of US | |
| 17 | + employment at risk" paper), re-estimated "with the most up-to-date data available", plus BLS occupation | |
| 18 | + data (employment, wages, growth). Since 2019 it collects **user poll votes** on perceived risk; in 2021 it | |
| 19 | + blended BLS data + polls + automation probability into a 0–10 "job score". | |
| 20 | + Source: https://willrobotstakemyjob.com/about | |
| 21 | +- **Critical weakness:** the underlying model is pre-LLM computerization/robotics-era work. Frey–Osborne | |
| 22 | + scored *whole occupations* on physical/routine automatability (manual dexterity, cramped workspace, | |
| 23 | + fine arts, social perceptiveness...), which inverts under generative AI: it rates cognitive/office work as | |
| 24 | + relatively safe and misses exactly the exposure the 2024–2026 evidence shows (translators, writers, | |
| 25 | + customer service, junior developers). Single doom-number framing ("X% probability of automation"), | |
| 26 | + no exposure/substitution/augmentation distinction, no confidence intervals, no versioning, no API. | |
| 27 | +- **Traffic (why it still matters):** ~71.3K visits in May 2026 per Semrush (down ~20% MoM), world rank | |
| 28 | + ~#50,395; ~42% of traffic from Google organic, ~39% direct. Other estimators give 45K–100K+/month. | |
| 29 | + Sources: https://www.semrush.com/website/willrobotstakemyjob.com/overview/ , https://hypestat.com/info/willrobotstakemyjob.com | |
| 30 | +- **Monetization:** ads; no API or paid tier found. | |
| 31 | + | |
| 32 | +### A.2 Other public lookup tools (US/EN) | |
| 33 | + | |
| 34 | +| Tool | Data / method | Output | Weaknesses | Pricing/API | | |
| 35 | +|---|---|---|---|---| | |
| 36 | +| **willrobotstakemyjob.com** (2017) | Frey–Osborne-style re-estimation + BLS + user polls | Single automation % + 0–10 "job score" | Pre-LLM model, whole-occupation doom number, no sub-scores, no versioning | Free, ads; no API | | |
| 37 | +| **replacedbyrobot.info** ("2026 AI Automation Risk Database") | Claims BLS + O\*NET over "57,000+ occupations" (job titles, not SOC codes) | "2 risk scores per job" (AI + robotics) | Opaque method, ad-heavy, title-level pseudo-precision | Free, AdSense; no API | | |
| 38 | +| **aijobimpactcalculator.com** (2026, by Digital Signet) | ILO 2025 GenAI exposure gradient (4 bands) + Brookings 2024 task rubric on O\*NET 30.2 tasks + BLS EP 2024-34 + WEF FoJ 2025; static, pre-computed; published methodology & revision history | Exposure band + top-5 tasks tagged Displaceable/Changing/Growing + "what's growing" panel | Band-level only (4 bands), no composite score, no CI, US-centric | Free; no API | | |
| 39 | +| **replacemeter.com** | Undisclosed (looks LLM-generated per submitted job title) | Letter grades: "AI Resilience" %, "Adaptability" % | No methodology page, arbitrary-title scoring, no provenance | Free | | |
| 40 | +| **tripleten.com/tools/what-jobs-will-ai-replace** | LLM analysis of user-entered title/industry; "most recent AI research data" (uncited) | 0–100% automation risk + skills advice + career alternatives | Lead-gen for a bootcamp; unreproducible | Free (lead-gen) | | |
| 41 | +| **ailayoffs.live** | Aggregates layoffs.fyi, Goldman, McKinsey, WEF; "Oxford research + real layoff data" risk checker | Live displacement counters + risk score | Doom-counter framing, mixes projections with counts | Free | | |
| 42 | +| **ailayofftracker.com** / **founderreports.com/ai-layoffs-tracker** / **skillsyncer.com/layoffs-tracker** | Curated AI-cited layoff announcements (Challenger, TechCrunch sourcing) | Event lists, totals | Event trackers, not occupation scores | Free | | |
| 43 | +| **techjacksolutions.com/job-displacement-trends** | Mash-up: Anthropic Economic Index %, Gartner, BLS growth, "WifiTalents" | Per-occupation risk ranges (e.g., customer service 67–80%) | Mixes usage shares with risk %, low-quality sources alongside good ones | Free | | |
| 44 | +| **Stanford Canaries Dashboard** (digitaleconomy.stanford.edu) | ADP payroll microdata, 4.6M workers, 730+ occupations, continuously updated | Employment trends by age × AI exposure | Research dashboard, not per-occupation risk lookup — but the credibility benchmark | Free | | |
| 45 | + | |
| 46 | +Academic/institutional exposure indices that power many of these (no consumer UI of their own): | |
| 47 | +**Felten–Raj–Seamans AIOE** (AI application ↔ 52 O\*NET abilities; https://sites.bu.edu/tpri/2021/06/02/occupational-industry-and-geographic-exposure-to-artificial-intelligence-a-novel-dataset-and-its-potential-uses), | |
| 48 | +**ILO Global Index of Occupational Exposure to GenAI** (ISCO-08, 4 gradient bands; https://webapps.ilo.org/static/english/intserv/working-papers/wp140/index.html), | |
| 49 | +**Microsoft "AI applicability score"** (Tomlinson et al. 2025, 200K Copilot conversations mapped to O\*NET work | |
| 50 | +activities — top: interpreters/translators (98% activity overlap), historians, passenger attendants, sales reps, | |
| 51 | +writers, customer service reps; https://www.microsoft.com/en-us/research/publication/working-with-ai-measuring-the-occupational-implications-of-generative-ai), | |
| 52 | +**OpenAI "GPTs are GPTs"** (Eloundou et al., Science 2024), **Pew 2023 O\*NET work-activity classification** | |
| 53 | +(https://www.pewresearch.org/social-trends/2023/07/26/2023-ai-and-jobs-methodology-for-onet-analysis). | |
| 54 | + | |
| 55 | +### A.3 French / EU equivalents | |
| 56 | + | |
| 57 | +- **jobimpact.aidoption.fr** — "Exposition IA du marché de l'emploi français": 532 ROME occupations, | |
| 58 | + treemap (surface = jobs, color = 0–10 exposure), France Travail ROME + DARES data, **scored by Claude** | |
| 59 | + (LLM-as-rater — directly comparable to our §6 pipeline, but with no audit trail or versioning). Has a /us/ twin. | |
| 60 | +- **transitions-ia.fr** ("IA & Métiers France" observatory) — ROME 4.0 + INSEE EEC 2024 + DARES BMO 2024; | |
| 61 | + task-level exposure score per métier. https://otakuch.github.io/transitions-ia.fr/ | |
| 62 | +- **job-guard.com** — French-language "votre métier va-t-il disparaître ?" test; editorial/affiliate quality. | |
| 63 | +- **Observatoire des Emplois Menacés et Émergents + Coface study** (Nov 2025, covered by Les Échos): ~16% of | |
| 64 | + French jobs at risk; white-collar metropolitan jobs most exposed (Paris ~19%, Lyon/Toulouse 18%); | |
| 65 | + legal/accounting, publishing/press, IT programming/consulting, insurance, finance >25% of jobs exposed. | |
| 66 | + https://www.lesechos.fr/monde/europe/ia-le-grand-bouleversement-a-venir-du-marche-du-travail-2221760 | |
| 67 | +- No credible official FR/EU consumer lookup exists (France Travail offers only e-learning content) → the | |
| 68 | + ESCO/ROME crosswalk in our roadmap targets an **empty niche**. | |
| 69 | + | |
| 70 | +### A.4 SEO landscape | |
| 71 | + | |
| 72 | +- Queries like "will AI take my job (2026)" are dominated not by tools but by **listicle/content marketing**: | |
| 73 | + Nucamp, NovoResume, Careerminds, AI Weekly, Shawn Kanungo — all citing the same WEF 92M-displaced / | |
| 74 | + 170M-created / +78M-net figure, plus Goldman's −16K net jobs/month (Apr 2026). Tool sites rank on | |
| 75 | + "AI job risk calculator" / "will robots take my job" variants; willrobotstakemyjob.com still owns its | |
| 76 | + brand query with ~42% organic share of its traffic. | |
| 77 | +- Opportunity: nothing ranking today combines (1) task-level methodology, (2) sub-scores with uncertainty, | |
| 78 | + (3) live evidence (Challenger/adoption data), (4) versioned transparency, (5) non-doom adaptation framing. | |
| 79 | + aijobimpactcalculator.com is the closest philosophical competitor (source-cited, anti-doom, "what's growing" | |
| 80 | + panel, "how to argue with this" page) but is static, band-level, and has no composite index, no API, no EU coverage. | |
| 81 | + | |
| 82 | +--- | |
| 83 | + | |
| 84 | +## Part B — Real-world evidence, 2024 → August 2026 | |
| 85 | + | |
| 86 | +### B.1 Entry-level employment effects ("Canaries" line of evidence) | |
| 87 | + | |
| 88 | +- **Brynjolfsson, Chandar & Chen (Stanford/ADP), "Canaries in the Coal Mine?"** (Aug 2025, rev. Nov 13 2025): | |
| 89 | + since gen-AI diffusion, workers **aged 22–25 in the most AI-exposed occupations saw a ~16% relative | |
| 90 | + employment decline** (software devs 22–25 down ~20% from late-2022 peak), controlling for firm-level shocks; | |
| 91 | + older workers in the same occupations stable/growing. Adjustment via **employment, not wages**. Declines | |
| 92 | + **concentrated where AI automates rather than augments** (per Anthropic Economic Index task classification). | |
| 93 | + https://digitaleconomy.stanford.edu/publication/canaries-in-the-coal-mine-six-facts-about-the-recent-employment-effects-of-artificial-intelligence/ | |
| 94 | +- **Follow-up note (Feb 9, 2026), "Canaries, Interest Rates, and Timing":** interest rates don't explain the | |
| 95 | + *differential* entry-level decline in AI-exposed occupations. | |
| 96 | + https://digitaleconomy.stanford.edu/news/canaries-interest-rates-and-timinga-more-on-recent-drivers-of-employment-changes-for-young-workers | |
| 97 | +- **Canaries Dashboard** (2026): continuous monitoring, 4.6M workers, 730+ occupations; Brynjolfsson June 2026: | |
| 98 | + "Whatever it is, it's not going away." | |
| 99 | + https://digitaleconomy.stanford.edu/project/indicators/canaries-dashboard/ , | |
| 100 | + https://fortune.com/2026/06/27/what-is-ai-impact-entry-level-jobs-stanford-adp-canaries-brynjolfsson-richardson/ | |
| 101 | +- **UK corroboration:** Adzuna — UK entry-level vacancies **−32% since ChatGPT launch (Nov 2022 → Jun 2025)**; | |
| 102 | + entry-level share of market 28.9% → 25%. Graduate vacancies **−42.1% YoY in May 2026** (worse than any | |
| 103 | + pandemic month). Indeed (Jun 2025): toughest graduate market since 2018, grad roles −33% YoY. | |
| 104 | + https://www.theguardian.com/business/2025/jun/30/uk-entry-level-jobs-chatgpt-launch-adzuna , | |
| 105 | + https://www.adzuna.co.uk/job-market-report | |
| 106 | +- **Grad unemployment by major (NY Fed data, 2025–26):** recent CS grads **6.1%** unemployment, | |
| 107 | + computer engineering **7.5%** — above the all-grad 4.8% average and above history/philosophy majors; | |
| 108 | + entry-level SWE postings ~−30% YoY (Handshake 2025); CS enrollment fell >10% in 2025–26. | |
| 109 | + https://interviewchamp.ai/learn/why-cs-new-grad-unemployment-hit-6-percent-2025 , | |
| 110 | + https://www.finalroundai.com/blog/computer-science-graduates-face-worst-job-market-in-decades | |
| 111 | +- **Caveats to keep the index honest:** LinkedIn Economic Graph (Apr 2026) notes hiring −20% since 2022 but | |
| 112 | + says it has *not* seen AI as the demonstrable cause (rates, post-2022 normalization overlap); Brookings 2025 | |
| 113 | + ("No AI Jobs Apocalypse, For Now") finds aggregate data doesn't yet show mass displacement. | |
| 114 | + | |
| 115 | +### B.2 Layoffs attributed to AI (Challenger, Gray & Christmas) | |
| 116 | + | |
| 117 | +- 2023: Challenger begins tracking "AI" as a stated layoff reason. **2025 full year: 54,836** AI-attributed cuts | |
| 118 | + (~5% of layoffs). **2026 is the discontinuity:** | |
| 119 | + - Jan 2026: AI = 7% of cuts → Mar: 25% (AI becomes **#1 cited reason for the first time**) → Apr: 21,490 cuts, | |
| 120 | + 26% → **May: 38,579 cuts, 40% of all cuts — highest monthly total ever recorded** → Jun: still #1. | |
| 121 | + - **H1 2026: 101,743 AI-cited cuts (~23% of all cuts), nearly 2× all of 2025.** AI #1 reason 4 consecutive | |
| 122 | + months (Mar–Jun). Tech sector: 139,156 H1 cuts, +83% YoY, ~31% of all layoffs. | |
| 123 | + - Sources: https://www.challengergray.com/blog/challenger-report-may-job-cuts-rise-16-from-april-highest-may-total-since-2020 , | |
| 124 | + https://www.challengergray.com/blog/challenger-report-april-job-cuts-rise-38-from-march-ytd-cuts-down-50 , | |
| 125 | + https://www.techtimes.com/articles/319588/20260703/ai-leads-us-job-cuts-record-4th-month-tech-claims-31-h1-layoffs.htm , | |
| 126 | + https://www.businessinsider.com/challenger-ai-layoffs-economy-jobs-2026-6 (Challenger itself: "not a jobpocalypse") | |
| 127 | +- **Goldman Sachs (Apr 2026):** AI eliminating ~25,000 US jobs/month, creating ~9,000 → **net −16,000/month**. | |
| 128 | +- **Named events:** Amazon **30,000 corporate cuts** (14K Oct 2025 + 16K Jan 2026, largest in its history; AI/ | |
| 129 | + automation cited as an efficiency driver; internal docs reportedly project 600K roles automated by 2033) | |
| 130 | + https://www.geekwire.com/2025/amazon-reportedly-set-to-lay-off-30000-corporate-employees-in-massive-workforce-cut/ ; | |
| 131 | + Salesforce cut **~4,000 customer-support roles** as AI agents absorbed workload (Benioff: "I need less heads", | |
| 132 | + Sep 2025) https://fortune.com/2025/09/02/salesforce-ceo-billionaire-marc-benioff-ai-agents-jobs-layoffs-customer-service-sales/ ; | |
| 133 | + HP 4,000–6,000 (Nov 2025, AI-cited). | |
| 134 | +- **Counter-signal (reversal risk):** Klarna replaced ~700 support agents with AI (chatbot = work of 700), then | |
| 135 | + **rehired humans through 2025–26** after CSAT dropped on complex/emotional cases → hybrid model. Quality, | |
| 136 | + not cost, was the binding constraint. https://www.digitalapplied.com/blog/klarna-reverses-ai-layoffs-replacing-700-workers-backfired | |
| 137 | + | |
| 138 | +### B.3 Sector-specific substitution evidence | |
| 139 | + | |
| 140 | +- **Translation — the most-displaced occupation to date.** Microsoft applicability rank #1 (98% activity | |
| 141 | + overlap). Society of Authors survey (2024): **36% of translators lost work to GenAI; 43% report income | |
| 142 | + declines; 77% expect negative future income**. Individual accounts (Blood in the Machine, mid-2025): 15-yr | |
| 143 | + technical translator down from six figures to €8K/yr; Quebec FR-EN translator −60% income in 2024. | |
| 144 | + https://www.theguardian.com/books/2024/apr/16/survey-finds-generative-ai-proving-major-threat-to-the-work-of-translators , | |
| 145 | + https://www.bloodinthemachine.com/p/ai-killed-my-job-translators | |
| 146 | +- **Customer support:** Salesforce −4,000; Klarna cycle (above); fintech cuts in May 2026 mostly AI-cited | |
| 147 | + (Challenger). High exposure AND high realized substitution — but Klarna shows a quality floor. | |
| 148 | +- **Software engineering:** bifurcated. Junior/entry roles: −20% employment (ages 22–25, Canaries), entry | |
| 149 | + postings −30% (Handshake), CS grad unemployment 6.1%; yet BLS still projects **+15% developer growth | |
| 150 | + 2024–34 (~129,200 openings/yr)** and senior demand holds. BCG (2026) classifies SWE as "amplified/divergent" | |
| 151 | + rather than substituted. https://www.bcg.com/publications/2026/ai-will-reshape-more-jobs-than-it-replaces | |
| 152 | + | |
| 153 | +### B.4 Adoption statistics (the `adoption_velocity` evidence base) | |
| 154 | + | |
| 155 | +- **Census BTOS (official, firm-weighted):** AI use in *core production* 3.8% (Sep 2023) → 4.6% (early 2024) → | |
| 156 | + ~10% (Sep–late 2025, doubling in ~18 months). Question broadened Nov 2025 to "any business function": | |
| 157 | + **17–20% of firms Dec 2025–May 2026; 20–23% expect use within 6 months.** | |
| 158 | + https://www.census.gov/library/stories/2026/05/ai-use-businesses.html | |
| 159 | +- **BTOS AI Supplement (Nov 2025–Jan 2026):** **18% of firms firm-weighted = 32% employment-weighted**; | |
| 160 | + very large firms in **Information / Professional Services / Finance: 50–60% (60–70% employment-weighted)**; | |
| 161 | + 57% of adopters use AI in ≤3 business functions (top: sales & marketing 52%, strategy/biz-dev 45%, IT 41%); | |
| 162 | + workers use AI in tasks at 23% of firms (41% employment-weighted). | |
| 163 | + https://www.census.gov/library/working-papers/2026/adrm/CES-WP-26-25.html | |
| 164 | +- **Fed monitoring note (Apr 2026)** reconciles the scales: BTOS firms 18%; **employment-weighted SBU 78%**; | |
| 165 | + **41% of the labor force uses GenAI for work** (RPS, Nov 2025; +9.7pp YoY); 50% uses it outside work. | |
| 166 | + https://www.federalreserve.gov/econres/notes/feds-notes/monitoring-ai-adoption-in-the-u-s-economy-20260403.html | |
| 167 | +- **Ramp AI Index (paid adoption, 70K+ firms' card/bill spend):** businesses **paying** for AI crossed **50.4% | |
| 168 | + in March 2026** (35% a year earlier; 46.8% Jan 2026); jump driven by "late majority" manufacturing/retail; | |
| 169 | + VC-backed startups ~80%. Vendor race: OpenAI 35.2% vs Anthropic 30.6% of businesses (Apr 2026). | |
| 170 | + https://ramp.com/data/ai-index , https://ramp.com/data/april-2026-ai-index | |
| 171 | +- **McKinsey State of AI (Nov 2025):** **88% of orgs use AI in ≥1 function**, but ~two-thirds still piloting; | |
| 172 | + **62% experimenting with agents, 23% scaling agents in ≥1 function, ≤10% scaling within a single function**; | |
| 173 | + only **39% report any enterprise EBIT impact; ~6% are "high performers"** (>5% EBIT from AI). | |
| 174 | + https://www.mckinsey.com/capabilities/quantumblack/our-insights/the-state-of-ai | |
| 175 | +- **Agentic reality check:** MIT NANDA: 95% of GenAI pilots show no P&L impact; Gartner: >40% of agentic | |
| 176 | + projects to be cancelled by end-2027; S&P Global/McKinsey: 31% of enterprises run ≥1 agent in production — | |
| 177 | + **banking/insurance 47% vs healthcare 18% and government 14%**. Gartner: 80% of enterprise apps shipped | |
| 178 | + in Q1 2026 embed an agent (vs 33% in 2024). | |
| 179 | +- **France anchor:** 35% of French firms >10 employees use AI (OPIEC 2025) — EU lags US enterprise adoption. | |
| 180 | + | |
| 181 | +### B.5 Wage effects & augmentation-vs-substitution | |
| 182 | + | |
| 183 | +- **Humlum & Vestergaard (NBER w33777; Denmark, 25,000 workers × 7,000 workplaces, 11 exposed occupations, | |
| 184 | + admin-linked):** AI chatbots → **precisely-estimated null on earnings and hours** (CIs rule out >1–2% average | |
| 185 | + effects; occupation-level >6%); avg time savings only **2.8–3%**; >80% of saved time reallocated to other | |
| 186 | + work; **8.4% of workers gained NEW tasks created by AI** (e.g., checking AI output); minimal pass-through | |
| 187 | + of gains to wages. https://www.nber.org/papers/w33777 , https://www.andershumlum.com/s/chatbots_july25.pdf | |
| 188 | + → Through 2024, *within-worker* wage effects ≈ 0; displacement shows up at hiring margins first (B.1). | |
| 189 | +- **PwC Global AI Jobs Barometer 2026 (1B+ job ads, 27 countries):** **62% average wage premium for jobs | |
| 190 | + requiring AI skills** (118% in consumer markets, 16% in government); AI-skill jobs growing 69% vs 9% market; | |
| 191 | + firms most able to use AI: headcount growth 53% vs 36% and wage growth 24% vs 17% vs least-exposed — | |
| 192 | + "two distinct labour-market paths". https://www.pwc.com/gx/en/news-room/press-releases/2026/pwc-2026-ai-jobs-barometer.html | |
| 193 | +- **Anthropic Economic Index (Claude usage, task-mapped to O\*NET):** initial report ~**57% augmentation / | |
| 194 | + 43% automation**; by Nov 2025, Claude.ai = **52% augmentation / 45% automation** (automation briefly led | |
| 195 | + mid-2025); **enterprise API traffic is automation-dominant** (back-office workflows: email, document | |
| 196 | + processing, CRM, scheduling). Directive/full-delegation usage rising. Reports: | |
| 197 | + https://www.anthropic.com/research/anthropic-economic-index-january-2026-report , | |
| 198 | + https://www.anthropic.com/research/economic-index-march-2026-report , | |
| 199 | + https://www.anthropic.com/research/economic-index-june-2026-report | |
| 200 | + → Validates keeping automation-vs-augmentation as *the* substitution discriminator (Canaries fact: | |
| 201 | + employment declines concentrate in automation-dominant occupations). | |
| 202 | +- **BCG (Jan 2026, Revelio 1,500 roles):** point estimate **10–15% of US jobs vulnerable over 4–5 years**; | |
| 203 | + distinguishes substituted vs "divergent" (demand-expansion offsets 0.5–1.0) vs amplified roles. | |
| 204 | + | |
| 205 | +### B.6 Regulation & barriers (the `barriers` evidence base) | |
| 206 | + | |
| 207 | +- **EU AI Act — timeline moved under our feet:** GPAI obligations applied Aug 2, 2025. The **Annex III | |
| 208 | + high-risk obligations (incl. ALL employment/HR AI: CV screening, targeted job ads, promotion/termination | |
| 209 | + decisions, worker monitoring — Annex III pt. 4) were due Aug 2, 2026 but the "Digital Omnibus" political | |
| 210 | + agreement postpones them to Dec 2, 2027.** Requirements when live: risk management, bias testing, logging, | |
| 211 | + human oversight, conformity assessment. Net effect: EU employment-AI adoption friction persists but the | |
| 212 | + binding date slipped ~16 months (a *barriers-lowering* event for 2026–27 velocity in the EU). | |
| 213 | + https://ogletree.com/insights-resources/blog-posts/eu-nears-approval-of-agreement-to-delay-rules-for-ai-use-in-employment-decisions/ , | |
| 214 | + https://accessfinancial.com/eu-ai-act-recruitment-high-risk-hiring-2026/ | |
| 215 | +- **US states:** Illinois **HB 3773** effective **Jan 1, 2026** (AI discrimination in employment = civil-rights | |
| 216 | + violation; notice required; zip-code-proxy ban). Colorado's AI Act (SB 24-205) delayed to Jun 30, 2026, then | |
| 217 | + **replaced by narrower SB 26-189 (May 2026)**. NYC Local Law 144 (bias audits for hiring tools) ongoing. | |
| 218 | + Pattern: US regulates *AI deciding about workers*, not AI *replacing* workers. | |
| 219 | + https://ogletree.com/insights-resources/blog-posts/illinois-steps-up-ai-regulation-in-employment-key-takeaways-for-employers/ | |
| 220 | +- **Professional licensing & liability as adoption brakes (occupation-level, measurable):** | |
| 221 | + - Legal: AI can't be licensed/disbarred/sworn; legal-specific AI tools show **17–34% error rates**; **700+ | |
| 222 | + court cases worldwide involve AI hallucinations** with sanctions → yet legal-professional GenAI adoption | |
| 223 | + still jumped **31% → 69% between 2025 and 2026** (54% of firms give no training, 43% no policy). | |
| 224 | + https://www.americanbar.org/groups/law_practice/resources/law-technology-today/2026/whats-really-holding-law-firms-back-from-embracing-ai/ | |
| 225 | + - Healthcare: litigious + risk-averse, FDA draft guidance (Jan 2025) on AI credibility in drug decisions; | |
| 226 | + physician AI utilization nonetheless 38% (2023) → 72% (2026), concentrated in *administrative* tasks — | |
| 227 | + augmentation inside a licensing moat. | |
| 228 | + - Customer-facing quality floors: Klarna reversal (B.2) = empirical "human-contact requirement" barrier. | |
| 229 | +- **Barrier taxonomy the evidence supports:** (1) statutory/licensing monopoly on the task; (2) liability & | |
| 230 | + error-cost asymmetry (hallucination sanctions, malpractice); (3) regulated-process requirements (EU AI Act | |
| 231 | + Annex III, state notice/audit laws); (4) human-contact/quality preference (Klarna); (5) organizational | |
| 232 | + friction (MIT 95% pilot failure; McKinsey: only 6% high performers; workflow redesign is the differentiator). | |
| 233 | + | |
| 234 | +### B.7 Sector adoption-velocity differentials (who's fast, who's slow, why) | |
| 235 | + | |
| 236 | +| Sector | Signal (2025–26) | Why | | |
| 237 | +|---|---|---| | |
| 238 | +| Information / Tech | BTOS large-firm use 50–60%; >90% of tech companies use AI in ≥1 function | Digital-native tasks, no licensing, in-house skills | | |
| 239 | +| Finance & insurance | BTOS top-3 sector; agents in production: banking/insurance 47% (S&P); NVIDIA State of AI: strongest ROI | Structured data, measurable use cases; regulation shapes but doesn't block | | |
| 240 | +| Professional services (legal, consulting, accounting) | Legal GenAI 31%→69% in one year | High exposure; liability slows *delegation*, not *use* | | |
| 241 | +| Telecom / Retail & CPG | Agentic adoption 48% / 47% (NVIDIA) | Customer-ops scale economics | | |
| 242 | +| Manufacturing / logistics | Fastest %-growth in AI job postings; Ramp "late majority" surge drove the 50% crossing (Mar 2026) | Started low; predictive maintenance, quality control | | |
| 243 | +| Healthcare | Physician use 38%→72% but agents-in-production only 18%; admin-first | Licensing, liability, FDA; augmentation-dominant | | |
| 244 | +| Government / public sector | Agents 14%; lowest AI wage premium (16%) | Procurement, accountability, unionization | | |
| 245 | +| Construction / trades | ~1.4% adoption (laggard anecdote); BTOS small-firm use <20% | Physical, unstructured, small-firm dominated | | |
| 246 | + | |
| 247 | +Cross-cutting velocity facts: firm size is the strongest adoption predictor (BTOS: <20% for ≤4-employee firms); | |
| 248 | +employment-weighted adoption ≈ 2× firm-weighted; paid adoption (Ramp 50.4%) runs far ahead of production-grade | |
| 249 | +deployment (31% ≥1 agent in production) which runs ahead of measured P&L impact (39% any EBIT effect). | |
| 250 | + | |
| 251 | +--- | |
| 252 | + | |
| 253 | +## Inputs for `barriers` & `adoption_velocity` scoring | |
| 254 | + | |
| 255 | +### adoption_velocity (weight 0.10) — proposed parameterization | |
| 256 | + | |
| 257 | +Score each occupation's dominant sector(s) (via BLS OES industry-occupation matrix) on observable adoption, | |
| 258 | +not vendor hype. Candidate sub-indicators, each normalizable to 0–1 with a public, refreshable source: | |
| 259 | + | |
| 260 | +1. **Sector AI-use rate, employment-weighted** — BTOS bi-weekly sector series + AI Supplement | |
| 261 | + (CES-WP-26-25). Anchors: Information ≈ 0.9 · construction ≈ 0.1. | |
| 262 | +2. **Sector adoption momentum** — 12-month delta in BTOS use rate and/or Ramp AI Index sector series | |
| 263 | + (captures manufacturing/retail late-majority acceleration, Mar 2026). | |
| 264 | +3. **Agentic deployment depth** — % of sector firms with agents in production (S&P Global/McKinsey: | |
| 265 | + banking 47% … government 14%). Agents, not chatbots, are the substitution-relevant margin. | |
| 266 | +4. **Realized displacement intensity** — Challenger AI-cited cuts by industry, trailing 12m, scaled by sector | |
| 267 | + employment (chemicals, fintech, tech score high in 2026); optionally corroborated by Canaries Dashboard | |
| 268 | + 22–25 employment trend for the occupation itself. | |
| 269 | +5. **Occupation-level usage intensity** — Anthropic Economic Index share of usage mapped to the occupation's | |
| 270 | + O\*NET tasks, split augmentation vs automation (automation share feeds substitution, not just velocity). | |
| 271 | + | |
| 272 | +Suggested v1 formula: `adoption_velocity = 0.35·(1) + 0.20·(2) + 0.20·(3) + 0.15·(4) + 0.10·(5)`, with the | |
| 273 | +caveat documented in METHODOLOGY.md that (1)–(3) are sector-level priors and (4)–(5) are occupation-level | |
| 274 | +correctors. Update cadence: quarterly (BTOS bi-weekly, Ramp monthly, Challenger monthly, AEI ~quarterly) — | |
| 275 | +each refresh = new `INDEX_VERSION` patch run. | |
| 276 | + | |
| 277 | +### barriers (weight 0.20) — proposed parameterization | |
| 278 | + | |
| 279 | +Score as *adoption friction* (high barriers ⇒ lower net risk), with five components: | |
| 280 | + | |
| 281 | +1. **Licensing/authorization requirement (0–1):** does task sign-off legally require a licensed human | |
| 282 | + (law, medicine, engineering PE, aviation, finance advice)? Source: O\*NET Job Zone + state licensing DBs. | |
| 283 | + Evidence: legal/medical augment-don't-substitute pattern (B.6). | |
| 284 | +2. **Liability & error-cost asymmetry (0–1):** cost of a wrong AI output (malpractice, sanctions, safety). | |
| 285 | + Proxy: occupation's litigation exposure + documented AI-error sanction record (700+ hallucination cases). | |
| 286 | +3. **Regulatory-process coverage (0–1):** is the occupation's automation itself regulated? EU AI Act Annex III | |
| 287 | + (now Dec 2027), Illinois HB 3773, Colorado SB 26-189, NYC LL144 — maintain a dated rule table per | |
| 288 | + jurisdiction; **this component is jurisdiction-specific** (US vs EU scores diverge; the Omnibus delay is a | |
| 289 | + worked example of a barrier score *dropping* between index versions). | |
| 290 | +4. **Human-contact requirement (0–1):** reuse O\*NET work-context variables ("contact with others", | |
| 291 | + "deal with external customers", physical proximity). Empirical anchor: Klarna reversal; CSAT floors. | |
| 292 | +5. **Organizational/implementation friction (0–1):** sector pilot-failure and scaling rates (MIT 95%, | |
| 293 | + McKinsey 6% high performers, Gartner >40% agent-project cancellations) — a global dampener that decays | |
| 294 | + over index versions as deployment matures. | |
| 295 | + | |
| 296 | +Suggested v1 formula: `barriers = 0.30·licensing + 0.25·liability + 0.20·regulatory + 0.15·human_contact + 0.10·org_friction`. | |
| 297 | + | |
| 298 | +### Positioning implications (for public copy, "adaptation not doom") | |
| 299 | + | |
| 300 | +- The 2026 record supports **differentiated, hedged claims**: entry-level exposure is real and measured | |
| 301 | + (−16% relative, Stanford/ADP; Challenger 101,743 H1-2026 AI-cited cuts) while incumbent wage/hours effects | |
| 302 | + are so far null (Denmark) and AI-skill premia are large (+62%, PwC). That *is* our three-concept split — | |
| 303 | + exposure ≠ substitution ≠ augmentation — now empirically vindicated; no competitor surfaces it. | |
| 304 | +- Publish a "what would change this score" section per occupation (aijobimpactcalculator.com's | |
| 305 | + "how to argue with this" page is the only competitor doing epistemic honesty — match and exceed it). | |
| 306 | +- Every headline stat above has a reversal or caveat attached (Klarna, LinkedIn attribution caution, | |
| 307 | + Brookings "no apocalypse yet") — cite these in-product to keep the tone guide credible. | |
added
docs/research/README.md
+41 −0
@@ -0,0 +1,41 @@ | ||
| 1 | +# Research Corpus — AI Risk Index (compiled 2026-08-05) | |
| 2 | + | |
| 3 | +Comprehensive web research to ground the v1 methodology, ETL pipeline, LLM rater, and product positioning. Each document carries full source URLs; claims are dated. | |
| 4 | + | |
| 5 | +| Doc | Contents | | |
| 6 | +|---|---| | |
| 7 | +| [01-existing-indices.md](01-existing-indices.md) | 20+ AI job-exposure indices & studies (Frey–Osborne → Eloundou → ILO/IMF/OECD → Anthropic Economic Index → 2025–26 frontier), comparison table, validation literature, implications for our 5 dimensions | | |
| 8 | +| [02-data-sources.md](02-data-sources.md) | 20 raw data sources with verified URLs, versions, licenses, cadences: O*NET, ESCO/ROME, OEWS wages, employment projections, AI-adoption surveys, benchmark exposure datasets; ingestion & manifest plan | | |
| 9 | +| [03-llm-rater-api.md](03-llm-rater-api.md) | Current Anthropic model lineup/pricing, Message Batches API, structured outputs (GA), prompt caching, BullMQ integration pattern, cost estimates for the full rating job | | |
| 10 | +| [04-landscape-and-evidence.md](04-landscape-and-evidence.md) | Competitor analysis (US + FR/EU), measured 2024–26 labor-market effects, adoption stats, regulatory barriers; proposed parameterization of `adoption_velocity` and `barriers` | | |
| 11 | + | |
| 12 | +## Headline takeaways | |
| 13 | + | |
| 14 | +**Methodology (01)** | |
| 15 | +- Single-number occupation scores have a poor empirical record: individual indices explain <11% of realized unemployment risk; ensembles reach 30–75% (Frank et al., PNAS Nexus 2025). Our sub-score + CI design is the right call. | |
| 16 | +- Yin et al. (2026) found a **19× spread** in Eloundou-style exposure headlines depending on which frontier LLM rates the tasks (2.7%–51.5%). ⇒ Rate with multiple models (or multiple samples) and derive `score_low/score_high` from rater disagreement; publish rater identity per run. | |
| 17 | +- Rate **automation vs. augmentation separately per task** (ILO WP140 and the Anthropic Economic Index both discriminate on this; the Stanford "Canaries" employment effects concentrate in automation-exposed occupations). | |
| 18 | +- `cost_ratio` is an under-researched dimension no published index operationalizes well — a differentiation opportunity. | |
| 19 | + | |
| 20 | +**Data (02)** | |
| 21 | +- ⚠️ **O*NET is at 30.3 (May 2026); 31.0 lands late Aug 2026.** CLAUDE.md §1 says 29.x — update it. Breaking schema change in 30.x: *Technology Skills → Software Skills*, Skills split into Essential/Transferable. | |
| 22 | +- ESCO v1.2.1 + official ESCO↔O*NET crosswalk CSV (built on v1.1, revalidate URIs); ROME 4.0 on data.gouv.fr (Licence Ouverte). | |
| 23 | +- Wages: OEWS May 2025 (released 2026-05-15; BLS blocks non-browser user agents — set UA in fetch scripts). | |
| 24 | +- Anthropic Economic Index on Hugging Face (6 releases, CC-BY, keyed to O*NET tasks) is the best empirical calibration source for the rater and for `adoption_velocity`. | |
| 25 | + | |
| 26 | +**Rater pipeline (03)** | |
| 27 | +- Recommended rater: **Sonnet 5** (pilot vs Haiku 4.5 against the 5% human-review sample). Full job (~18k tasks × 3 samples) fits in **one** Message Batch; ~$130–$250 with batch discount + prompt caching. | |
| 28 | +- Structured outputs is GA — use JSON schema with the 5-point score as an `enum`, not tool-forcing. | |
| 29 | +- ⚠️ `temperature` is rejected on Sonnet 5/Opus 5 — the 3-sample variance design must rely on model/prompt diversity instead of sampling temperature. Haiku 4.5 has a 4,096-token cache minimum (a 2k rubric won't cache there). | |
| 30 | +- Docs moved: docs.claude.com → platform.claude.com. | |
| 31 | + | |
| 32 | +**Positioning (04)** | |
| 33 | +- No competitor combines task-level scoring, sub-scores with CIs, versioned methodology, EU/France coverage, and a public API. Incumbent willrobotstakemyjob.com (~71k visits/mo) still runs pre-LLM Frey–Osborne with a single doom number. The French market is nearly empty. | |
| 34 | +- Evidence base for tone guide ("adaptation, not doom"): effects so far are concentrated (entry-level, automation-exposed occupations: −16% relative employment ages 22–25), not economy-wide; augmentation usage still dominates in AEI data. | |
| 35 | +- `adoption_velocity` and `barriers` can each be built from 4–5 refreshable public sources (BTOS, Ramp, Challenger, AEI; licensing/liability/AI-Act coverage) — concrete formulas proposed in doc 04 §final. | |
| 36 | + | |
| 37 | +## Decisions this research forces (flag before implementation) | |
| 38 | + | |
| 39 | +1. Bump CLAUDE.md's O*NET reference 29.x → 30.3/31.0 and plan ETL for the 30.x schema renames. | |
| 40 | +2. Choose the rater-variance mechanism (multi-model panel vs. multi-prompt) given no `temperature` on Sonnet 5. | |
| 41 | +3. Decide whether v1 rates automation and augmentation as two separate task-level LLM ratings (research strongly says yes; affects prompt design, cost ×2, and the composite formula). | |
added
eslint.config.mjs
+25 −0
@@ -0,0 +1,25 @@ | ||
| 1 | +import js from "@eslint/js"; | |
| 2 | +import tseslint from "typescript-eslint"; | |
| 3 | + | |
| 4 | +export default tseslint.config( | |
| 5 | + { | |
| 6 | + ignores: [ | |
| 7 | + "**/node_modules/**", | |
| 8 | + "**/.next/**", | |
| 9 | + "**/dist/**", | |
| 10 | + "**/.turbo/**", | |
| 11 | + "apps/etl/**", | |
| 12 | + "**/next-env.d.ts", | |
| 13 | + ], | |
| 14 | + }, | |
| 15 | + js.configs.recommended, | |
| 16 | + ...tseslint.configs.recommended, | |
| 17 | + { | |
| 18 | + rules: { | |
| 19 | + "@typescript-eslint/no-unused-vars": [ | |
| 20 | + "error", | |
| 21 | + { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }, | |
| 22 | + ], | |
| 23 | + }, | |
| 24 | + }, | |
| 25 | +); | |
added
infra/.env.example
+20 −0
@@ -0,0 +1,20 @@ | ||
| 1 | +# Template only — real values live in /srv/airiskindex/.env (never committed). | |
| 2 | +DATABASE_URL= | |
| 3 | +REDIS_URL= | |
| 4 | +ANTHROPIC_API_KEY= | |
| 5 | +RATER_MODELS= | |
| 6 | +# Optional cross-provider rater panel keys | |
| 7 | +OPENAI_API_KEY= | |
| 8 | +XAI_API_KEY= | |
| 9 | +MISTRAL_API_KEY= | |
| 10 | +GEMINI_API_KEY= | |
| 11 | +DASHSCOPE_API_KEY= | |
| 12 | +DEEPINFRA_API_KEY= | |
| 13 | +CEREBRAS_API_KEY= | |
| 14 | +PERPLEXITY_API_KEY= | |
| 15 | +DEEPSEEK_API_KEY= | |
| 16 | +KIMI_API_KEY= | |
| 17 | +NGROK_AUTHTOKEN= | |
| 18 | +NEXTAUTH_URL= | |
| 19 | +NEXTAUTH_SECRET= | |
| 20 | +PUBLIC_BASE_URL= | |
added
infra/deploy.sh
+19 −0
@@ -0,0 +1,19 @@ | ||
| 1 | +#!/usr/bin/env bash | |
| 2 | +# Deploy to m3u96b (staging/current prod) — CLAUDE.md §7. Run ON the node. | |
| 3 | +set -euo pipefail | |
| 4 | + | |
| 5 | +cd /srv/airiskindex | |
| 6 | + | |
| 7 | +git pull --ff-only origin main | |
| 8 | +pnpm install --frozen-lockfile | |
| 9 | +pnpm build | |
| 10 | +pnpm db:migrate:deploy | |
| 11 | + | |
| 12 | +sudo systemctl restart airiskindex-web airiskindex-worker | |
| 13 | +sudo systemctl status airiskindex-web --no-pager | |
| 14 | + | |
| 15 | +# Localhost health, then the PUBLIC URL — tunnel failures are the most common outage cause. | |
| 16 | +curl -fsS http://127.0.0.1:3000/api/v1/health | |
| 17 | +curl -fsS https://www.airiskindex.io/api/v1/health | |
| 18 | + | |
| 19 | +echo "deploy OK" | |
added
infra/docker-compose.dev.yml
+19 −0
@@ -0,0 +1,19 @@ | ||
| 1 | +services: | |
| 2 | + postgres: | |
| 3 | + image: postgres:16 | |
| 4 | + environment: | |
| 5 | + POSTGRES_USER: airiskindex | |
| 6 | + POSTGRES_PASSWORD: airiskindex | |
| 7 | + POSTGRES_DB: airiskindex | |
| 8 | + ports: | |
| 9 | + - "127.0.0.1:5432:5432" | |
| 10 | + volumes: | |
| 11 | + - pgdata:/var/lib/postgresql/data | |
| 12 | + | |
| 13 | + redis: | |
| 14 | + image: redis:7 | |
| 15 | + ports: | |
| 16 | + - "127.0.0.1:6379:6379" | |
| 17 | + | |
| 18 | +volumes: | |
| 19 | + pgdata: | |
added
infra/docker-compose.prod.yml
+23 −0
@@ -0,0 +1,23 @@ | ||
| 1 | +# Production data services on m3u96b (CLAUDE.md §7). App processes run under | |
| 2 | +# systemd (airiskindex-web/worker), not in Docker. | |
| 3 | +services: | |
| 4 | + postgres: | |
| 5 | + image: postgres:16 | |
| 6 | + restart: always | |
| 7 | + environment: | |
| 8 | + POSTGRES_USER: ${POSTGRES_USER:?} | |
| 9 | + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?} | |
| 10 | + POSTGRES_DB: airiskindex | |
| 11 | + ports: | |
| 12 | + - "127.0.0.1:5432:5432" | |
| 13 | + volumes: | |
| 14 | + - pgdata:/var/lib/postgresql/data | |
| 15 | + | |
| 16 | + redis: | |
| 17 | + image: redis:7 | |
| 18 | + restart: always | |
| 19 | + ports: | |
| 20 | + - "127.0.0.1:6379:6379" | |
| 21 | + | |
| 22 | +volumes: | |
| 23 | + pgdata: | |
added
infra/ngrok.yml
+11 −0
@@ -0,0 +1,11 @@ | ||
| 1 | +# Copied to /etc/ngrok/ngrok.yml on m3u96b (CLAUDE.md §7). | |
| 2 | +# Custom domain must be configured in the ngrok dashboard + DNS CNAME for | |
| 3 | +# www.airiskindex.io. Verify with: dig CNAME www.airiskindex.io | |
| 4 | +version: 3 | |
| 5 | +agent: | |
| 6 | + authtoken: ${NGROK_AUTHTOKEN} | |
| 7 | +endpoints: | |
| 8 | + - name: airiskindex | |
| 9 | + url: https://www.airiskindex.io | |
| 10 | + upstream: | |
| 11 | + url: 3000 | |
added
package.json
+29 −0
@@ -0,0 +1,29 @@ | ||
| 1 | +{ | |
| 2 | + "name": "airiskindex", | |
| 3 | + "private": true, | |
| 4 | + "packageManager": "pnpm@9.6.0", | |
| 5 | + "engines": { | |
| 6 | + "node": ">=20" | |
| 7 | + }, | |
| 8 | + "scripts": { | |
| 9 | + "dev": "turbo dev --filter=@airiskindex/web", | |
| 10 | + "build": "turbo build", | |
| 11 | + "test": "turbo test", | |
| 12 | + "test:e2e": "pnpm --filter @airiskindex/web test:e2e", | |
| 13 | + "lint": "eslint .", | |
| 14 | + "typecheck": "turbo typecheck", | |
| 15 | + "format": "prettier --write .", | |
| 16 | + "db:migrate": "pnpm --filter @airiskindex/db db:migrate", | |
| 17 | + "db:migrate:deploy": "pnpm --filter @airiskindex/db db:migrate:deploy", | |
| 18 | + "db:seed": "pnpm --filter @airiskindex/db db:seed", | |
| 19 | + "score:recompute": "pnpm --filter @airiskindex/worker score:recompute" | |
| 20 | + }, | |
| 21 | + "devDependencies": { | |
| 22 | + "@eslint/js": "^9.8.0", | |
| 23 | + "eslint": "^9.8.0", | |
| 24 | + "prettier": "^3.3.3", | |
| 25 | + "turbo": "^2.0.9", | |
| 26 | + "typescript": "^5.5.4", | |
| 27 | + "typescript-eslint": "^8.0.0" | |
| 28 | + } | |
| 29 | +} | |
added
packages/config/package.json
+6 −0
@@ -0,0 +1,6 @@ | ||
| 1 | +{ | |
| 2 | + "name": "@airiskindex/config", | |
| 3 | + "version": "0.0.0", | |
| 4 | + "private": true, | |
| 5 | + "description": "Shared tsconfig presets for the monorepo" | |
| 6 | +} | |
added
packages/config/tsconfig/base.json
+16 −0
@@ -0,0 +1,16 @@ | ||
| 1 | +{ | |
| 2 | + "$schema": "https://json.schemastore.org/tsconfig", | |
| 3 | + "compilerOptions": { | |
| 4 | + "strict": true, | |
| 5 | + "target": "ES2022", | |
| 6 | + "lib": ["ES2022"], | |
| 7 | + "module": "Preserve", | |
| 8 | + "moduleResolution": "Bundler", | |
| 9 | + "esModuleInterop": true, | |
| 10 | + "skipLibCheck": true, | |
| 11 | + "forceConsistentCasingInFileNames": true, | |
| 12 | + "resolveJsonModule": true, | |
| 13 | + "isolatedModules": true, | |
| 14 | + "noEmit": true | |
| 15 | + } | |
| 16 | +} | |
added
packages/config/tsconfig/nextjs.json
+13 −0
@@ -0,0 +1,13 @@ | ||
| 1 | +{ | |
| 2 | + "$schema": "https://json.schemastore.org/tsconfig", | |
| 3 | + "extends": "./base.json", | |
| 4 | + "compilerOptions": { | |
| 5 | + "lib": ["dom", "dom.iterable", "ES2022"], | |
| 6 | + "jsx": "preserve", | |
| 7 | + "module": "esnext", | |
| 8 | + "moduleResolution": "bundler", | |
| 9 | + "allowJs": true, | |
| 10 | + "incremental": true, | |
| 11 | + "plugins": [{ "name": "next" }] | |
| 12 | + } | |
| 13 | +} | |
added
packages/db/package.json
+30 −0
@@ -0,0 +1,30 @@ | ||
| 1 | +{ | |
| 2 | + "name": "@airiskindex/db", | |
| 3 | + "version": "0.0.0", | |
| 4 | + "private": true, | |
| 5 | + "type": "module", | |
| 6 | + "main": "./src/index.ts", | |
| 7 | + "types": "./src/index.ts", | |
| 8 | + "exports": { | |
| 9 | + ".": { | |
| 10 | + "types": "./src/index.ts", | |
| 11 | + "default": "./src/index.ts" | |
| 12 | + } | |
| 13 | + }, | |
| 14 | + "scripts": { | |
| 15 | + "postinstall": "prisma generate", | |
| 16 | + "db:migrate": "prisma migrate dev", | |
| 17 | + "db:migrate:deploy": "prisma migrate deploy", | |
| 18 | + "db:seed": "tsx prisma/seed.ts", | |
| 19 | + "typecheck": "tsc --noEmit" | |
| 20 | + }, | |
| 21 | + "dependencies": { | |
| 22 | + "@prisma/client": "^5.17.0" | |
| 23 | + }, | |
| 24 | + "devDependencies": { | |
| 25 | + "@types/node": "^20.14.11", | |
| 26 | + "prisma": "^5.17.0", | |
| 27 | + "tsx": "^4.16.2", | |
| 28 | + "typescript": "^5.5.4" | |
| 29 | + } | |
| 30 | +} | |
added
packages/db/prisma/schema.prisma
+150 −0
@@ -0,0 +1,150 @@ | ||
| 1 | +// AI Risk Index — database schema. | |
| 2 | +// Methodology integrity (CLAUDE.md §9): every published score traces to a | |
| 3 | +// ScoreRun; runs are immutable — recomputations create new runs. | |
| 4 | + | |
| 5 | +generator client { | |
| 6 | + provider = "prisma-client-js" | |
| 7 | +} | |
| 8 | + | |
| 9 | +datasource db { | |
| 10 | + provider = "postgresql" | |
| 11 | + url = env("DATABASE_URL") | |
| 12 | +} | |
| 13 | + | |
| 14 | +model Occupation { | |
| 15 | + code String @id // O*NET-SOC 2019, e.g. "15-1252.00" | |
| 16 | + title String | |
| 17 | + description String? | |
| 18 | + // Crosswalks (METHODOLOGY.md §2); crosswalk loss flagged per occupation | |
| 19 | + escoUri String? | |
| 20 | + romeCode String? | |
| 21 | + crosswalkNote String? | |
| 22 | + // Wages as integer cents + ISO currency (CLAUDE.md §5) | |
| 23 | + medianWageCents Int? | |
| 24 | + wageCurrency String @default("USD") | |
| 25 | + employment Int? | |
| 26 | + | |
| 27 | + tasks Task[] | |
| 28 | + scores OccupationScore[] | |
| 29 | +} | |
| 30 | + | |
| 31 | +model Task { | |
| 32 | + id String @id // O*NET Task ID | |
| 33 | + occupationCode String | |
| 34 | + statement String | |
| 35 | + /// O*NET Task Ratings importance (IM, 1–5) | |
| 36 | + importance Float? | |
| 37 | + | |
| 38 | + occupation Occupation @relation(fields: [occupationCode], references: [code]) | |
| 39 | + ratings TaskRating[] | |
| 40 | + overrides ExpertOverride[] | |
| 41 | + scores TaskScore[] | |
| 42 | + | |
| 43 | + @@index([occupationCode]) | |
| 44 | +} | |
| 45 | + | |
| 46 | +/// One LLM rating of one task on one dimension by one model — full audit | |
| 47 | +/// trail (CLAUDE.md §6): model, prompt version, raw response, parsed score. | |
| 48 | +model TaskRating { | |
| 49 | + id String @id @default(cuid()) | |
| 50 | + taskId String | |
| 51 | + /// DimensionKey from packages/scoring, or "augmentation" | |
| 52 | + dimension String | |
| 53 | + model String | |
| 54 | + promptVersion String | |
| 55 | + sampleIndex Int @default(0) | |
| 56 | + rating Int // 1–5 | |
| 57 | + rationale String? | |
| 58 | + rawResponse Json | |
| 59 | + ratedAt DateTime @default(now()) | |
| 60 | + | |
| 61 | + task Task @relation(fields: [taskId], references: [id]) | |
| 62 | + | |
| 63 | + @@unique([taskId, dimension, model, promptVersion, sampleIndex]) | |
| 64 | + @@index([taskId, dimension]) | |
| 65 | + @@index([promptVersion]) | |
| 66 | +} | |
| 67 | + | |
| 68 | +/// Expert (Delphi) override: replaces the LLM rating band for a task × | |
| 69 | +/// dimension. Flagged in API output (METHODOLOGY.md §3). | |
| 70 | +model ExpertOverride { | |
| 71 | + id String @id @default(cuid()) | |
| 72 | + taskId String | |
| 73 | + dimension String | |
| 74 | + ratingLow Int | |
| 75 | + ratingMid Float | |
| 76 | + ratingHigh Int | |
| 77 | + reviewer String | |
| 78 | + note String? | |
| 79 | + createdAt DateTime @default(now()) | |
| 80 | + | |
| 81 | + task Task @relation(fields: [taskId], references: [id]) | |
| 82 | + | |
| 83 | + @@unique([taskId, dimension]) | |
| 84 | +} | |
| 85 | + | |
| 86 | +/// Immutable computation run. Old runs remain queryable forever. | |
| 87 | +model ScoreRun { | |
| 88 | + id String @id @default(cuid()) | |
| 89 | + indexVersion String | |
| 90 | + raterPromptVersion String | |
| 91 | + raterModels String[] | |
| 92 | + notes String? | |
| 93 | + createdAt DateTime @default(now()) | |
| 94 | + | |
| 95 | + occupationScores OccupationScore[] | |
| 96 | + taskScores TaskScore[] | |
| 97 | + | |
| 98 | + @@index([indexVersion]) | |
| 99 | +} | |
| 100 | + | |
| 101 | +model OccupationScore { | |
| 102 | + id String @id @default(cuid()) | |
| 103 | + runId String | |
| 104 | + occupationCode String | |
| 105 | + substitutionLow Float | |
| 106 | + substitution Float | |
| 107 | + substitutionHigh Float | |
| 108 | + exposureLow Float | |
| 109 | + exposure Float | |
| 110 | + exposureHigh Float | |
| 111 | + augmentationLow Float | |
| 112 | + augmentation Float | |
| 113 | + augmentationHigh Float | |
| 114 | + highlyExposedTaskShare Float | |
| 115 | + | |
| 116 | + run ScoreRun @relation(fields: [runId], references: [id]) | |
| 117 | + occupation Occupation @relation(fields: [occupationCode], references: [code]) | |
| 118 | + | |
| 119 | + @@unique([runId, occupationCode]) | |
| 120 | + @@index([occupationCode]) | |
| 121 | +} | |
| 122 | + | |
| 123 | +model TaskScore { | |
| 124 | + id String @id @default(cuid()) | |
| 125 | + runId String | |
| 126 | + taskId String | |
| 127 | + substitutionLow Float | |
| 128 | + substitution Float | |
| 129 | + substitutionHigh Float | |
| 130 | + exposureLow Float | |
| 131 | + exposure Float | |
| 132 | + exposureHigh Float | |
| 133 | + augmentationLow Float | |
| 134 | + augmentation Float | |
| 135 | + augmentationHigh Float | |
| 136 | + | |
| 137 | + run ScoreRun @relation(fields: [runId], references: [id]) | |
| 138 | + task Task @relation(fields: [taskId], references: [id]) | |
| 139 | + | |
| 140 | + @@unique([runId, taskId]) | |
| 141 | + @@index([taskId]) | |
| 142 | +} | |
| 143 | + | |
| 144 | +model ApiKey { | |
| 145 | + id String @id @default(cuid()) | |
| 146 | + hashedKey String @unique | |
| 147 | + name String | |
| 148 | + rateLimitPerMin Int @default(600) | |
| 149 | + createdAt DateTime @default(now()) | |
| 150 | +} | |
added
packages/db/prisma/seed.ts
+46 −0
@@ -0,0 +1,46 @@ | ||
| 1 | +import { readFileSync } from "node:fs"; | |
| 2 | +import { PrismaClient } from "@prisma/client"; | |
| 3 | + | |
| 4 | +// Seeds the synthetic methodology example occupation so the app and API have | |
| 5 | +// demo data before the real O*NET ETL runs. Real data: `cd apps/etl && make pipeline`. | |
| 6 | +const prisma = new PrismaClient(); | |
| 7 | + | |
| 8 | +const example = JSON.parse( | |
| 9 | + readFileSync(new URL("../../../docs/methodology/examples/example-analyst.json", import.meta.url), "utf8"), | |
| 10 | +); | |
| 11 | + | |
| 12 | +async function main(): Promise<void> { | |
| 13 | + const { code, title } = example.occupation; | |
| 14 | + await prisma.occupation.upsert({ | |
| 15 | + where: { code }, | |
| 16 | + update: { title }, | |
| 17 | + create: { | |
| 18 | + code, | |
| 19 | + title, | |
| 20 | + description: | |
| 21 | + "Synthetic occupation from the published methodology example. Not real O*NET data.", | |
| 22 | + }, | |
| 23 | + }); | |
| 24 | + | |
| 25 | + for (const task of example.tasks) { | |
| 26 | + await prisma.task.upsert({ | |
| 27 | + where: { id: `${code}-${task.taskId}` }, | |
| 28 | + update: { importance: task.importance }, | |
| 29 | + create: { | |
| 30 | + id: `${code}-${task.taskId}`, | |
| 31 | + occupationCode: code, | |
| 32 | + statement: `Synthetic task ${task.taskId} (see docs/methodology/examples/)`, | |
| 33 | + importance: task.importance, | |
| 34 | + }, | |
| 35 | + }); | |
| 36 | + } | |
| 37 | + | |
| 38 | + console.log(`Seeded example occupation ${code} with ${example.tasks.length} tasks.`); | |
| 39 | +} | |
| 40 | + | |
| 41 | +main() | |
| 42 | + .catch((error) => { | |
| 43 | + console.error(error); | |
| 44 | + process.exitCode = 1; | |
| 45 | + }) | |
| 46 | + .finally(() => prisma.$disconnect()); | |
added
packages/db/src/index.ts
+12 −0
@@ -0,0 +1,12 @@ | ||
| 1 | +import { PrismaClient } from "@prisma/client"; | |
| 2 | + | |
| 3 | +// Singleton to avoid exhausting connections under Next.js dev hot-reload. | |
| 4 | +const globalForPrisma = globalThis as unknown as { prisma?: PrismaClient }; | |
| 5 | + | |
| 6 | +export const prisma = globalForPrisma.prisma ?? new PrismaClient(); | |
| 7 | + | |
| 8 | +if (process.env.NODE_ENV !== "production") { | |
| 9 | + globalForPrisma.prisma = prisma; | |
| 10 | +} | |
| 11 | + | |
| 12 | +export * from "@prisma/client"; | |
added
packages/db/tsconfig.json
+4 −0
@@ -0,0 +1,4 @@ | ||
| 1 | +{ | |
| 2 | + "extends": "../config/tsconfig/base.json", | |
| 3 | + "include": ["src", "prisma"] | |
| 4 | +} | |
added
packages/scoring/package.json
+24 −0
@@ -0,0 +1,24 @@ | ||
| 1 | +{ | |
| 2 | + "name": "@airiskindex/scoring", | |
| 3 | + "version": "1.0.0", | |
| 4 | + "private": true, | |
| 5 | + "type": "module", | |
| 6 | + "main": "./src/index.ts", | |
| 7 | + "types": "./src/index.ts", | |
| 8 | + "exports": { | |
| 9 | + ".": { | |
| 10 | + "types": "./src/index.ts", | |
| 11 | + "default": "./src/index.ts" | |
| 12 | + } | |
| 13 | + }, | |
| 14 | + "scripts": { | |
| 15 | + "test": "vitest run", | |
| 16 | + "typecheck": "tsc --noEmit" | |
| 17 | + }, | |
| 18 | + "devDependencies": { | |
| 19 | + "@types/node": "^20.14.11", | |
| 20 | + "fast-check": "^3.20.0", | |
| 21 | + "typescript": "^5.5.4", | |
| 22 | + "vitest": "^2.0.4" | |
| 23 | + } | |
| 24 | +} | |
added
packages/scoring/src/composite.test.ts
+71 −0
@@ -0,0 +1,71 @@ | ||
| 1 | +import { readFileSync } from "node:fs"; | |
| 2 | +import { describe, expect, it } from "vitest"; | |
| 3 | +import { HIGH_EXPOSURE_THRESHOLD, scoreOccupation } from "./index"; | |
| 4 | + | |
| 5 | +// Published worked example — METHODOLOGY.md §5-6. Changing it requires an INDEX_VERSION bump. | |
| 6 | +const example = JSON.parse( | |
| 7 | + readFileSync(new URL("../../../docs/methodology/examples/example-analyst.json", import.meta.url), "utf8"), | |
| 8 | +); | |
| 9 | + | |
| 10 | +const BAND_KEYS = ["substitution", "exposure", "augmentation"] as const; | |
| 11 | + | |
| 12 | +describe("published methodology example (example-analyst.json)", () => { | |
| 13 | + const result = scoreOccupation(example.tasks); | |
| 14 | + | |
| 15 | + it("matches the expected task scores", () => { | |
| 16 | + expect(result.tasks).toHaveLength(example.expected.taskScores.length); | |
| 17 | + for (const [index, expected] of example.expected.taskScores.entries()) { | |
| 18 | + const actual = result.tasks[index]; | |
| 19 | + expect(actual.taskId).toBe(expected.taskId); | |
| 20 | + for (const key of BAND_KEYS) { | |
| 21 | + expect(actual[key].low).toBeCloseTo(expected[key].low, 3); | |
| 22 | + expect(actual[key].score).toBeCloseTo(expected[key].score, 3); | |
| 23 | + expect(actual[key].high).toBeCloseTo(expected[key].high, 3); | |
| 24 | + } | |
| 25 | + } | |
| 26 | + }); | |
| 27 | + | |
| 28 | + it("matches the expected occupation scores", () => { | |
| 29 | + for (const key of BAND_KEYS) { | |
| 30 | + expect(result[key].low).toBeCloseTo(example.expected.occupation[key].low, 3); | |
| 31 | + expect(result[key].score).toBeCloseTo(example.expected.occupation[key].score, 3); | |
| 32 | + expect(result[key].high).toBeCloseTo(example.expected.occupation[key].high, 3); | |
| 33 | + } | |
| 34 | + }); | |
| 35 | + | |
| 36 | + it("reports the highly exposed task share", () => { | |
| 37 | + expect(HIGH_EXPOSURE_THRESHOLD).toBe(70); | |
| 38 | + expect(result.highlyExposedTaskShare).toBeCloseTo( | |
| 39 | + example.expected.occupation.highlyExposedTaskShare, | |
| 40 | + 3, | |
| 41 | + ); | |
| 42 | + }); | |
| 43 | +}); | |
| 44 | + | |
| 45 | +describe("scoreOccupation input handling", () => { | |
| 46 | + it("rejects an empty task list", () => { | |
| 47 | + expect(() => scoreOccupation([])).toThrow(RangeError); | |
| 48 | + }); | |
| 49 | + | |
| 50 | + it("fills missing importance with the occupation mean", () => { | |
| 51 | + const [t1, t2, t3] = example.tasks; | |
| 52 | + const withMissing = [t1, { ...t2, importance: undefined }, t3]; | |
| 53 | + const result = scoreOccupation(withMissing); | |
| 54 | + // t2 gets mean(4, 1) = 2.5 → weights 4/7.5, 2.5/7.5, 1/7.5 | |
| 55 | + const expected = | |
| 56 | + (4 / 7.5) * 71.25 + (2.5 / 7.5) * 27.5 + (1 / 7.5) * 86.25; | |
| 57 | + expect(result.substitution.score).toBeCloseTo(expected, 3); | |
| 58 | + }); | |
| 59 | + | |
| 60 | + it("rejects out-of-range ratings", () => { | |
| 61 | + const bad = JSON.parse(JSON.stringify(example.tasks[0])); | |
| 62 | + bad.ratings.automatability.mid = 6; | |
| 63 | + expect(() => scoreOccupation([bad])).toThrow(RangeError); | |
| 64 | + }); | |
| 65 | + | |
| 66 | + it("rejects inverted bands (low > high)", () => { | |
| 67 | + const bad = JSON.parse(JSON.stringify(example.tasks[0])); | |
| 68 | + bad.ratings.feasibility = { low: 4, mid: 3, high: 2 }; | |
| 69 | + expect(() => scoreOccupation([bad])).toThrow(RangeError); | |
| 70 | + }); | |
| 71 | +}); | |
added
packages/scoring/src/composite.ts
+60 −0
@@ -0,0 +1,60 @@ | ||
| 1 | +import { scoreTask } from "./task"; | |
| 2 | +import type { OccupationScores, ScoreBand, TaskInput, TaskScores } from "./types"; | |
| 3 | +import { INDEX_VERSION } from "./version"; | |
| 4 | + | |
| 5 | +/** Substitution score at or above which a task counts as "highly exposed" — METHODOLOGY.md §6. */ | |
| 6 | +export const HIGH_EXPOSURE_THRESHOLD = 70; | |
| 7 | + | |
| 8 | +/** | |
| 9 | + * Aggregate task scores to an occupation: importance-weighted mean, weights | |
| 10 | + * normalized within the occupation — METHODOLOGY.md §6. Pure and deterministic. | |
| 11 | + */ | |
| 12 | +export function scoreOccupation(tasks: readonly TaskInput[]): OccupationScores { | |
| 13 | + if (tasks.length === 0) { | |
| 14 | + throw new RangeError("scoreOccupation: at least one task is required"); | |
| 15 | + } | |
| 16 | + | |
| 17 | + const taskScores = tasks.map(scoreTask); | |
| 18 | + | |
| 19 | + const provided = tasks | |
| 20 | + .map((task) => task.importance) | |
| 21 | + .filter((value): value is number => value !== undefined); | |
| 22 | + for (const value of provided) { | |
| 23 | + if (!Number.isFinite(value) || value < 0) { | |
| 24 | + throw new RangeError(`scoreOccupation: importance must be a non-negative number, got ${value}`); | |
| 25 | + } | |
| 26 | + } | |
| 27 | + // Tasks lacking an importance rating receive the occupation-mean importance (§6). | |
| 28 | + const fallback = | |
| 29 | + provided.length > 0 ? provided.reduce((sum, value) => sum + value, 0) / provided.length : 1; | |
| 30 | + const raw = tasks.map((task) => task.importance ?? fallback); | |
| 31 | + const rawSum = raw.reduce((sum, value) => sum + value, 0); | |
| 32 | + const weights = rawSum > 0 ? raw.map((value) => value / rawSum) : raw.map(() => 1 / raw.length); | |
| 33 | + | |
| 34 | + const aggregate = (pick: (task: TaskScores) => ScoreBand): ScoreBand => { | |
| 35 | + let low = 0; | |
| 36 | + let score = 0; | |
| 37 | + let high = 0; | |
| 38 | + for (const [index, task] of taskScores.entries()) { | |
| 39 | + const weight = weights[index]; | |
| 40 | + const band = pick(task); | |
| 41 | + low += weight * band.low; | |
| 42 | + score += weight * band.score; | |
| 43 | + high += weight * band.high; | |
| 44 | + } | |
| 45 | + return { low, score, high }; | |
| 46 | + }; | |
| 47 | + | |
| 48 | + const highlyExposedTaskShare = | |
| 49 | + taskScores.filter((task) => task.substitution.score >= HIGH_EXPOSURE_THRESHOLD).length / | |
| 50 | + taskScores.length; | |
| 51 | + | |
| 52 | + return { | |
| 53 | + indexVersion: INDEX_VERSION, | |
| 54 | + substitution: aggregate((task) => task.substitution), | |
| 55 | + exposure: aggregate((task) => task.exposure), | |
| 56 | + augmentation: aggregate((task) => task.augmentation), | |
| 57 | + highlyExposedTaskShare, | |
| 58 | + tasks: taskScores, | |
| 59 | + }; | |
| 60 | +} | |
added
packages/scoring/src/index.ts
+18 −0
@@ -0,0 +1,18 @@ | ||
| 1 | +export { HIGH_EXPOSURE_THRESHOLD, scoreOccupation } from "./composite"; | |
| 2 | +export { pressure, scoreTask } from "./task"; | |
| 3 | +export type { | |
| 4 | + OccupationScores, | |
| 5 | + RatingBand, | |
| 6 | + ScoreBand, | |
| 7 | + TaskInput, | |
| 8 | + TaskRatings, | |
| 9 | + TaskScores, | |
| 10 | +} from "./types"; | |
| 11 | +export { INDEX_VERSION } from "./version"; | |
| 12 | +export { | |
| 13 | + DIMENSIONS, | |
| 14 | + type DimensionKey, | |
| 15 | + EXPOSURE_DIMENSIONS, | |
| 16 | + INVERTED_DIMENSIONS, | |
| 17 | + WEIGHTS, | |
| 18 | +} from "./weights"; | |
added
packages/scoring/src/properties.test.ts
+118 −0
@@ -0,0 +1,118 @@ | ||
| 1 | +import fc from "fast-check"; | |
| 2 | +import { describe, expect, it } from "vitest"; | |
| 3 | +import type { RatingBand, TaskInput, TaskRatings } from "./index"; | |
| 4 | +import { scoreOccupation, scoreTask, WEIGHTS } from "./index"; | |
| 5 | + | |
| 6 | +const ratingBand = fc | |
| 7 | + .tuple( | |
| 8 | + fc.integer({ min: 1, max: 5 }), | |
| 9 | + fc.integer({ min: 1, max: 5 }), | |
| 10 | + fc.integer({ min: 1, max: 5 }), | |
| 11 | + ) | |
| 12 | + .map(([a, b, c]): RatingBand => { | |
| 13 | + const [low, mid, high] = [a, b, c].sort((x, y) => x - y); | |
| 14 | + return { low, mid, high }; | |
| 15 | + }); | |
| 16 | + | |
| 17 | +const taskRatings = fc.record<TaskRatings>({ | |
| 18 | + automatability: ratingBand, | |
| 19 | + feasibility: ratingBand, | |
| 20 | + cost_ratio: ratingBand, | |
| 21 | + barriers: ratingBand, | |
| 22 | + adoption_velocity: ratingBand, | |
| 23 | + augmentation: ratingBand, | |
| 24 | +}); | |
| 25 | + | |
| 26 | +const taskInput = fc.record<TaskInput>({ | |
| 27 | + taskId: fc.hexaString({ minLength: 1, maxLength: 8 }), | |
| 28 | + importance: fc.double({ min: 0.1, max: 5, noNaN: true }), | |
| 29 | + ratings: taskRatings, | |
| 30 | +}); | |
| 31 | + | |
| 32 | +const occupationTasks = fc.array(taskInput, { minLength: 1, maxLength: 8 }); | |
| 33 | + | |
| 34 | +const flat = (rating: number): RatingBand => ({ low: rating, mid: rating, high: rating }); | |
| 35 | + | |
| 36 | +describe("scoring invariants (property-based)", () => { | |
| 37 | + it("weights sum to 1", () => { | |
| 38 | + const sum = Object.values(WEIGHTS).reduce((total, weight) => total + weight, 0); | |
| 39 | + expect(sum).toBeCloseTo(1, 10); | |
| 40 | + }); | |
| 41 | + | |
| 42 | + it("all score bands satisfy 0 ≤ low ≤ score ≤ high ≤ 100", () => { | |
| 43 | + fc.assert( | |
| 44 | + fc.property(occupationTasks, (tasks) => { | |
| 45 | + const result = scoreOccupation(tasks); | |
| 46 | + const bands = [ | |
| 47 | + result.substitution, | |
| 48 | + result.exposure, | |
| 49 | + result.augmentation, | |
| 50 | + ...result.tasks.flatMap((task) => [task.substitution, task.exposure, task.augmentation]), | |
| 51 | + ]; | |
| 52 | + for (const band of bands) { | |
| 53 | + expect(band.low).toBeGreaterThanOrEqual(-1e-9); | |
| 54 | + expect(band.score).toBeGreaterThanOrEqual(band.low - 1e-9); | |
| 55 | + expect(band.high).toBeGreaterThanOrEqual(band.score - 1e-9); | |
| 56 | + expect(band.high).toBeLessThanOrEqual(100 + 1e-9); | |
| 57 | + } | |
| 58 | + expect(result.highlyExposedTaskShare).toBeGreaterThanOrEqual(0); | |
| 59 | + expect(result.highlyExposedTaskShare).toBeLessThanOrEqual(1); | |
| 60 | + }), | |
| 61 | + ); | |
| 62 | + }); | |
| 63 | + | |
| 64 | + it("is deterministic", () => { | |
| 65 | + fc.assert( | |
| 66 | + fc.property(occupationTasks, (tasks) => { | |
| 67 | + expect(JSON.stringify(scoreOccupation(tasks))).toBe(JSON.stringify(scoreOccupation(tasks))); | |
| 68 | + }), | |
| 69 | + ); | |
| 70 | + }); | |
| 71 | + | |
| 72 | + it("substitution increases with automatability and decreases with barriers", () => { | |
| 73 | + const flatRating = fc.integer({ min: 1, max: 5 }); | |
| 74 | + fc.assert( | |
| 75 | + fc.property( | |
| 76 | + fc.record({ | |
| 77 | + feasibility: flatRating, | |
| 78 | + cost_ratio: flatRating, | |
| 79 | + adoption_velocity: flatRating, | |
| 80 | + augmentation: flatRating, | |
| 81 | + }), | |
| 82 | + fc.integer({ min: 1, max: 4 }), | |
| 83 | + (rest, rating) => { | |
| 84 | + const withDims = (automatability: number, barriers: number): number => | |
| 85 | + scoreTask({ | |
| 86 | + taskId: "t", | |
| 87 | + ratings: { | |
| 88 | + automatability: flat(automatability), | |
| 89 | + feasibility: flat(rest.feasibility), | |
| 90 | + cost_ratio: flat(rest.cost_ratio), | |
| 91 | + barriers: flat(barriers), | |
| 92 | + adoption_velocity: flat(rest.adoption_velocity), | |
| 93 | + augmentation: flat(rest.augmentation), | |
| 94 | + }, | |
| 95 | + }).substitution.score; | |
| 96 | + expect(withDims(rating + 1, 3)).toBeGreaterThan(withDims(rating, 3)); | |
| 97 | + expect(withDims(3, rating + 1)).toBeLessThan(withDims(3, rating)); | |
| 98 | + }, | |
| 99 | + ), | |
| 100 | + ); | |
| 101 | + }); | |
| 102 | + | |
| 103 | + it("is invariant to uniform scaling of importance weights", () => { | |
| 104 | + fc.assert( | |
| 105 | + fc.property(occupationTasks, fc.double({ min: 0.5, max: 10, noNaN: true }), (tasks, k) => { | |
| 106 | + const scaled = tasks.map((task) => ({ | |
| 107 | + ...task, | |
| 108 | + importance: (task.importance ?? 1) * k, | |
| 109 | + })); | |
| 110 | + const a = scoreOccupation(tasks); | |
| 111 | + const b = scoreOccupation(scaled); | |
| 112 | + expect(b.substitution.score).toBeCloseTo(a.substitution.score, 6); | |
| 113 | + expect(b.exposure.score).toBeCloseTo(a.exposure.score, 6); | |
| 114 | + expect(b.augmentation.score).toBeCloseTo(a.augmentation.score, 6); | |
| 115 | + }), | |
| 116 | + ); | |
| 117 | + }); | |
| 118 | +}); | |
added
packages/scoring/src/task.ts
+99 −0
@@ -0,0 +1,99 @@ | ||
| 1 | +import type { RatingBand, ScoreBand, TaskInput, TaskScores } from "./types"; | |
| 2 | +import { | |
| 3 | + DIMENSIONS, | |
| 4 | + type DimensionKey, | |
| 5 | + EXPOSURE_DIMENSIONS, | |
| 6 | + INVERTED_DIMENSIONS, | |
| 7 | + WEIGHTS, | |
| 8 | +} from "./weights"; | |
| 9 | + | |
| 10 | +function assertRating(value: number, context: string): void { | |
| 11 | + if (!Number.isFinite(value) || value < 1 || value > 5) { | |
| 12 | + throw new RangeError(`${context}: rating must be within [1, 5], got ${value}`); | |
| 13 | + } | |
| 14 | +} | |
| 15 | + | |
| 16 | +function assertBand(band: RatingBand, context: string): void { | |
| 17 | + assertRating(band.low, `${context}.low`); | |
| 18 | + assertRating(band.mid, `${context}.mid`); | |
| 19 | + assertRating(band.high, `${context}.high`); | |
| 20 | + if (band.low > band.mid || band.mid > band.high) { | |
| 21 | + throw new RangeError(`${context}: band must satisfy low ≤ mid ≤ high`); | |
| 22 | + } | |
| 23 | +} | |
| 24 | + | |
| 25 | +interface PressureBand { | |
| 26 | + low: number; | |
| 27 | + mid: number; | |
| 28 | + high: number; | |
| 29 | +} | |
| 30 | + | |
| 31 | +/** METHODOLOGY.md §5: rating (1–5) → substitution pressure in [0, 1]. */ | |
| 32 | +export function pressure(dimension: DimensionKey, rating: number): number { | |
| 33 | + const p = (rating - 1) / 4; | |
| 34 | + return INVERTED_DIMENSIONS.has(dimension) ? 1 - p : p; | |
| 35 | +} | |
| 36 | + | |
| 37 | +/** | |
| 38 | + * Pressure bounds for a rating band. For inverted dimensions the rating's | |
| 39 | + * HIGH bound minimizes pressure, so the bounds swap — this is what keeps | |
| 40 | + * low ≤ score ≤ high true for every dimension orientation. | |
| 41 | + */ | |
| 42 | +function pressureBand(dimension: DimensionKey, band: RatingBand): PressureBand { | |
| 43 | + const inverted = INVERTED_DIMENSIONS.has(dimension); | |
| 44 | + return { | |
| 45 | + low: pressure(dimension, inverted ? band.high : band.low), | |
| 46 | + mid: pressure(dimension, band.mid), | |
| 47 | + high: pressure(dimension, inverted ? band.low : band.high), | |
| 48 | + }; | |
| 49 | +} | |
| 50 | + | |
| 51 | +function weightedScore( | |
| 52 | + pressures: ReadonlyMap<DimensionKey, PressureBand>, | |
| 53 | + dimensions: readonly DimensionKey[], | |
| 54 | + bound: keyof PressureBand, | |
| 55 | +): number { | |
| 56 | + let total = 0; | |
| 57 | + let weightSum = 0; | |
| 58 | + for (const dimension of dimensions) { | |
| 59 | + const band = pressures.get(dimension); | |
| 60 | + if (!band) throw new RangeError(`missing pressure for dimension "${dimension}"`); | |
| 61 | + total += WEIGHTS[dimension] * band[bound]; | |
| 62 | + weightSum += WEIGHTS[dimension]; | |
| 63 | + } | |
| 64 | + return (100 * total) / weightSum; | |
| 65 | +} | |
| 66 | + | |
| 67 | +function toScoreBand(compute: (bound: keyof PressureBand) => number): ScoreBand { | |
| 68 | + return { low: compute("low"), score: compute("mid"), high: compute("high") }; | |
| 69 | +} | |
| 70 | + | |
| 71 | +/** Score a single task — formulas in METHODOLOGY.md §5. Pure and deterministic. */ | |
| 72 | +export function scoreTask(input: TaskInput): TaskScores { | |
| 73 | + const pressures = new Map<DimensionKey, PressureBand>(); | |
| 74 | + for (const dimension of DIMENSIONS) { | |
| 75 | + const band = input.ratings[dimension]; | |
| 76 | + if (!band) { | |
| 77 | + throw new RangeError(`task ${input.taskId}: missing rating for dimension "${dimension}"`); | |
| 78 | + } | |
| 79 | + assertBand(band, `task ${input.taskId}.${dimension}`); | |
| 80 | + pressures.set(dimension, pressureBand(dimension, band)); | |
| 81 | + } | |
| 82 | + | |
| 83 | + const augmentation = input.ratings.augmentation; | |
| 84 | + if (!augmentation) { | |
| 85 | + throw new RangeError(`task ${input.taskId}: missing augmentation rating`); | |
| 86 | + } | |
| 87 | + assertBand(augmentation, `task ${input.taskId}.augmentation`); | |
| 88 | + | |
| 89 | + return { | |
| 90 | + taskId: input.taskId, | |
| 91 | + substitution: toScoreBand((bound) => weightedScore(pressures, DIMENSIONS, bound)), | |
| 92 | + exposure: toScoreBand((bound) => weightedScore(pressures, EXPOSURE_DIMENSIONS, bound)), | |
| 93 | + augmentation: { | |
| 94 | + low: (100 * (augmentation.low - 1)) / 4, | |
| 95 | + score: (100 * (augmentation.mid - 1)) / 4, | |
| 96 | + high: (100 * (augmentation.high - 1)) / 4, | |
| 97 | + }, | |
| 98 | + }; | |
| 99 | +} | |
added
packages/scoring/src/types.ts
+51 −0
@@ -0,0 +1,51 @@ | ||
| 1 | +import type { DimensionKey } from "./weights"; | |
| 2 | + | |
| 3 | +/** | |
| 4 | + * A 1–5 rating band. low/high span the disagreement across the multi-model | |
| 5 | + * rater panel (or an expert override) — METHODOLOGY.md §3. | |
| 6 | + */ | |
| 7 | +export interface RatingBand { | |
| 8 | + low: number; | |
| 9 | + mid: number; | |
| 10 | + high: number; | |
| 11 | +} | |
| 12 | + | |
| 13 | +/** | |
| 14 | + * Per-task ratings: the five composite dimensions plus the separate | |
| 15 | + * augmentation rating (not part of the substitution composite). | |
| 16 | + */ | |
| 17 | +export type TaskRatings = Record<DimensionKey | "augmentation", RatingBand>; | |
| 18 | + | |
| 19 | +export interface TaskInput { | |
| 20 | + taskId: string; | |
| 21 | + /** | |
| 22 | + * O*NET Task Ratings importance (IM, 1–5). Omitted → occupation-mean | |
| 23 | + * importance — METHODOLOGY.md §6. | |
| 24 | + */ | |
| 25 | + importance?: number; | |
| 26 | + ratings: TaskRatings; | |
| 27 | +} | |
| 28 | + | |
| 29 | +/** A 0–100 score with confidence bounds; low ≤ score ≤ high always holds. */ | |
| 30 | +export interface ScoreBand { | |
| 31 | + low: number; | |
| 32 | + score: number; | |
| 33 | + high: number; | |
| 34 | +} | |
| 35 | + | |
| 36 | +export interface TaskScores { | |
| 37 | + taskId: string; | |
| 38 | + substitution: ScoreBand; | |
| 39 | + exposure: ScoreBand; | |
| 40 | + augmentation: ScoreBand; | |
| 41 | +} | |
| 42 | + | |
| 43 | +export interface OccupationScores { | |
| 44 | + indexVersion: string; | |
| 45 | + substitution: ScoreBand; | |
| 46 | + exposure: ScoreBand; | |
| 47 | + augmentation: ScoreBand; | |
| 48 | + /** Share of tasks with substitution score ≥ HIGH_EXPOSURE_THRESHOLD — METHODOLOGY.md §6. */ | |
| 49 | + highlyExposedTaskShare: number; | |
| 50 | + tasks: TaskScores[]; | |
| 51 | +} | |
added
packages/scoring/src/version.ts
+5 −0
@@ -0,0 +1,5 @@ | ||
| 1 | +/** | |
| 2 | + * Index version (semver). Bump on any change to weights, formulas, or rater | |
| 3 | + * prompts, with a docs/methodology/CHANGELOG.md entry — METHODOLOGY.md §7. | |
| 4 | + */ | |
| 5 | +export const INDEX_VERSION = "1.0.0-draft.1"; | |
added
packages/scoring/src/weights.ts
+30 −0
@@ -0,0 +1,30 @@ | ||
| 1 | +/** | |
| 2 | + * Composite dimension weights — the single source of truth (CLAUDE.md §1: | |
| 3 | + * never hardcode weights anywhere else). METHODOLOGY.md §4. | |
| 4 | + */ | |
| 5 | +export const DIMENSIONS = [ | |
| 6 | + "automatability", | |
| 7 | + "feasibility", | |
| 8 | + "cost_ratio", | |
| 9 | + "barriers", | |
| 10 | + "adoption_velocity", | |
| 11 | +] as const; | |
| 12 | + | |
| 13 | +export type DimensionKey = (typeof DIMENSIONS)[number]; | |
| 14 | + | |
| 15 | +export const WEIGHTS: Record<DimensionKey, number> = { | |
| 16 | + automatability: 0.35, | |
| 17 | + feasibility: 0.2, | |
| 18 | + cost_ratio: 0.15, | |
| 19 | + barriers: 0.2, | |
| 20 | + adoption_velocity: 0.1, | |
| 21 | +}; | |
| 22 | + | |
| 23 | +/** | |
| 24 | + * Dimensions where a higher rating means LESS substitution pressure | |
| 25 | + * (stronger barriers protect the task) — METHODOLOGY.md §4 "orientation". | |
| 26 | + */ | |
| 27 | +export const INVERTED_DIMENSIONS: ReadonlySet<DimensionKey> = new Set(["barriers"]); | |
| 28 | + | |
| 29 | +/** Dimensions composing the exposure sub-score — METHODOLOGY.md §5. */ | |
| 30 | +export const EXPOSURE_DIMENSIONS: readonly DimensionKey[] = ["automatability", "feasibility"]; | |
added
packages/scoring/tsconfig.json
+4 −0
@@ -0,0 +1,4 @@ | ||
| 1 | +{ | |
| 2 | + "extends": "../config/tsconfig/base.json", | |
| 3 | + "include": ["src"] | |
| 4 | +} | |
added
packages/ui/package.json
+25 −0
@@ -0,0 +1,25 @@ | ||
| 1 | +{ | |
| 2 | + "name": "@airiskindex/ui", | |
| 3 | + "version": "0.0.0", | |
| 4 | + "private": true, | |
| 5 | + "type": "module", | |
| 6 | + "main": "./src/index.ts", | |
| 7 | + "types": "./src/index.ts", | |
| 8 | + "exports": { | |
| 9 | + ".": { | |
| 10 | + "types": "./src/index.ts", | |
| 11 | + "default": "./src/index.ts" | |
| 12 | + } | |
| 13 | + }, | |
| 14 | + "scripts": { | |
| 15 | + "typecheck": "tsc --noEmit" | |
| 16 | + }, | |
| 17 | + "peerDependencies": { | |
| 18 | + "react": "^18.3.1" | |
| 19 | + }, | |
| 20 | + "devDependencies": { | |
| 21 | + "@types/react": "^18.3.3", | |
| 22 | + "react": "^18.3.1", | |
| 23 | + "typescript": "^5.5.4" | |
| 24 | + } | |
| 25 | +} | |
added
packages/ui/src/index.ts
+1 −0
@@ -0,0 +1 @@ | ||
| 1 | +export { ScoreBandPill, type ScoreBandProps } from "./score-band"; | |
added
packages/ui/src/score-band.tsx
+23 −0
@@ -0,0 +1,23 @@ | ||
| 1 | +export interface ScoreBandProps { | |
| 2 | + label: string; | |
| 3 | + low: number; | |
| 4 | + score: number; | |
| 5 | + high: number; | |
| 6 | +} | |
| 7 | + | |
| 8 | +/** | |
| 9 | + * Displays a 0–100 score with its confidence interval. The UI must always be | |
| 10 | + * able to display uncertainty (CLAUDE.md §1) — never render `score` without | |
| 11 | + * offering the band. | |
| 12 | + */ | |
| 13 | +export function ScoreBandPill({ label, low, score, high }: ScoreBandProps): JSX.Element { | |
| 14 | + return ( | |
| 15 | + <span className="inline-flex items-baseline gap-2 rounded-full border border-slate-300 px-3 py-1 text-sm"> | |
| 16 | + <span className="font-medium text-slate-700">{label}</span> | |
| 17 | + <span className="text-lg font-semibold tabular-nums">{score.toFixed(0)}</span> | |
| 18 | + <span className="text-xs text-slate-500 tabular-nums"> | |
| 19 | + {low.toFixed(0)}–{high.toFixed(0)} | |
| 20 | + </span> | |
| 21 | + </span> | |
| 22 | + ); | |
| 23 | +} | |
added
packages/ui/tsconfig.json
+8 −0
@@ -0,0 +1,8 @@ | ||
| 1 | +{ | |
| 2 | + "extends": "../config/tsconfig/base.json", | |
| 3 | + "compilerOptions": { | |
| 4 | + "lib": ["dom", "dom.iterable", "ES2022"], | |
| 5 | + "jsx": "react-jsx" | |
| 6 | + }, | |
| 7 | + "include": ["src"] | |
| 8 | +} | |
added
pnpm-lock.yaml
+3425 −0
@@ -0,0 +1,3425 @@ | ||
| 1 | +lockfileVersion: '9.0' | |
| 2 | + | |
| 3 | +settings: | |
| 4 | + autoInstallPeers: true | |
| 5 | + excludeLinksFromLockfile: false | |
| 6 | + | |
| 7 | +importers: | |
| 8 | + | |
| 9 | + .: | |
| 10 | + devDependencies: | |
| 11 | + '@eslint/js': | |
| 12 | + specifier: ^9.8.0 | |
| 13 | + version: 9.39.5 | |
| 14 | + eslint: | |
| 15 | + specifier: ^9.8.0 | |
| 16 | + version: 9.39.5(jiti@1.21.7) | |
| 17 | + prettier: | |
| 18 | + specifier: ^3.3.3 | |
| 19 | + version: 3.9.6 | |
| 20 | + turbo: | |
| 21 | + specifier: ^2.0.9 | |
| 22 | + version: 2.10.8 | |
| 23 | + typescript: | |
| 24 | + specifier: ^5.5.4 | |
| 25 | + version: 5.9.3 | |
| 26 | + typescript-eslint: | |
| 27 | + specifier: ^8.0.0 | |
| 28 | + version: 8.66.0(eslint@9.39.5(jiti@1.21.7))(typescript@5.9.3) | |
| 29 | + | |
| 30 | + apps/web: | |
| 31 | + dependencies: | |
| 32 | + '@airiskindex/db': | |
| 33 | + specifier: workspace:* | |
| 34 | + version: link:../../packages/db | |
| 35 | + '@airiskindex/scoring': | |
| 36 | + specifier: workspace:* | |
| 37 | + version: link:../../packages/scoring | |
| 38 | + '@airiskindex/ui': | |
| 39 | + specifier: workspace:* | |
| 40 | + version: link:../../packages/ui | |
| 41 | + next: | |
| 42 | + specifier: ^14.2.5 | |
| 43 | + version: 14.2.35(react-dom@18.3.1(react@18.3.1))(react@18.3.1) | |
| 44 | + react: | |
| 45 | + specifier: ^18.3.1 | |
| 46 | + version: 18.3.1 | |
| 47 | + react-dom: | |
| 48 | + specifier: ^18.3.1 | |
| 49 | + version: 18.3.1(react@18.3.1) | |
| 50 | + devDependencies: | |
| 51 | + '@types/node': | |
| 52 | + specifier: ^20.14.11 | |
| 53 | + version: 20.19.43 | |
| 54 | + '@types/react': | |
| 55 | + specifier: ^18.3.3 | |
| 56 | + version: 18.3.31 | |
| 57 | + '@types/react-dom': | |
| 58 | + specifier: ^18.3.0 | |
| 59 | + version: 18.3.7(@types/react@18.3.31) | |
| 60 | + autoprefixer: | |
| 61 | + specifier: ^10.4.19 | |
| 62 | + version: 10.5.4(postcss@8.5.25) | |
| 63 | + postcss: | |
| 64 | + specifier: ^8.4.39 | |
| 65 | + version: 8.5.25 | |
| 66 | + tailwindcss: | |
| 67 | + specifier: ^3.4.6 | |
| 68 | + version: 3.4.19(tsx@4.23.6) | |
| 69 | + typescript: | |
| 70 | + specifier: ^5.5.4 | |
| 71 | + version: 5.9.3 | |
| 72 | + | |
| 73 | + apps/worker: | |
| 74 | + dependencies: | |
| 75 | + '@airiskindex/db': | |
| 76 | + specifier: workspace:* | |
| 77 | + version: link:../../packages/db | |
| 78 | + '@airiskindex/scoring': | |
| 79 | + specifier: workspace:* | |
| 80 | + version: link:../../packages/scoring | |
| 81 | + '@anthropic-ai/sdk': | |
| 82 | + specifier: '>=0.32.1 <1' | |
| 83 | + version: 0.115.0(zod@3.25.76) | |
| 84 | + bullmq: | |
| 85 | + specifier: ^5.8.7 | |
| 86 | + version: 5.81.3 | |
| 87 | + ioredis: | |
| 88 | + specifier: ^5.4.1 | |
| 89 | + version: 5.11.1 | |
| 90 | + zod: | |
| 91 | + specifier: ^3.23.8 | |
| 92 | + version: 3.25.76 | |
| 93 | + devDependencies: | |
| 94 | + '@types/node': | |
| 95 | + specifier: ^20.14.11 | |
| 96 | + version: 20.19.43 | |
| 97 | + tsx: | |
| 98 | + specifier: ^4.16.2 | |
| 99 | + version: 4.23.6 | |
| 100 | + typescript: | |
| 101 | + specifier: ^5.5.4 | |
| 102 | + version: 5.9.3 | |
| 103 | + | |
| 104 | + packages/config: {} | |
| 105 | + | |
| 106 | + packages/db: | |
| 107 | + dependencies: | |
| 108 | + '@prisma/client': | |
| 109 | + specifier: ^5.17.0 | |
| 110 | + version: 5.22.0(prisma@5.22.0) | |
| 111 | + devDependencies: | |
| 112 | + '@types/node': | |
| 113 | + specifier: ^20.14.11 | |
| 114 | + version: 20.19.43 | |
| 115 | + prisma: | |
| 116 | + specifier: ^5.17.0 | |
| 117 | + version: 5.22.0 | |
| 118 | + tsx: | |
| 119 | + specifier: ^4.16.2 | |
| 120 | + version: 4.23.6 | |
| 121 | + typescript: | |
| 122 | + specifier: ^5.5.4 | |
| 123 | + version: 5.9.3 | |
| 124 | + | |
| 125 | + packages/scoring: | |
| 126 | + devDependencies: | |
| 127 | + '@types/node': | |
| 128 | + specifier: ^20.14.11 | |
| 129 | + version: 20.19.43 | |
| 130 | + fast-check: | |
| 131 | + specifier: ^3.20.0 | |
| 132 | + version: 3.23.2 | |
| 133 | + typescript: | |
| 134 | + specifier: ^5.5.4 | |
| 135 | + version: 5.9.3 | |
| 136 | + vitest: | |
| 137 | + specifier: ^2.0.4 | |
| 138 | + version: 2.1.9(@types/node@20.19.43) | |
| 139 | + | |
| 140 | + packages/ui: | |
| 141 | + devDependencies: | |
| 142 | + '@types/react': | |
| 143 | + specifier: ^18.3.3 | |
| 144 | + version: 18.3.31 | |
| 145 | + react: | |
| 146 | + specifier: ^18.3.1 | |
| 147 | + version: 18.3.1 | |
| 148 | + typescript: | |
| 149 | + specifier: ^5.5.4 | |
| 150 | + version: 5.9.3 | |
| 151 | + | |
| 152 | +packages: | |
| 153 | + | |
| 154 | + '@alloc/quick-lru@5.2.0': | |
| 155 | + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} | |
| 156 | + engines: {node: '>=10'} | |
| 157 | + | |
| 158 | + '@anthropic-ai/sdk@0.115.0': | |
| 159 | + resolution: {integrity: sha512-BJrFIVyjNuU8lfDyIJTvlRYzgQg+zEl78BxE7fq8esULsGz9IRQvGtW5spq3tydmtjQb/GFdooKGdGsetpx+lQ==} | |
| 160 | + hasBin: true | |
| 161 | + peerDependencies: | |
| 162 | + zod: ^3.25.0 || ^4.0.0 | |
| 163 | + peerDependenciesMeta: | |
| 164 | + zod: | |
| 165 | + optional: true | |
| 166 | + | |
| 167 | + '@babel/runtime@7.29.7': | |
| 168 | + resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} | |
| 169 | + engines: {node: '>=6.9.0'} | |
| 170 | + | |
| 171 | + '@esbuild/aix-ppc64@0.21.5': | |
| 172 | + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} | |
| 173 | + engines: {node: '>=12'} | |
| 174 | + cpu: [ppc64] | |
| 175 | + os: [aix] | |
| 176 | + | |
| 177 | + '@esbuild/aix-ppc64@0.28.1': | |
| 178 | + resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==} | |
| 179 | + engines: {node: '>=18'} | |
| 180 | + cpu: [ppc64] | |
| 181 | + os: [aix] | |
| 182 | + | |
| 183 | + '@esbuild/android-arm64@0.21.5': | |
| 184 | + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} | |
| 185 | + engines: {node: '>=12'} | |
| 186 | + cpu: [arm64] | |
| 187 | + os: [android] | |
| 188 | + | |
| 189 | + '@esbuild/android-arm64@0.28.1': | |
| 190 | + resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==} | |
| 191 | + engines: {node: '>=18'} | |
| 192 | + cpu: [arm64] | |
| 193 | + os: [android] | |
| 194 | + | |
| 195 | + '@esbuild/android-arm@0.21.5': | |
| 196 | + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} | |
| 197 | + engines: {node: '>=12'} | |
| 198 | + cpu: [arm] | |
| 199 | + os: [android] | |
| 200 | + | |
| 201 | + '@esbuild/android-arm@0.28.1': | |
| 202 | + resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==} | |
| 203 | + engines: {node: '>=18'} | |
| 204 | + cpu: [arm] | |
| 205 | + os: [android] | |
| 206 | + | |
| 207 | + '@esbuild/android-x64@0.21.5': | |
| 208 | + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} | |
| 209 | + engines: {node: '>=12'} | |
| 210 | + cpu: [x64] | |
| 211 | + os: [android] | |
| 212 | + | |
| 213 | + '@esbuild/android-x64@0.28.1': | |
| 214 | + resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==} | |
| 215 | + engines: {node: '>=18'} | |
| 216 | + cpu: [x64] | |
| 217 | + os: [android] | |
| 218 | + | |
| 219 | + '@esbuild/darwin-arm64@0.21.5': | |
| 220 | + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} | |
| 221 | + engines: {node: '>=12'} | |
| 222 | + cpu: [arm64] | |
| 223 | + os: [darwin] | |
| 224 | + | |
| 225 | + '@esbuild/darwin-arm64@0.28.1': | |
| 226 | + resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==} | |
| 227 | + engines: {node: '>=18'} | |
| 228 | + cpu: [arm64] | |
| 229 | + os: [darwin] | |
| 230 | + | |
| 231 | + '@esbuild/darwin-x64@0.21.5': | |
| 232 | + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} | |
| 233 | + engines: {node: '>=12'} | |
| 234 | + cpu: [x64] | |
| 235 | + os: [darwin] | |
| 236 | + | |
| 237 | + '@esbuild/darwin-x64@0.28.1': | |
| 238 | + resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==} | |
| 239 | + engines: {node: '>=18'} | |
| 240 | + cpu: [x64] | |
| 241 | + os: [darwin] | |
| 242 | + | |
| 243 | + '@esbuild/freebsd-arm64@0.21.5': | |
| 244 | + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} | |
| 245 | + engines: {node: '>=12'} | |
| 246 | + cpu: [arm64] | |
| 247 | + os: [freebsd] | |
| 248 | + | |
| 249 | + '@esbuild/freebsd-arm64@0.28.1': | |
| 250 | + resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==} | |
| 251 | + engines: {node: '>=18'} | |
| 252 | + cpu: [arm64] | |
| 253 | + os: [freebsd] | |
| 254 | + | |
| 255 | + '@esbuild/freebsd-x64@0.21.5': | |
| 256 | + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} | |
| 257 | + engines: {node: '>=12'} | |
| 258 | + cpu: [x64] | |
| 259 | + os: [freebsd] | |
| 260 | + | |
| 261 | + '@esbuild/freebsd-x64@0.28.1': | |
| 262 | + resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==} | |
| 263 | + engines: {node: '>=18'} | |
| 264 | + cpu: [x64] | |
| 265 | + os: [freebsd] | |
| 266 | + | |
| 267 | + '@esbuild/linux-arm64@0.21.5': | |
| 268 | + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} | |
| 269 | + engines: {node: '>=12'} | |
| 270 | + cpu: [arm64] | |
| 271 | + os: [linux] | |
| 272 | + | |
| 273 | + '@esbuild/linux-arm64@0.28.1': | |
| 274 | + resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==} | |
| 275 | + engines: {node: '>=18'} | |
| 276 | + cpu: [arm64] | |
| 277 | + os: [linux] | |
| 278 | + | |
| 279 | + '@esbuild/linux-arm@0.21.5': | |
| 280 | + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} | |
| 281 | + engines: {node: '>=12'} | |
| 282 | + cpu: [arm] | |
| 283 | + os: [linux] | |
| 284 | + | |
| 285 | + '@esbuild/linux-arm@0.28.1': | |
| 286 | + resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==} | |
| 287 | + engines: {node: '>=18'} | |
| 288 | + cpu: [arm] | |
| 289 | + os: [linux] | |
| 290 | + | |
| 291 | + '@esbuild/linux-ia32@0.21.5': | |
| 292 | + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} | |
| 293 | + engines: {node: '>=12'} | |
| 294 | + cpu: [ia32] | |
| 295 | + os: [linux] | |
| 296 | + | |
| 297 | + '@esbuild/linux-ia32@0.28.1': | |
| 298 | + resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==} | |
| 299 | + engines: {node: '>=18'} | |
| 300 | + cpu: [ia32] | |
| 301 | + os: [linux] | |
| 302 | + | |
| 303 | + '@esbuild/linux-loong64@0.21.5': | |
| 304 | + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} | |
| 305 | + engines: {node: '>=12'} | |
| 306 | + cpu: [loong64] | |
| 307 | + os: [linux] | |
| 308 | + | |
| 309 | + '@esbuild/linux-loong64@0.28.1': | |
| 310 | + resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==} | |
| 311 | + engines: {node: '>=18'} | |
| 312 | + cpu: [loong64] | |
| 313 | + os: [linux] | |
| 314 | + | |
| 315 | + '@esbuild/linux-mips64el@0.21.5': | |
| 316 | + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} | |
| 317 | + engines: {node: '>=12'} | |
| 318 | + cpu: [mips64el] | |
| 319 | + os: [linux] | |
| 320 | + | |
| 321 | + '@esbuild/linux-mips64el@0.28.1': | |
| 322 | + resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==} | |
| 323 | + engines: {node: '>=18'} | |
| 324 | + cpu: [mips64el] | |
| 325 | + os: [linux] | |
| 326 | + | |
| 327 | + '@esbuild/linux-ppc64@0.21.5': | |
| 328 | + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} | |
| 329 | + engines: {node: '>=12'} | |
| 330 | + cpu: [ppc64] | |
| 331 | + os: [linux] | |
| 332 | + | |
| 333 | + '@esbuild/linux-ppc64@0.28.1': | |
| 334 | + resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==} | |
| 335 | + engines: {node: '>=18'} | |
| 336 | + cpu: [ppc64] | |
| 337 | + os: [linux] | |
| 338 | + | |
| 339 | + '@esbuild/linux-riscv64@0.21.5': | |
| 340 | + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} | |
| 341 | + engines: {node: '>=12'} | |
| 342 | + cpu: [riscv64] | |
| 343 | + os: [linux] | |
| 344 | + | |
| 345 | + '@esbuild/linux-riscv64@0.28.1': | |
| 346 | + resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==} | |
| 347 | + engines: {node: '>=18'} | |
| 348 | + cpu: [riscv64] | |
| 349 | + os: [linux] | |
| 350 | + | |
| 351 | + '@esbuild/linux-s390x@0.21.5': | |
| 352 | + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} | |
| 353 | + engines: {node: '>=12'} | |
| 354 | + cpu: [s390x] | |
| 355 | + os: [linux] | |
| 356 | + | |
| 357 | + '@esbuild/linux-s390x@0.28.1': | |
| 358 | + resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==} | |
| 359 | + engines: {node: '>=18'} | |
| 360 | + cpu: [s390x] | |
| 361 | + os: [linux] | |
| 362 | + | |
| 363 | + '@esbuild/linux-x64@0.21.5': | |
| 364 | + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} | |
| 365 | + engines: {node: '>=12'} | |
| 366 | + cpu: [x64] | |
| 367 | + os: [linux] | |
| 368 | + | |
| 369 | + '@esbuild/linux-x64@0.28.1': | |
| 370 | + resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==} | |
| 371 | + engines: {node: '>=18'} | |
| 372 | + cpu: [x64] | |
| 373 | + os: [linux] | |
| 374 | + | |
| 375 | + '@esbuild/netbsd-arm64@0.28.1': | |
| 376 | + resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==} | |
| 377 | + engines: {node: '>=18'} | |
| 378 | + cpu: [arm64] | |
| 379 | + os: [netbsd] | |
| 380 | + | |
| 381 | + '@esbuild/netbsd-x64@0.21.5': | |
| 382 | + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} | |
| 383 | + engines: {node: '>=12'} | |
| 384 | + cpu: [x64] | |
| 385 | + os: [netbsd] | |
| 386 | + | |
| 387 | + '@esbuild/netbsd-x64@0.28.1': | |
| 388 | + resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==} | |
| 389 | + engines: {node: '>=18'} | |
| 390 | + cpu: [x64] | |
| 391 | + os: [netbsd] | |
| 392 | + | |
| 393 | + '@esbuild/openbsd-arm64@0.28.1': | |
| 394 | + resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==} | |
| 395 | + engines: {node: '>=18'} | |
| 396 | + cpu: [arm64] | |
| 397 | + os: [openbsd] | |
| 398 | + | |
| 399 | + '@esbuild/openbsd-x64@0.21.5': | |
| 400 | + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} | |
| 401 | + engines: {node: '>=12'} | |
| 402 | + cpu: [x64] | |
| 403 | + os: [openbsd] | |
| 404 | + | |
| 405 | + '@esbuild/openbsd-x64@0.28.1': | |
| 406 | + resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==} | |
| 407 | + engines: {node: '>=18'} | |
| 408 | + cpu: [x64] | |
| 409 | + os: [openbsd] | |
| 410 | + | |
| 411 | + '@esbuild/openharmony-arm64@0.28.1': | |
| 412 | + resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==} | |
| 413 | + engines: {node: '>=18'} | |
| 414 | + cpu: [arm64] | |
| 415 | + os: [openharmony] | |
| 416 | + | |
| 417 | + '@esbuild/sunos-x64@0.21.5': | |
| 418 | + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} | |
| 419 | + engines: {node: '>=12'} | |
| 420 | + cpu: [x64] | |
| 421 | + os: [sunos] | |
| 422 | + | |
| 423 | + '@esbuild/sunos-x64@0.28.1': | |
| 424 | + resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==} | |
| 425 | + engines: {node: '>=18'} | |
| 426 | + cpu: [x64] | |
| 427 | + os: [sunos] | |
| 428 | + | |
| 429 | + '@esbuild/win32-arm64@0.21.5': | |
| 430 | + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} | |
| 431 | + engines: {node: '>=12'} | |
| 432 | + cpu: [arm64] | |
| 433 | + os: [win32] | |
| 434 | + | |
| 435 | + '@esbuild/win32-arm64@0.28.1': | |
| 436 | + resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==} | |
| 437 | + engines: {node: '>=18'} | |
| 438 | + cpu: [arm64] | |
| 439 | + os: [win32] | |
| 440 | + | |
| 441 | + '@esbuild/win32-ia32@0.21.5': | |
| 442 | + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} | |
| 443 | + engines: {node: '>=12'} | |
| 444 | + cpu: [ia32] | |
| 445 | + os: [win32] | |
| 446 | + | |
| 447 | + '@esbuild/win32-ia32@0.28.1': | |
| 448 | + resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==} | |
| 449 | + engines: {node: '>=18'} | |
| 450 | + cpu: [ia32] | |
| 451 | + os: [win32] | |
| 452 | + | |
| 453 | + '@esbuild/win32-x64@0.21.5': | |
| 454 | + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} | |
| 455 | + engines: {node: '>=12'} | |
| 456 | + cpu: [x64] | |
| 457 | + os: [win32] | |
| 458 | + | |
| 459 | + '@esbuild/win32-x64@0.28.1': | |
| 460 | + resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==} | |
| 461 | + engines: {node: '>=18'} | |
| 462 | + cpu: [x64] | |
| 463 | + os: [win32] | |
| 464 | + | |
| 465 | + '@eslint-community/eslint-utils@4.10.1': | |
| 466 | + resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==} | |
| 467 | + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} | |
| 468 | + peerDependencies: | |
| 469 | + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 | |
| 470 | + | |
| 471 | + '@eslint-community/regexpp@4.12.2': | |
| 472 | + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} | |
| 473 | + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} | |
| 474 | + | |
| 475 | + '@eslint/config-array@0.21.2': | |
| 476 | + resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==} | |
| 477 | + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} | |
| 478 | + | |
| 479 | + '@eslint/config-helpers@0.4.2': | |
| 480 | + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} | |
| 481 | + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} | |
| 482 | + | |
| 483 | + '@eslint/core@0.17.0': | |
| 484 | + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} | |
| 485 | + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} | |
| 486 | + | |
| 487 | + '@eslint/eslintrc@3.3.6': | |
| 488 | + resolution: {integrity: sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==} | |
| 489 | + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} | |
| 490 | + | |
| 491 | + '@eslint/js@9.39.5': | |
| 492 | + resolution: {integrity: sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==} | |
| 493 | + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} | |
| 494 | + | |
| 495 | + '@eslint/object-schema@2.1.7': | |
| 496 | + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} | |
| 497 | + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} | |
| 498 | + | |
| 499 | + '@eslint/plugin-kit@0.4.1': | |
| 500 | + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} | |
| 501 | + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} | |
| 502 | + | |
| 503 | + '@humanfs/core@0.19.2': | |
| 504 | + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} | |
| 505 | + engines: {node: '>=18.18.0'} | |
| 506 | + | |
| 507 | + '@humanfs/node@0.16.8': | |
| 508 | + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} | |
| 509 | + engines: {node: '>=18.18.0'} | |
| 510 | + | |
| 511 | + '@humanfs/types@0.15.0': | |
| 512 | + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} | |
| 513 | + engines: {node: '>=18.18.0'} | |
| 514 | + | |
| 515 | + '@humanwhocodes/module-importer@1.0.1': | |
| 516 | + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} | |
| 517 | + engines: {node: '>=12.22'} | |
| 518 | + | |
| 519 | + '@humanwhocodes/retry@0.4.3': | |
| 520 | + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} | |
| 521 | + engines: {node: '>=18.18'} | |
| 522 | + | |
| 523 | + '@ioredis/commands@1.10.0': | |
| 524 | + resolution: {integrity: sha512-UmeW7z4LfctwoQ5wkhVzgq8tXkreED2xZGpX+Bg+zA+WJFZCT6c062AfCK/Dfk81xZnnwdhJCUMkitihRaoC2Q==} | |
| 525 | + | |
| 526 | + '@jridgewell/gen-mapping@0.3.13': | |
| 527 | + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} | |
| 528 | + | |
| 529 | + '@jridgewell/resolve-uri@3.1.2': | |
| 530 | + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} | |
| 531 | + engines: {node: '>=6.0.0'} | |
| 532 | + | |
| 533 | + '@jridgewell/sourcemap-codec@1.5.5': | |
| 534 | + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} | |
| 535 | + | |
| 536 | + '@jridgewell/trace-mapping@0.3.31': | |
| 537 | + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} | |
| 538 | + | |
| 539 | + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4': | |
| 540 | + resolution: {integrity: sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ==} | |
| 541 | + cpu: [arm64] | |
| 542 | + os: [darwin] | |
| 543 | + | |
| 544 | + '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.4': | |
| 545 | + resolution: {integrity: sha512-zExlW9zUJKZH/tOtVMttwjKa4Xm/3KcNjnE3dPN92uCktwavMxpgCA3MoJK/DOnTWsQgo224OaST27/mPNAf+w==} | |
| 546 | + cpu: [x64] | |
| 547 | + os: [darwin] | |
| 548 | + | |
| 549 | + '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.4': | |
| 550 | + resolution: {integrity: sha512-dgX0P/9wGPJeHFBG+ZmhgE6bmtMt7NP5CRBGyyktpopdk/mW4POnrpQsSLtKI1dwpc+pPLuXHDh6vvskyQE/sw==} | |
| 551 | + cpu: [arm64] | |
| 552 | + os: [linux] | |
| 553 | + | |
| 554 | + '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.4': | |
| 555 | + resolution: {integrity: sha512-Tg3yX65f5GbtXLkrYEHE5oibZG9epyYWas7FogTTEJeDEF9JlXJzKgXaNhT3UXlTOeA+AfZpYZYZ0uPj7Cfquw==} | |
| 556 | + cpu: [arm] | |
| 557 | + os: [linux] | |
| 558 | + | |
| 559 | + '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.4': | |
| 560 | + resolution: {integrity: sha512-8TNXMEjJc3QEy7R/x1INhgiU+XakDAFUzBhaz7+Rbrs8NH5UQeHQxxmzsSBJGyV6I1jW79undiQm8tOI+D+8FQ==} | |
| 561 | + cpu: [x64] | |
| 562 | + os: [linux] | |
| 563 | + | |
| 564 | + '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.4': | |
| 565 | + resolution: {integrity: sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ==} | |
| 566 | + cpu: [x64] | |
| 567 | + os: [win32] | |
| 568 | + | |
| 569 | + '@napi-rs/lzma-linux-x64-gnu@1.5.1': | |
| 570 | + resolution: {integrity: sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==} | |
| 571 | + engines: {node: ^22.20 || ^24.12 || >=25} | |
| 572 | + cpu: [x64] | |
| 573 | + os: [linux] | |
| 574 | + | |
| 575 | + '@next/env@14.2.35': | |
| 576 | + resolution: {integrity: sha512-DuhvCtj4t9Gwrx80dmz2F4t/zKQ4ktN8WrMwOuVzkJfBilwAwGr6v16M5eI8yCuZ63H9TTuEU09Iu2HqkzFPVQ==} | |
| 577 | + | |
| 578 | + '@next/swc-darwin-arm64@14.2.33': | |
| 579 | + resolution: {integrity: sha512-HqYnb6pxlsshoSTubdXKu15g3iivcbsMXg4bYpjL2iS/V6aQot+iyF4BUc2qA/J/n55YtvE4PHMKWBKGCF/+wA==} | |
| 580 | + engines: {node: '>= 10'} | |
| 581 | + cpu: [arm64] | |
| 582 | + os: [darwin] | |
| 583 | + | |
| 584 | + '@next/swc-darwin-x64@14.2.33': | |
| 585 | + resolution: {integrity: sha512-8HGBeAE5rX3jzKvF593XTTFg3gxeU4f+UWnswa6JPhzaR6+zblO5+fjltJWIZc4aUalqTclvN2QtTC37LxvZAA==} | |
| 586 | + engines: {node: '>= 10'} | |
| 587 | + cpu: [x64] | |
| 588 | + os: [darwin] | |
| 589 | + | |
| 590 | + '@next/swc-linux-arm64-gnu@14.2.33': | |
| 591 | + resolution: {integrity: sha512-JXMBka6lNNmqbkvcTtaX8Gu5by9547bukHQvPoLe9VRBx1gHwzf5tdt4AaezW85HAB3pikcvyqBToRTDA4DeLw==} | |
| 592 | + engines: {node: '>= 10'} | |
| 593 | + cpu: [arm64] | |
| 594 | + os: [linux] | |
| 595 | + | |
| 596 | + '@next/swc-linux-arm64-musl@14.2.33': | |
| 597 | + resolution: {integrity: sha512-Bm+QulsAItD/x6Ih8wGIMfRJy4G73tu1HJsrccPW6AfqdZd0Sfm5Imhgkgq2+kly065rYMnCOxTBvmvFY1BKfg==} | |
| 598 | + engines: {node: '>= 10'} | |
| 599 | + cpu: [arm64] | |
| 600 | + os: [linux] | |
| 601 | + | |
| 602 | + '@next/swc-linux-x64-gnu@14.2.33': | |
| 603 | + resolution: {integrity: sha512-FnFn+ZBgsVMbGDsTqo8zsnRzydvsGV8vfiWwUo1LD8FTmPTdV+otGSWKc4LJec0oSexFnCYVO4hX8P8qQKaSlg==} | |
| 604 | + engines: {node: '>= 10'} | |
| 605 | + cpu: [x64] | |
| 606 | + os: [linux] | |
| 607 | + | |
| 608 | + '@next/swc-linux-x64-musl@14.2.33': | |
| 609 | + resolution: {integrity: sha512-345tsIWMzoXaQndUTDv1qypDRiebFxGYx9pYkhwY4hBRaOLt8UGfiWKr9FSSHs25dFIf8ZqIFaPdy5MljdoawA==} | |
| 610 | + engines: {node: '>= 10'} | |
| 611 | + cpu: [x64] | |
| 612 | + os: [linux] | |
| 613 | + | |
| 614 | + '@next/swc-win32-arm64-msvc@14.2.33': | |
| 615 | + resolution: {integrity: sha512-nscpt0G6UCTkrT2ppnJnFsYbPDQwmum4GNXYTeoTIdsmMydSKFz9Iny2jpaRupTb+Wl298+Rh82WKzt9LCcqSQ==} | |
| 616 | + engines: {node: '>= 10'} | |
| 617 | + cpu: [arm64] | |
| 618 | + os: [win32] | |
| 619 | + | |
| 620 | + '@next/swc-win32-ia32-msvc@14.2.33': | |
| 621 | + resolution: {integrity: sha512-pc9LpGNKhJ0dXQhZ5QMmYxtARwwmWLpeocFmVG5Z0DzWq5Uf0izcI8tLc+qOpqxO1PWqZ5A7J1blrUIKrIFc7Q==} | |
| 622 | + engines: {node: '>= 10'} | |
| 623 | + cpu: [ia32] | |
| 624 | + os: [win32] | |
| 625 | + | |
| 626 | + '@next/swc-win32-x64-msvc@14.2.33': | |
| 627 | + resolution: {integrity: sha512-nOjfZMy8B94MdisuzZo9/57xuFVLHJaDj5e/xrduJp9CV2/HrfxTRH2fbyLe+K9QT41WBLUd4iXX3R7jBp0EUg==} | |
| 628 | + engines: {node: '>= 10'} | |
| 629 | + cpu: [x64] | |
| 630 | + os: [win32] | |
| 631 | + | |
| 632 | + '@nodelib/fs.scandir@2.1.5': | |
| 633 | + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} | |
| 634 | + engines: {node: '>= 8'} | |
| 635 | + | |
| 636 | + '@nodelib/fs.stat@2.0.5': | |
| 637 | + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} | |
| 638 | + engines: {node: '>= 8'} | |
| 639 | + | |
| 640 | + '@nodelib/fs.walk@1.2.8': | |
| 641 | + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} | |
| 642 | + engines: {node: '>= 8'} | |
| 643 | + | |
| 644 | + '@prisma/client@5.22.0': | |
| 645 | + resolution: {integrity: sha512-M0SVXfyHnQREBKxCgyo7sffrKttwE6R8PMq330MIUF0pTwjUhLbW84pFDlf06B27XyCR++VtjugEnIHdr07SVA==} | |
| 646 | + engines: {node: '>=16.13'} | |
| 647 | + peerDependencies: | |
| 648 | + prisma: '*' | |
| 649 | + peerDependenciesMeta: | |
| 650 | + prisma: | |
| 651 | + optional: true | |
| 652 | + | |
| 653 | + '@prisma/debug@5.22.0': | |
| 654 | + resolution: {integrity: sha512-AUt44v3YJeggO2ZU5BkXI7M4hu9BF2zzH2iF2V5pyXT/lRTyWiElZ7It+bRH1EshoMRxHgpYg4VB6rCM+mG5jQ==} | |
| 655 | + | |
| 656 | + '@prisma/engines-version@5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2': | |
| 657 | + resolution: {integrity: sha512-2PTmxFR2yHW/eB3uqWtcgRcgAbG1rwG9ZriSvQw+nnb7c4uCr3RAcGMb6/zfE88SKlC1Nj2ziUvc96Z379mHgQ==} | |
| 658 | + | |
| 659 | + '@prisma/engines@5.22.0': | |
| 660 | + resolution: {integrity: sha512-UNjfslWhAt06kVL3CjkuYpHAWSO6L4kDCVPegV6itt7nD1kSJavd3vhgAEhjglLJJKEdJ7oIqDJ+yHk6qO8gPA==} | |
| 661 | + | |
| 662 | + '@prisma/fetch-engine@5.22.0': | |
| 663 | + resolution: {integrity: sha512-bkrD/Mc2fSvkQBV5EpoFcZ87AvOgDxbG99488a5cexp5Ccny+UM6MAe/UFkUC0wLYD9+9befNOqGiIJhhq+HbA==} | |
| 664 | + | |
| 665 | + '@prisma/get-platform@5.22.0': | |
| 666 | + resolution: {integrity: sha512-pHhpQdr1UPFpt+zFfnPazhulaZYCUqeIcPpJViYoq9R+D/yw4fjE+CtnsnKzPYm0ddUbeXUzjGVGIRVgPDCk4Q==} | |
| 667 | + | |
| 668 | + '@rollup/rollup-android-arm-eabi@4.62.4': | |
| 669 | + resolution: {integrity: sha512-RrPokAb7dmbxFoeO3TloqHyOjgye8RkBhSqmp4aJMIex4c9r46ZstPnleDQOq1t46VOVjwIuwNogIqbodV1Vvg==} | |
| 670 | + cpu: [arm] | |
| 671 | + os: [android] | |
| 672 | + | |
| 673 | + '@rollup/rollup-android-arm64@4.62.4': | |
| 674 | + resolution: {integrity: sha512-JKuJc+pnpks2pjy7L/N3v/cAkZxYlnmuZoD840ldbMI5KDbC4iO9NKwPKYdjYFCMAIIlBzYSFHxIJVYzRo2/8A==} | |
| 675 | + cpu: [arm64] | |
| 676 | + os: [android] | |
| 677 | + | |
| 678 | + '@rollup/rollup-darwin-arm64@4.62.4': | |
| 679 | + resolution: {integrity: sha512-krw5uS2STmvJ02x0uTXHbqQNuz+9eZ1iw+qXk9dmW2gvV4jV7O2hEoOnuhFrpOPiel1mBFtqbxYZZtC46hXLOw==} | |
| 680 | + cpu: [arm64] | |
| 681 | + os: [darwin] | |
| 682 | + | |
| 683 | + '@rollup/rollup-darwin-x64@4.62.4': | |
| 684 | + resolution: {integrity: sha512-wsTxtgApb4PrOsNJIm0FZ1h3WvCC+k9uxLJ4ad75hgoS4NiRes2SoJFlDAyMwiUY8IssDqGcHbXuN0sx1tfF1A==} | |
| 685 | + cpu: [x64] | |
| 686 | + os: [darwin] | |
| 687 | + | |
| 688 | + '@rollup/rollup-freebsd-arm64@4.62.4': | |
| 689 | + resolution: {integrity: sha512-GUOnQlyZe3yAXhWOtOMsn5Qkrv5E5mZXa0thbARWi5Ei2szlVXJFQhddZ4HbAzh8q92w5twp+CQvs/eFanz9YQ==} | |
| 690 | + cpu: [arm64] | |
| 691 | + os: [freebsd] | |
| 692 | + | |
| 693 | + '@rollup/rollup-freebsd-x64@4.62.4': | |
| 694 | + resolution: {integrity: sha512-/Y7f3QuxjzPKsjA/rfEDa3+0vXqyjmJ50Ln8dPpCmWkKTrUoWHG1cWhTqaAMLob2m2nESWuC7yGrREz019Ztqg==} | |
| 695 | + cpu: [x64] | |
| 696 | + os: [freebsd] | |
| 697 | + | |
| 698 | + '@rollup/rollup-linux-arm-gnueabihf@4.62.4': | |
| 699 | + resolution: {integrity: sha512-81wiiX3v7aqy+T+bT61TJ78yJjRquqFFTTbAPt08imfQQzkPIW8t6aJbkTagtCCrXMNc9D66+geqlK7ydLPNqA==} | |
| 700 | + cpu: [arm] | |
| 701 | + os: [linux] | |
| 702 | + | |
| 703 | + '@rollup/rollup-linux-arm-musleabihf@4.62.4': | |
| 704 | + resolution: {integrity: sha512-9kmDIvNZqdoHOBZgNtpTBeLWYO/LVipM3H/j62P8848/l/VPEQL6N3uxU9pvP1oZAsXyC2MEnFP3ovRjo7WYNQ==} | |
| 705 | + cpu: [arm] | |
| 706 | + os: [linux] | |
| 707 | + | |
| 708 | + '@rollup/rollup-linux-arm64-gnu@4.62.4': | |
| 709 | + resolution: {integrity: sha512-CcnXHWnXg69g+DX5VWL3FHts3qMRN2uVEHX+BZvGLdd07/gXkn3ePjYtO1LDJvxkGKVHMclKBRa1QUTH+6toYQ==} | |
| 710 | + cpu: [arm64] | |
| 711 | + os: [linux] | |
| 712 | + | |
| 713 | + '@rollup/rollup-linux-arm64-musl@4.62.4': | |
| 714 | + resolution: {integrity: sha512-iFOibiHnTRuhrWLlRsOQFdZJJIa7S8OwkneJr4ocALP16u5yk6lWLINFwhHaEqBFMsKDUZofLkGos7+CPzGB3g==} | |
| 715 | + cpu: [arm64] | |
| 716 | + os: [linux] | |
| 717 | + | |
| 718 | + '@rollup/rollup-linux-loong64-gnu@4.62.4': | |
| 719 | + resolution: {integrity: sha512-XnWYMI7euHlb5a871xPja+Gm7DRCFU+FGRrtS2sMq9N8FvqtpagUy6gD4YOemC5MRk9xbh8+jYMEJbigFQwsgA==} | |
| 720 | + cpu: [loong64] | |
| 721 | + os: [linux] | |
| 722 | + | |
| 723 | + '@rollup/rollup-linux-loong64-musl@4.62.4': | |
| 724 | + resolution: {integrity: sha512-qGDAlO0U8xedCcsdRm9oaoQY8DAx/QT7uIxJWhCdx0ceIWX783UC9QSYkdpzAe29wNiVfp24+bZdQmn49o45SQ==} | |
| 725 | + cpu: [loong64] | |
| 726 | + os: [linux] | |
| 727 | + | |
| 728 | + '@rollup/rollup-linux-ppc64-gnu@4.62.4': | |
| 729 | + resolution: {integrity: sha512-ru4H6ezD7ysA5EiEK6qkkaEb4modH8CTej6kUy/gQi20u3kB3G7Zn8snXXkeJSCOFKG/rbPPtM/+9Wgas1961w==} | |
| 730 | + cpu: [ppc64] | |
| 731 | + os: [linux] | |
| 732 | + | |
| 733 | + '@rollup/rollup-linux-ppc64-musl@4.62.4': | |
| 734 | + resolution: {integrity: sha512-2W4MO5WQVJnbJaZdvDb9rhBDuFU1nKIepPFpJUBsTh2k1YY2g+ODViaWuyOAjQ5cOP7NvrvLzt3wvHOoiAvc7w==} | |
| 735 | + cpu: [ppc64] | |
| 736 | + os: [linux] | |
| 737 | + | |
| 738 | + '@rollup/rollup-linux-riscv64-gnu@4.62.4': | |
| 739 | + resolution: {integrity: sha512-+fxjfuoAmVMCYV5QyjoIpu0cp5DOiOTeqYFk1AVaxGr+/ravWLX89XfQmptsoWcaVy/TGf2hexzbUOrCQIL1CQ==} | |
| 740 | + cpu: [riscv64] | |
| 741 | + os: [linux] | |
| 742 | + | |
| 743 | + '@rollup/rollup-linux-riscv64-musl@4.62.4': | |
| 744 | + resolution: {integrity: sha512-jTn8JfHGL4djjFxPuM06LmNUJDsst2jeVlsd9OmIH6zc5sC9K6rIuO4YajXatLUpBmBKl6b35ro1QZocLi+tcA==} | |
| 745 | + cpu: [riscv64] | |
| 746 | + os: [linux] | |
| 747 | + | |
| 748 | + '@rollup/rollup-linux-s390x-gnu@4.62.4': | |
| 749 | + resolution: {integrity: sha512-oCJCJL4pXsoDcP2QZ+JVlPTIRc6266zsIaeJJsWImmF7HO0W8nb6HuSgZlMWxJwaPf8ehbSw8yo0EUw925hKsA==} | |
| 750 | + cpu: [s390x] | |
| 751 | + os: [linux] | |
| 752 | + | |
| 753 | + '@rollup/rollup-linux-x64-gnu@4.62.4': | |
| 754 | + resolution: {integrity: sha512-W69hukhZ3KKNRCaMIEzKvcFye42hh0FE1+YoYaf5+Ikacuftoco6yO/xouz0hc5d5W/s3yBro5jRiuEE/Q5vUw==} | |
| 755 | + cpu: [x64] | |
| 756 | + os: [linux] | |
| 757 | + | |
| 758 | + '@rollup/rollup-linux-x64-musl@4.62.4': | |
| 759 | + resolution: {integrity: sha512-qiXbGG2jkjXhzXpsFZSR2Xpb8DN/UaxYsbb/STbuR/6fpaDgRmmaq1B/LmtF2wQFOFOSsK2jdE0RZ3a0zHn4QA==} | |
| 760 | + cpu: [x64] | |
| 761 | + os: [linux] | |
| 762 | + | |
| 763 | + '@rollup/rollup-openbsd-x64@4.62.4': | |
| 764 | + resolution: {integrity: sha512-nWeM//hxv8mIo6jD7Hu4o48DVmV9pbV6gsKaWU+4NFyqHoPKwrkRiZGLKUhOBk8qNmDmpwFtPKg80Bo/Tn4xiQ==} | |
| 765 | + cpu: [x64] | |
| 766 | + os: [openbsd] | |
| 767 | + | |
| 768 | + '@rollup/rollup-openharmony-arm64@4.62.4': | |
| 769 | + resolution: {integrity: sha512-s62SQ/vgsRSvMwDkOEfTqfgASF0f26ZNaQuTA6Aok5lrikf89yI2W0gFHvZb2Jpgc6N8JnOKZgCK2iciO3CsxQ==} | |
| 770 | + cpu: [arm64] | |
| 771 | + os: [openharmony] | |
| 772 | + | |
| 773 | + '@rollup/rollup-win32-arm64-msvc@4.62.4': | |
| 774 | + resolution: {integrity: sha512-J6wGf8TVGbXJq+HH+ttTvrcfNKPbuZecV6KT1B8I18BC5IURUh5kl4Yl5OEP5eFIUoI5BWxCsyYMhFsDx8kekw==} | |
| 775 | + cpu: [arm64] | |
| 776 | + os: [win32] | |
| 777 | + | |
| 778 | + '@rollup/rollup-win32-ia32-msvc@4.62.4': | |
| 779 | + resolution: {integrity: sha512-zmfrQd/0wu6oJs8Vq8KwY/YtsKSsLtKe/HwAP4Wqy8LhWjeT55fHRAkOhYQ12wI3ayS4Tt12d5CDRD7N96SAYQ==} | |
| 780 | + cpu: [ia32] | |
| 781 | + os: [win32] | |
| 782 | + | |
| 783 | + '@rollup/rollup-win32-x64-gnu@4.62.4': | |
| 784 | + resolution: {integrity: sha512-qPzHqdj9rfUD+w79dtE07zi/kFwKyCJqplp5K5ygeLTp7jLpAoc16OAH39HSmRC9UpozaecsleI8uAdEj6v2yw==} | |
| 785 | + cpu: [x64] | |
| 786 | + os: [win32] | |
| 787 | + | |
| 788 | + '@rollup/rollup-win32-x64-msvc@4.62.4': | |
| 789 | + resolution: {integrity: sha512-zD6NdeWEByGE9QF9vCrlJ5YQB4oq9q91kPZS37Jwj5hOkvR1lTBSpsKhKDw4IJtbQ35LsTS1HD9DZYGKIshU1Q==} | |
| 790 | + cpu: [x64] | |
| 791 | + os: [win32] | |
| 792 | + | |
| 793 | + '@stablelib/base64@1.0.1': | |
| 794 | + resolution: {integrity: sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==} | |
| 795 | + | |
| 796 | + '@swc/counter@0.1.3': | |
| 797 | + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} | |
| 798 | + | |
| 799 | + '@swc/helpers@0.5.5': | |
| 800 | + resolution: {integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==} | |
| 801 | + | |
| 802 | + '@turbo/darwin-64@2.10.8': | |
| 803 | + resolution: {integrity: sha512-po+7rfJfUnFXjWlcoN2RwhErgzCdRtBc1T26vYPcywHlggmCQiQe1uWaE4j+BibI2uY9/2pDoFzMN0rmSaPFOw==} | |
| 804 | + cpu: [x64] | |
| 805 | + os: [darwin] | |
| 806 | + | |
| 807 | + '@turbo/darwin-arm64@2.10.8': | |
| 808 | + resolution: {integrity: sha512-+zB2btDJ00lnPRuqOvpVvgl4x34k/djZQGZTTCfjn7JgNCl8QFY5Njo5+dqkY1g/+9gbbsnAvWm9CmJg9ebcXA==} | |
| 809 | + cpu: [arm64] | |
| 810 | + os: [darwin] | |
| 811 | + | |
| 812 | + '@turbo/linux-64@2.10.8': | |
| 813 | + resolution: {integrity: sha512-K1dxqiVisyN7cViVsfQLs6xscQbYuI8aO2nbUhFURDACgEDfZRdP/b4CCxeosBJpcMfhYyiibWqJorCnvz9kKg==} | |
| 814 | + cpu: [x64] | |
| 815 | + os: [android, linux] | |
| 816 | + | |
| 817 | + '@turbo/linux-arm64@2.10.8': | |
| 818 | + resolution: {integrity: sha512-Gi77ibVnrE1fEmvr+/wBD/yvRqhwp/RQuCp2+//lv1U1wNFFyVg0V7Wj8FG9FXPFAw5QHReo8rxc9+wBSDZjzA==} | |
| 819 | + cpu: [arm64] | |
| 820 | + os: [android, linux] | |
| 821 | + | |
| 822 | + '@turbo/windows-64@2.10.8': | |
| 823 | + resolution: {integrity: sha512-znnLO1haJPYTHoKMKwlAvlkjRiYbbhBzME6wIGaMd+fwir23U6jVd1ecaTWWi1fbnRVqxMfgDBKseQ/hLKb83g==} | |
| 824 | + cpu: [x64] | |
| 825 | + os: [win32] | |
| 826 | + | |
| 827 | + '@turbo/windows-arm64@2.10.8': | |
| 828 | + resolution: {integrity: sha512-VN30vh3b3Czh2WzYHNTfF1FE0YMZ5aHsLO8dBMGHJewA6792wX6iJR8ZxlzFW6WdOu0gEAKIvlYhfyT81Wkm4Q==} | |
| 829 | + cpu: [arm64] | |
| 830 | + os: [win32] | |
| 831 | + | |
| 832 | + '@types/estree@1.0.9': | |
| 833 | + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} | |
| 834 | + | |
| 835 | + '@types/json-schema@7.0.15': | |
| 836 | + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} | |
| 837 | + | |
| 838 | + '@types/node@20.19.43': | |
| 839 | + resolution: {integrity: sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==} | |
| 840 | + | |
| 841 | + '@types/prop-types@15.7.15': | |
| 842 | + resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} | |
| 843 | + | |
| 844 | + '@types/react-dom@18.3.7': | |
| 845 | + resolution: {integrity: sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==} | |
| 846 | + peerDependencies: | |
| 847 | + '@types/react': ^18.0.0 | |
| 848 | + | |
| 849 | + '@types/react@18.3.31': | |
| 850 | + resolution: {integrity: sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==} | |
| 851 | + | |
| 852 | + '@typescript-eslint/eslint-plugin@8.66.0': | |
| 853 | + resolution: {integrity: sha512-p088eaGrzYz1s+7cov0aMOCkNGTJlVxF4jgubf28c8L0Cv9Rloj8YBHnv4hXLq6IIEE1AsjNWavO+k+8kP2Y0A==} | |
| 854 | + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} | |
| 855 | + peerDependencies: | |
| 856 | + '@typescript-eslint/parser': ^8.66.0 | |
| 857 | + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 | |
| 858 | + typescript: '>=4.8.4 <6.1.0' | |
| 859 | + | |
| 860 | + '@typescript-eslint/parser@8.66.0': | |
| 861 | + resolution: {integrity: sha512-X6ypGChaWYk6PBtUg2BwuTZEFFcHJAtGTVJ9/lCTOufhZ4i9fNolQNnktq+kkMCwMj7V8Svsq7+TxSDslmhE0g==} | |
| 862 | + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} | |
| 863 | + peerDependencies: | |
| 864 | + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 | |
| 865 | + typescript: '>=4.8.4 <6.1.0' | |
| 866 | + | |
| 867 | + '@typescript-eslint/project-service@8.66.0': | |
| 868 | + resolution: {integrity: sha512-7MthGPTt4BP69lSryqpqq8HQqxuzynssckL/jyDyk3+TNMQ3y2jFWkptCrktWvBrP+EH787Nl5N5Qpw7WZg+5g==} | |
| 869 | + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} | |
| 870 | + peerDependencies: | |
| 871 | + typescript: '>=4.8.4 <6.1.0' | |
| 872 | + | |
| 873 | + '@typescript-eslint/scope-manager@8.66.0': | |
| 874 | + resolution: {integrity: sha512-8TGcH25j9zqJ/IULB/ppyhRvxA8QYfFEZ7nfbg6/BN9spDgb8fPWQXlE5l8TWBL50EtUx007uZ1o9VOwrq2/9g==} | |
| 875 | + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} | |
| 876 | + | |
| 877 | + '@typescript-eslint/tsconfig-utils@8.66.0': | |
| 878 | + resolution: {integrity: sha512-9D5gLYZG4rOjcoag8MQ/fWI8WqA9wcPDyOGyWtWFhvM1lHRbliqUSPIY5J3zqCU1tvSwzXxnnjhQhz5Ne7mJ4g==} | |
| 879 | + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} | |
| 880 | + peerDependencies: | |
| 881 | + typescript: '>=4.8.4 <6.1.0' | |
| 882 | + | |
| 883 | + '@typescript-eslint/type-utils@8.66.0': | |
| 884 | + resolution: {integrity: sha512-LG2dWfjZQQp0ADtAu/EWJVayefGL2UEZ3CDeI44D9v3rXB/WYUqE/jpO28KrEKul5AySrmI+Zh1v6v+xW2U9+g==} | |
| 885 | + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} | |
| 886 | + peerDependencies: | |
| 887 | + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 | |
| 888 | + typescript: '>=4.8.4 <6.1.0' | |
| 889 | + | |
| 890 | + '@typescript-eslint/types@8.66.0': | |
| 891 | + resolution: {integrity: sha512-H6gcYaSDOyvL3AD/jHUtUFo2jqGgn/F6nuyuZSu0QTesxL+cP4dQoIMrODRofuJC09g64+WgZ6tE19Y1N2YIFQ==} | |
| 892 | + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} | |
| 893 | + | |
| 894 | + '@typescript-eslint/typescript-estree@8.66.0': | |
| 895 | + resolution: {integrity: sha512-8/x4INiiQb10jGgXYD7116/zQ+OL84ZIFn0za68wwFHCanT/VLbBEroWht8RV8fn0/ZCAoazHLQgwUC0UQcDfg==} | |
| 896 | + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} | |
| 897 | + peerDependencies: | |
| 898 | + typescript: '>=4.8.4 <6.1.0' | |
| 899 | + | |
| 900 | + '@typescript-eslint/utils@8.66.0': | |
| 901 | + resolution: {integrity: sha512-jasearZPolBw5NJNYGMwxzHMF83niVWmMU1VdHzG1CyfI2VS7f7nZltnKtHcg20hW+7Uo5GfK4MeDPoU3qI8EA==} | |
| 902 | + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} | |
| 903 | + peerDependencies: | |
| 904 | + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 | |
| 905 | + typescript: '>=4.8.4 <6.1.0' | |
| 906 | + | |
| 907 | + '@typescript-eslint/visitor-keys@8.66.0': | |
| 908 | + resolution: {integrity: sha512-dkKR8q+lKciskj1Y3vthHktl+3cMLWGyVUP23bRiPZ5O9BRT++4EqDDV+TVeIKBL1VXVEqrJlz8MYbcnvJcAlg==} | |
| 909 | + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} | |
| 910 | + | |
| 911 | + '@vitest/expect@2.1.9': | |
| 912 | + resolution: {integrity: sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==} | |
| 913 | + | |
| 914 | + '@vitest/mocker@2.1.9': | |
| 915 | + resolution: {integrity: sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==} | |
| 916 | + peerDependencies: | |
| 917 | + msw: ^2.4.9 | |
| 918 | + vite: ^5.0.0 | |
| 919 | + peerDependenciesMeta: | |
| 920 | + msw: | |
| 921 | + optional: true | |
| 922 | + vite: | |
| 923 | + optional: true | |
| 924 | + | |
| 925 | + '@vitest/pretty-format@2.1.9': | |
| 926 | + resolution: {integrity: sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==} | |
| 927 | + | |
| 928 | + '@vitest/runner@2.1.9': | |
| 929 | + resolution: {integrity: sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==} | |
| 930 | + | |
| 931 | + '@vitest/snapshot@2.1.9': | |
| 932 | + resolution: {integrity: sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==} | |
| 933 | + | |
| 934 | + '@vitest/spy@2.1.9': | |
| 935 | + resolution: {integrity: sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==} | |
| 936 | + | |
| 937 | + '@vitest/utils@2.1.9': | |
| 938 | + resolution: {integrity: sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==} | |
| 939 | + | |
| 940 | + acorn-jsx@5.3.2: | |
| 941 | + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} | |
| 942 | + peerDependencies: | |
| 943 | + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 | |
| 944 | + | |
| 945 | + acorn@8.18.0: | |
| 946 | + resolution: {integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==} | |
| 947 | + engines: {node: '>=0.4.0'} | |
| 948 | + hasBin: true | |
| 949 | + | |
| 950 | + ajv@6.15.0: | |
| 951 | + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} | |
| 952 | + | |
| 953 | + ansi-styles@4.3.0: | |
| 954 | + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} | |
| 955 | + engines: {node: '>=8'} | |
| 956 | + | |
| 957 | + any-promise@1.3.0: | |
| 958 | + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} | |
| 959 | + | |
| 960 | + anymatch@3.1.3: | |
| 961 | + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} | |
| 962 | + engines: {node: '>= 8'} | |
| 963 | + | |
| 964 | + arg@5.0.2: | |
| 965 | + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} | |
| 966 | + | |
| 967 | + argparse@2.0.1: | |
| 968 | + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} | |
| 969 | + | |
| 970 | + assertion-error@2.0.1: | |
| 971 | + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} | |
| 972 | + engines: {node: '>=12'} | |
| 973 | + | |
| 974 | + autoprefixer@10.5.4: | |
| 975 | + resolution: {integrity: sha512-MaU0U/za7N3r6brxD4YB/l4NSrFzLPlANv6wEuQVaIPlD3L4W9rFcQPbL/EilY9BHhHvhfcz3gInDLrEtWT4EA==} | |
| 976 | + engines: {node: ^10 || ^12 || >=14} | |
| 977 | + hasBin: true | |
| 978 | + peerDependencies: | |
| 979 | + postcss: ^8.1.0 | |
| 980 | + | |
| 981 | + balanced-match@1.0.2: | |
| 982 | + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} | |
| 983 | + | |
| 984 | + balanced-match@4.0.4: | |
| 985 | + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} | |
| 986 | + engines: {node: 18 || 20 || >=22} | |
| 987 | + | |
| 988 | + baseline-browser-mapping@2.11.12: | |
| 989 | + resolution: {integrity: sha512-r7WnVImvVCeFpf2DOXfy41aPWzeNg3H/A2X4dKmy1QL0MSyyk/e7z8ihJ3N6Nn2PsdhkVlqnEfnUE4a05P2aTA==} | |
| 990 | + engines: {node: '>=6.0.0'} | |
| 991 | + hasBin: true | |
| 992 | + | |
| 993 | + binary-extensions@2.3.0: | |
| 994 | + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} | |
| 995 | + engines: {node: '>=8'} | |
| 996 | + | |
| 997 | + brace-expansion@1.1.18: | |
| 998 | + resolution: {integrity: sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==} | |
| 999 | + | |
| 1000 | + brace-expansion@5.0.9: | |
| 1001 | + resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==} | |
| 1002 | + engines: {node: 20 || >=22} | |
| 1003 | + | |
| 1004 | + braces@3.0.3: | |
| 1005 | + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} | |
| 1006 | + engines: {node: '>=8'} | |
| 1007 | + | |
| 1008 | + browserslist@4.28.7: | |
| 1009 | + resolution: {integrity: sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==} | |
| 1010 | + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} | |
| 1011 | + hasBin: true | |
| 1012 | + | |
| 1013 | + bullmq@5.81.3: | |
| 1014 | + resolution: {integrity: sha512-Q7uEH2G92rVjX3Yl2qw3+6VEO15uxehC6DfeTHQBQTehddcRDSDuquD9zuvDjxiCffXsPUaBwrRlvlbVcgKs7g==} | |
| 1015 | + engines: {node: '>=12.22.0'} | |
| 1016 | + peerDependencies: | |
| 1017 | + redis: '>=5.0.0' | |
| 1018 | + peerDependenciesMeta: | |
| 1019 | + redis: | |
| 1020 | + optional: true | |
| 1021 | + | |
| 1022 | + busboy@1.6.0: | |
| 1023 | + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} | |
| 1024 | + engines: {node: '>=10.16.0'} | |
| 1025 | + | |
| 1026 | + cac@6.7.14: | |
| 1027 | + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} | |
| 1028 | + engines: {node: '>=8'} | |
| 1029 | + | |
| 1030 | + callsites@3.1.0: | |
| 1031 | + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} | |
| 1032 | + engines: {node: '>=6'} | |
| 1033 | + | |
| 1034 | + camelcase-css@2.0.1: | |
| 1035 | + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} | |
| 1036 | + engines: {node: '>= 6'} | |
| 1037 | + | |
| 1038 | + caniuse-lite@1.0.30001806: | |
| 1039 | + resolution: {integrity: sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==} | |
| 1040 | + | |
| 1041 | + chai@5.3.3: | |
| 1042 | + resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} | |
| 1043 | + engines: {node: '>=18'} | |
| 1044 | + | |
| 1045 | + chalk@4.1.2: | |
| 1046 | + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} | |
| 1047 | + engines: {node: '>=10'} | |
| 1048 | + | |
| 1049 | + check-error@2.1.3: | |
| 1050 | + resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} | |
| 1051 | + engines: {node: '>= 16'} | |
| 1052 | + | |
| 1053 | + chokidar@3.6.0: | |
| 1054 | + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} | |
| 1055 | + engines: {node: '>= 8.10.0'} | |
| 1056 | + | |
| 1057 | + client-only@0.0.1: | |
| 1058 | + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} | |
| 1059 | + | |
| 1060 | + cluster-key-slot@1.1.1: | |
| 1061 | + resolution: {integrity: sha512-rwHwUfXL40Chm1r08yrhU3qpUvdVlgkKNeyeGPOxnW8/SyVDvgRaed/Uz54AqWNaTCAThlj6QAs3TZcKI0xDEw==} | |
| 1062 | + engines: {node: '>=0.10.0'} | |
| 1063 | + | |
| 1064 | + color-convert@2.0.1: | |
| 1065 | + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} | |
| 1066 | + engines: {node: '>=7.0.0'} | |
| 1067 | + | |
| 1068 | + color-name@1.1.4: | |
| 1069 | + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} | |
| 1070 | + | |
| 1071 | + commander@4.1.1: | |
| 1072 | + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} | |
| 1073 | + engines: {node: '>= 6'} | |
| 1074 | + | |
| 1075 | + concat-map@0.0.1: | |
| 1076 | + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} | |
| 1077 | + | |
| 1078 | + cron-parser@4.9.0: | |
| 1079 | + resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==} | |
| 1080 | + engines: {node: '>=12.0.0'} | |
| 1081 | + deprecated: v4 is no longer maintained, upgrade to v5 | |
| 1082 | + | |
| 1083 | + cross-spawn@7.0.6: | |
| 1084 | + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} | |
| 1085 | + engines: {node: '>= 8'} | |
| 1086 | + | |
| 1087 | + cssesc@3.0.0: | |
| 1088 | + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} | |
| 1089 | + engines: {node: '>=4'} | |
| 1090 | + hasBin: true | |
| 1091 | + | |
| 1092 | + csstype@3.2.3: | |
| 1093 | + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} | |
| 1094 | + | |
| 1095 | + debug@4.4.3: | |
| 1096 | + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} | |
| 1097 | + engines: {node: '>=6.0'} | |
| 1098 | + peerDependencies: | |
| 1099 | + supports-color: '*' | |
| 1100 | + peerDependenciesMeta: | |
| 1101 | + supports-color: | |
| 1102 | + optional: true | |
| 1103 | + | |
| 1104 | + deep-eql@5.0.2: | |
| 1105 | + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} | |
| 1106 | + engines: {node: '>=6'} | |
| 1107 | + | |
| 1108 | + deep-is@0.1.4: | |
| 1109 | + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} | |
| 1110 | + | |
| 1111 | + denque@2.1.0: | |
| 1112 | + resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} | |
| 1113 | + engines: {node: '>=0.10'} | |
| 1114 | + | |
| 1115 | + detect-libc@2.1.2: | |
| 1116 | + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} | |
| 1117 | + engines: {node: '>=8'} | |
| 1118 | + | |
| 1119 | + didyoumean@1.2.2: | |
| 1120 | + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} | |
| 1121 | + | |
| 1122 | + dlv@1.1.3: | |
| 1123 | + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} | |
| 1124 | + | |
| 1125 | + electron-to-chromium@1.5.401: | |
| 1126 | + resolution: {integrity: sha512-H6ViHN68nGYlChEvlIU67fn8O2/tpbWQPwck98yaJmh+08LSvHiydzDQ6oXNccLU3kNRVIRS9A4mA7CG+i6fLQ==} | |
| 1127 | + | |
| 1128 | + es-errors@1.3.0: | |
| 1129 | + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} | |
| 1130 | + engines: {node: '>= 0.4'} | |
| 1131 | + | |
| 1132 | + es-module-lexer@1.7.0: | |
| 1133 | + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} | |
| 1134 | + | |
| 1135 | + esbuild@0.21.5: | |
| 1136 | + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} | |
| 1137 | + engines: {node: '>=12'} | |
| 1138 | + hasBin: true | |
| 1139 | + | |
| 1140 | + esbuild@0.28.1: | |
| 1141 | + resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} | |
| 1142 | + engines: {node: '>=18'} | |
| 1143 | + hasBin: true | |
| 1144 | + | |
| 1145 | + escalade@3.2.0: | |
| 1146 | + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} | |
| 1147 | + engines: {node: '>=6'} | |
| 1148 | + | |
| 1149 | + escape-string-regexp@4.0.0: | |
| 1150 | + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} | |
| 1151 | + engines: {node: '>=10'} | |
| 1152 | + | |
| 1153 | + eslint-scope@8.4.0: | |
| 1154 | + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} | |
| 1155 | + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} | |
| 1156 | + | |
| 1157 | + eslint-visitor-keys@3.4.3: | |
| 1158 | + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} | |
| 1159 | + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} | |
| 1160 | + | |
| 1161 | + eslint-visitor-keys@4.2.1: | |
| 1162 | + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} | |
| 1163 | + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} | |
| 1164 | + | |
| 1165 | + eslint-visitor-keys@5.0.1: | |
| 1166 | + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} | |
| 1167 | + engines: {node: ^20.19.0 || ^22.13.0 || >=24} | |
| 1168 | + | |
| 1169 | + eslint@9.39.5: | |
| 1170 | + resolution: {integrity: sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==} | |
| 1171 | + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} | |
| 1172 | + hasBin: true | |
| 1173 | + peerDependencies: | |
| 1174 | + jiti: '*' | |
| 1175 | + peerDependenciesMeta: | |
| 1176 | + jiti: | |
| 1177 | + optional: true | |
| 1178 | + | |
| 1179 | + espree@10.4.0: | |
| 1180 | + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} | |
| 1181 | + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} | |
| 1182 | + | |
| 1183 | + esquery@1.7.0: | |
| 1184 | + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} | |
| 1185 | + engines: {node: '>=0.10'} | |
| 1186 | + | |
| 1187 | + esrecurse@4.3.0: | |
| 1188 | + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} | |
| 1189 | + engines: {node: '>=4.0'} | |
| 1190 | + | |
| 1191 | + estraverse@5.3.0: | |
| 1192 | + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} | |
| 1193 | + engines: {node: '>=4.0'} | |
| 1194 | + | |
| 1195 | + estree-walker@3.0.3: | |
| 1196 | + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} | |
| 1197 | + | |
| 1198 | + esutils@2.0.3: | |
| 1199 | + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} | |
| 1200 | + engines: {node: '>=0.10.0'} | |
| 1201 | + | |
| 1202 | + expect-type@1.4.0: | |
| 1203 | + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} | |
| 1204 | + engines: {node: '>=12.0.0'} | |
| 1205 | + | |
| 1206 | + fast-check@3.23.2: | |
| 1207 | + resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==} | |
| 1208 | + engines: {node: '>=8.0.0'} | |
| 1209 | + | |
| 1210 | + fast-deep-equal@3.1.3: | |
| 1211 | + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} | |
| 1212 | + | |
| 1213 | + fast-glob@3.3.3: | |
| 1214 | + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} | |
| 1215 | + engines: {node: '>=8.6.0'} | |
| 1216 | + | |
| 1217 | + fast-json-stable-stringify@2.1.0: | |
| 1218 | + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} | |
| 1219 | + | |
| 1220 | + fast-levenshtein@2.0.6: | |
| 1221 | + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} | |
| 1222 | + | |
| 1223 | + fast-sha256@1.3.0: | |
| 1224 | + resolution: {integrity: sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==} | |
| 1225 | + | |
| 1226 | + fastq@1.20.1: | |
| 1227 | + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} | |
| 1228 | + | |
| 1229 | + fdir@6.5.0: | |
| 1230 | + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} | |
| 1231 | + engines: {node: '>=12.0.0'} | |
| 1232 | + peerDependencies: | |
| 1233 | + picomatch: ^3 || ^4 | |
| 1234 | + peerDependenciesMeta: | |
| 1235 | + picomatch: | |
| 1236 | + optional: true | |
| 1237 | + | |
| 1238 | + file-entry-cache@8.0.0: | |
| 1239 | + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} | |
| 1240 | + engines: {node: '>=16.0.0'} | |
| 1241 | + | |
| 1242 | + fill-range@7.1.1: | |
| 1243 | + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} | |
| 1244 | + engines: {node: '>=8'} | |
| 1245 | + | |
| 1246 | + find-up@5.0.0: | |
| 1247 | + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} | |
| 1248 | + engines: {node: '>=10'} | |
| 1249 | + | |
| 1250 | + flat-cache@4.0.1: | |
| 1251 | + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} | |
| 1252 | + engines: {node: '>=16'} | |
| 1253 | + | |
| 1254 | + flatted@3.4.4: | |
| 1255 | + resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==} | |
| 1256 | + | |
| 1257 | + fraction.js@5.3.4: | |
| 1258 | + resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} | |
| 1259 | + | |
| 1260 | + fsevents@2.3.3: | |
| 1261 | + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} | |
| 1262 | + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} | |
| 1263 | + os: [darwin] | |
| 1264 | + | |
| 1265 | + function-bind@1.1.2: | |
| 1266 | + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} | |
| 1267 | + | |
| 1268 | + glob-parent@5.1.2: | |
| 1269 | + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} | |
| 1270 | + engines: {node: '>= 6'} | |
| 1271 | + | |
| 1272 | + glob-parent@6.0.2: | |
| 1273 | + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} | |
| 1274 | + engines: {node: '>=10.13.0'} | |
| 1275 | + | |
| 1276 | + globals@14.0.0: | |
| 1277 | + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} | |
| 1278 | + engines: {node: '>=18'} | |
| 1279 | + | |
| 1280 | + graceful-fs@4.2.11: | |
| 1281 | + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} | |
| 1282 | + | |
| 1283 | + has-flag@4.0.0: | |
| 1284 | + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} | |
| 1285 | + engines: {node: '>=8'} | |
| 1286 | + | |
| 1287 | + hasown@2.0.4: | |
| 1288 | + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} | |
| 1289 | + engines: {node: '>= 0.4'} | |
| 1290 | + | |
| 1291 | + ignore@5.3.2: | |
| 1292 | + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} | |
| 1293 | + engines: {node: '>= 4'} | |
| 1294 | + | |
| 1295 | + ignore@7.0.6: | |
| 1296 | + resolution: {integrity: sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==} | |
| 1297 | + engines: {node: '>= 4'} | |
| 1298 | + | |
| 1299 | + import-fresh@3.3.1: | |
| 1300 | + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} | |
| 1301 | + engines: {node: '>=6'} | |
| 1302 | + | |
| 1303 | + imurmurhash@0.1.4: | |
| 1304 | + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} | |
| 1305 | + engines: {node: '>=0.8.19'} | |
| 1306 | + | |
| 1307 | + ioredis@5.11.1: | |
| 1308 | + resolution: {integrity: sha512-ehuGcf94bQXhfagULNXrJdfnWO38v070jxSx/qE87Kjzmu2fU7ro5EFAb+OPituLqgfyuQaym5DlrNydW2sJ9A==} | |
| 1309 | + engines: {node: '>=12.22.0'} | |
| 1310 | + | |
| 1311 | + is-binary-path@2.1.0: | |
| 1312 | + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} | |
| 1313 | + engines: {node: '>=8'} | |
| 1314 | + | |
| 1315 | + is-core-module@2.16.2: | |
| 1316 | + resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==} | |
| 1317 | + engines: {node: '>= 0.4'} | |
| 1318 | + | |
| 1319 | + is-extglob@2.1.1: | |
| 1320 | + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} | |
| 1321 | + engines: {node: '>=0.10.0'} | |
| 1322 | + | |
| 1323 | + is-glob@4.0.3: | |
| 1324 | + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} | |
| 1325 | + engines: {node: '>=0.10.0'} | |
| 1326 | + | |
| 1327 | + is-number@7.0.0: | |
| 1328 | + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} | |
| 1329 | + engines: {node: '>=0.12.0'} | |
| 1330 | + | |
| 1331 | + isexe@2.0.0: | |
| 1332 | + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} | |
| 1333 | + | |
| 1334 | + jiti@1.21.7: | |
| 1335 | + resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} | |
| 1336 | + hasBin: true | |
| 1337 | + | |
| 1338 | + js-tokens@4.0.0: | |
| 1339 | + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} | |
| 1340 | + | |
| 1341 | + js-yaml@4.3.1: | |
| 1342 | + resolution: {integrity: sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==} | |
| 1343 | + hasBin: true | |
| 1344 | + | |
| 1345 | + json-buffer@3.0.1: | |
| 1346 | + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} | |
| 1347 | + | |
| 1348 | + json-schema-to-ts@3.1.1: | |
| 1349 | + resolution: {integrity: sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==} | |
| 1350 | + engines: {node: '>=16'} | |
| 1351 | + | |
| 1352 | + json-schema-traverse@0.4.1: | |
| 1353 | + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} | |
| 1354 | + | |
| 1355 | + json-stable-stringify-without-jsonify@1.0.1: | |
| 1356 | + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} | |
| 1357 | + | |
| 1358 | + keyv@4.5.4: | |
| 1359 | + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} | |
| 1360 | + | |
| 1361 | + levn@0.4.1: | |
| 1362 | + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} | |
| 1363 | + engines: {node: '>= 0.8.0'} | |
| 1364 | + | |
| 1365 | + lilconfig@3.1.3: | |
| 1366 | + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} | |
| 1367 | + engines: {node: '>=14'} | |
| 1368 | + | |
| 1369 | + lines-and-columns@1.2.4: | |
| 1370 | + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} | |
| 1371 | + | |
| 1372 | + locate-path@6.0.0: | |
| 1373 | + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} | |
| 1374 | + engines: {node: '>=10'} | |
| 1375 | + | |
| 1376 | + lodash.merge@4.6.2: | |
| 1377 | + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} | |
| 1378 | + | |
| 1379 | + loose-envify@1.4.0: | |
| 1380 | + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} | |
| 1381 | + hasBin: true | |
| 1382 | + | |
| 1383 | + loupe@3.2.1: | |
| 1384 | + resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} | |
| 1385 | + | |
| 1386 | + luxon@3.7.2: | |
| 1387 | + resolution: {integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==} | |
| 1388 | + engines: {node: '>=12'} | |
| 1389 | + | |
| 1390 | + magic-string@0.30.21: | |
| 1391 | + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} | |
| 1392 | + | |
| 1393 | + merge2@1.4.1: | |
| 1394 | + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} | |
| 1395 | + engines: {node: '>= 8'} | |
| 1396 | + | |
| 1397 | + micromatch@4.0.8: | |
| 1398 | + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} | |
| 1399 | + engines: {node: '>=8.6'} | |
| 1400 | + | |
| 1401 | + minimatch@10.2.6: | |
| 1402 | + resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} | |
| 1403 | + engines: {node: 18 || 20 || >=22} | |
| 1404 | + | |
| 1405 | + minimatch@3.1.5: | |
| 1406 | + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} | |
| 1407 | + | |
| 1408 | + ms@2.1.3: | |
| 1409 | + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} | |
| 1410 | + | |
| 1411 | + msgpackr-extract@3.0.4: | |
| 1412 | + resolution: {integrity: sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw==} | |
| 1413 | + hasBin: true | |
| 1414 | + | |
| 1415 | + msgpackr@2.0.5: | |
| 1416 | + resolution: {integrity: sha512-cef05H/dSYpLpqp3sj/qyZh5vhUYCalnaLO7j1yOmpsR0y/XwLVtK7r5gn+U/F7CTEfMowcGhlUQJDLcLf7jcA==} | |
| 1417 | + | |
| 1418 | + mz@2.7.0: | |
| 1419 | + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} | |
| 1420 | + | |
| 1421 | + nanoid@3.3.17: | |
| 1422 | + resolution: {integrity: sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g==} | |
| 1423 | + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} | |
| 1424 | + hasBin: true | |
| 1425 | + | |
| 1426 | + natural-compare@1.4.0: | |
| 1427 | + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} | |
| 1428 | + | |
| 1429 | + next@14.2.35: | |
| 1430 | + resolution: {integrity: sha512-KhYd2Hjt/O1/1aZVX3dCwGXM1QmOV4eNM2UTacK5gipDdPN/oHHK/4oVGy7X8GMfPMsUTUEmGlsy0EY1YGAkig==} | |
| 1431 | + engines: {node: '>=18.17.0'} | |
| 1432 | + hasBin: true | |
| 1433 | + peerDependencies: | |
| 1434 | + '@opentelemetry/api': ^1.1.0 | |
| 1435 | + '@playwright/test': ^1.41.2 | |
| 1436 | + react: ^18.2.0 | |
| 1437 | + react-dom: ^18.2.0 | |
| 1438 | + sass: ^1.3.0 | |
| 1439 | + peerDependenciesMeta: | |
| 1440 | + '@opentelemetry/api': | |
| 1441 | + optional: true | |
| 1442 | + '@playwright/test': | |
| 1443 | + optional: true | |
| 1444 | + sass: | |
| 1445 | + optional: true | |
| 1446 | + | |
| 1447 | + node-abort-controller@3.1.1: | |
| 1448 | + resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} | |
| 1449 | + | |
| 1450 | + node-gyp-build-optional-packages@5.2.2: | |
| 1451 | + resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==} | |
| 1452 | + hasBin: true | |
| 1453 | + | |
| 1454 | + node-releases@2.0.52: | |
| 1455 | + resolution: {integrity: sha512-MRlTqhAfoMx/4mhEbPo3Hi02g9LJZaJkka69V6h67Cb1gjrAG0jsTE4CZX1eptNx+VCAwJmfpnDIF4P0Nh1A7A==} | |
| 1456 | + engines: {node: '>=18'} | |
| 1457 | + | |
| 1458 | + normalize-path@3.0.0: | |
| 1459 | + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} | |
| 1460 | + engines: {node: '>=0.10.0'} | |
| 1461 | + | |
| 1462 | + object-assign@4.1.1: | |
| 1463 | + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} | |
| 1464 | + engines: {node: '>=0.10.0'} | |
| 1465 | + | |
| 1466 | + object-hash@3.0.0: | |
| 1467 | + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} | |
| 1468 | + engines: {node: '>= 6'} | |
| 1469 | + | |
| 1470 | + optionator@0.9.4: | |
| 1471 | + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} | |
| 1472 | + engines: {node: '>= 0.8.0'} | |
| 1473 | + | |
| 1474 | + p-limit@3.1.0: | |
| 1475 | + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} | |
| 1476 | + engines: {node: '>=10'} | |
| 1477 | + | |
| 1478 | + p-locate@5.0.0: | |
| 1479 | + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} | |
| 1480 | + engines: {node: '>=10'} | |
| 1481 | + | |
| 1482 | + parent-module@1.0.1: | |
| 1483 | + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} | |
| 1484 | + engines: {node: '>=6'} | |
| 1485 | + | |
| 1486 | + path-exists@4.0.0: | |
| 1487 | + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} | |
| 1488 | + engines: {node: '>=8'} | |
| 1489 | + | |
| 1490 | + path-key@3.1.1: | |
| 1491 | + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} | |
| 1492 | + engines: {node: '>=8'} | |
| 1493 | + | |
| 1494 | + path-parse@1.0.7: | |
| 1495 | + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} | |
| 1496 | + | |
| 1497 | + pathe@1.1.2: | |
| 1498 | + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} | |
| 1499 | + | |
| 1500 | + pathval@2.0.1: | |
| 1501 | + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} | |
| 1502 | + engines: {node: '>= 14.16'} | |
| 1503 | + | |
| 1504 | + picocolors@1.1.1: | |
| 1505 | + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} | |
| 1506 | + | |
| 1507 | + picomatch@2.3.2: | |
| 1508 | + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} | |
| 1509 | + engines: {node: '>=8.6'} | |
| 1510 | + | |
| 1511 | + picomatch@4.0.5: | |
| 1512 | + resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} | |
| 1513 | + engines: {node: '>=12'} | |
| 1514 | + | |
| 1515 | + pify@2.3.0: | |
| 1516 | + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} | |
| 1517 | + engines: {node: '>=0.10.0'} | |
| 1518 | + | |
| 1519 | + pirates@4.0.7: | |
| 1520 | + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} | |
| 1521 | + engines: {node: '>= 6'} | |
| 1522 | + | |
| 1523 | + postcss-import@15.1.0: | |
| 1524 | + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} | |
| 1525 | + engines: {node: '>=14.0.0'} | |
| 1526 | + peerDependencies: | |
| 1527 | + postcss: ^8.0.0 | |
| 1528 | + | |
| 1529 | + postcss-js@4.1.0: | |
| 1530 | + resolution: {integrity: sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==} | |
| 1531 | + engines: {node: ^12 || ^14 || >= 16} | |
| 1532 | + peerDependencies: | |
| 1533 | + postcss: ^8.4.21 | |
| 1534 | + | |
| 1535 | + postcss-load-config@6.0.1: | |
| 1536 | + resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} | |
| 1537 | + engines: {node: '>= 18'} | |
| 1538 | + peerDependencies: | |
| 1539 | + jiti: '>=1.21.0' | |
| 1540 | + postcss: '>=8.0.9' | |
| 1541 | + tsx: ^4.8.1 | |
| 1542 | + yaml: ^2.4.2 | |
| 1543 | + peerDependenciesMeta: | |
| 1544 | + jiti: | |
| 1545 | + optional: true | |
| 1546 | + postcss: | |
| 1547 | + optional: true | |
| 1548 | + tsx: | |
| 1549 | + optional: true | |
| 1550 | + yaml: | |
| 1551 | + optional: true | |
| 1552 | + | |
| 1553 | + postcss-nested@6.2.0: | |
| 1554 | + resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} | |
| 1555 | + engines: {node: '>=12.0'} | |
| 1556 | + peerDependencies: | |
| 1557 | + postcss: ^8.2.14 | |
| 1558 | + | |
| 1559 | + postcss-selector-parser@6.1.4: | |
| 1560 | + resolution: {integrity: sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==} | |
| 1561 | + engines: {node: '>=4'} | |
| 1562 | + | |
| 1563 | + postcss-value-parser@4.2.0: | |
| 1564 | + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} | |
| 1565 | + | |
| 1566 | + postcss@8.4.31: | |
| 1567 | + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} | |
| 1568 | + engines: {node: ^10 || ^12 || >=14} | |
| 1569 | + | |
| 1570 | + postcss@8.5.25: | |
| 1571 | + resolution: {integrity: sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==} | |
| 1572 | + engines: {node: ^10 || ^12 || >=14} | |
| 1573 | + | |
| 1574 | + prelude-ls@1.2.1: | |
| 1575 | + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} | |
| 1576 | + engines: {node: '>= 0.8.0'} | |
| 1577 | + | |
| 1578 | + prettier@3.9.6: | |
| 1579 | + resolution: {integrity: sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==} | |
| 1580 | + engines: {node: '>=14'} | |
| 1581 | + hasBin: true | |
| 1582 | + | |
| 1583 | + prisma@5.22.0: | |
| 1584 | + resolution: {integrity: sha512-vtpjW3XuYCSnMsNVBjLMNkTj6OZbudcPPTPYHqX0CJfpcdWciI1dM8uHETwmDxxiqEwCIE6WvXucWUetJgfu/A==} | |
| 1585 | + engines: {node: '>=16.13'} | |
| 1586 | + hasBin: true | |
| 1587 | + | |
| 1588 | + punycode@2.3.1: | |
| 1589 | + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} | |
| 1590 | + engines: {node: '>=6'} | |
| 1591 | + | |
| 1592 | + pure-rand@6.1.0: | |
| 1593 | + resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} | |
| 1594 | + | |
| 1595 | + queue-microtask@1.2.3: | |
| 1596 | + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} | |
| 1597 | + | |
| 1598 | + react-dom@18.3.1: | |
| 1599 | + resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} | |
| 1600 | + peerDependencies: | |
| 1601 | + react: ^18.3.1 | |
| 1602 | + | |
| 1603 | + react@18.3.1: | |
| 1604 | + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} | |
| 1605 | + engines: {node: '>=0.10.0'} | |
| 1606 | + | |
| 1607 | + read-cache@1.0.0: | |
| 1608 | + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} | |
| 1609 | + | |
| 1610 | + readdirp@3.6.0: | |
| 1611 | + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} | |
| 1612 | + engines: {node: '>=8.10.0'} | |
| 1613 | + | |
| 1614 | + redis-errors@1.2.0: | |
| 1615 | + resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==} | |
| 1616 | + engines: {node: '>=4'} | |
| 1617 | + | |
| 1618 | + redis-parser@3.0.0: | |
| 1619 | + resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} | |
| 1620 | + engines: {node: '>=4'} | |
| 1621 | + | |
| 1622 | + resolve-from@4.0.0: | |
| 1623 | + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} | |
| 1624 | + engines: {node: '>=4'} | |
| 1625 | + | |
| 1626 | + resolve@1.22.12: | |
| 1627 | + resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} | |
| 1628 | + engines: {node: '>= 0.4'} | |
| 1629 | + hasBin: true | |
| 1630 | + | |
| 1631 | + reusify@1.1.0: | |
| 1632 | + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} | |
| 1633 | + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} | |
| 1634 | + | |
| 1635 | + rollup@4.62.4: | |
| 1636 | + resolution: {integrity: sha512-RXOqwaPsBGjMNMa4sQjDjHieHEZDFoj/Rdr46l2MU5DfEs16wHJPC2RPTPHWhNl+M3aI472LLqFkFKut4SblOg==} | |
| 1637 | + engines: {node: '>=18.0.0', npm: '>=8.0.0'} | |
| 1638 | + hasBin: true | |
| 1639 | + | |
| 1640 | + run-parallel@1.2.0: | |
| 1641 | + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} | |
| 1642 | + | |
| 1643 | + scheduler@0.23.2: | |
| 1644 | + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} | |
| 1645 | + | |
| 1646 | + semver@7.8.5: | |
| 1647 | + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} | |
| 1648 | + engines: {node: '>=10'} | |
| 1649 | + hasBin: true | |
| 1650 | + | |
| 1651 | + shebang-command@2.0.0: | |
| 1652 | + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} | |
| 1653 | + engines: {node: '>=8'} | |
| 1654 | + | |
| 1655 | + shebang-regex@3.0.0: | |
| 1656 | + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} | |
| 1657 | + engines: {node: '>=8'} | |
| 1658 | + | |
| 1659 | + siginfo@2.0.0: | |
| 1660 | + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} | |
| 1661 | + | |
| 1662 | + source-map-js@1.2.1: | |
| 1663 | + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} | |
| 1664 | + engines: {node: '>=0.10.0'} | |
| 1665 | + | |
| 1666 | + stackback@0.0.2: | |
| 1667 | + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} | |
| 1668 | + | |
| 1669 | + standard-as-callback@2.1.0: | |
| 1670 | + resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} | |
| 1671 | + | |
| 1672 | + standardwebhooks@1.0.0: | |
| 1673 | + resolution: {integrity: sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==} | |
| 1674 | + | |
| 1675 | + std-env@3.10.0: | |
| 1676 | + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} | |
| 1677 | + | |
| 1678 | + streamsearch@1.1.0: | |
| 1679 | + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} | |
| 1680 | + engines: {node: '>=10.0.0'} | |
| 1681 | + | |
| 1682 | + strip-json-comments@3.1.1: | |
| 1683 | + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} | |
| 1684 | + engines: {node: '>=8'} | |
| 1685 | + | |
| 1686 | + styled-jsx@5.1.1: | |
| 1687 | + resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} | |
| 1688 | + engines: {node: '>= 12.0.0'} | |
| 1689 | + peerDependencies: | |
| 1690 | + '@babel/core': '*' | |
| 1691 | + babel-plugin-macros: '*' | |
| 1692 | + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0' | |
| 1693 | + peerDependenciesMeta: | |
| 1694 | + '@babel/core': | |
| 1695 | + optional: true | |
| 1696 | + babel-plugin-macros: | |
| 1697 | + optional: true | |
| 1698 | + | |
| 1699 | + sucrase@3.35.1: | |
| 1700 | + resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} | |
| 1701 | + engines: {node: '>=16 || 14 >=14.17'} | |
| 1702 | + hasBin: true | |
| 1703 | + | |
| 1704 | + supports-color@7.2.0: | |
| 1705 | + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} | |
| 1706 | + engines: {node: '>=8'} | |
| 1707 | + | |
| 1708 | + supports-preserve-symlinks-flag@1.0.0: | |
| 1709 | + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} | |
| 1710 | + engines: {node: '>= 0.4'} | |
| 1711 | + | |
| 1712 | + tailwindcss@3.4.19: | |
| 1713 | + resolution: {integrity: sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==} | |
| 1714 | + engines: {node: '>=14.0.0'} | |
| 1715 | + hasBin: true | |
| 1716 | + | |
| 1717 | + thenify-all@1.6.0: | |
| 1718 | + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} | |
| 1719 | + engines: {node: '>=0.8'} | |
| 1720 | + | |
| 1721 | + thenify@3.3.1: | |
| 1722 | + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} | |
| 1723 | + | |
| 1724 | + tinybench@2.9.0: | |
| 1725 | + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} | |
| 1726 | + | |
| 1727 | + tinyexec@0.3.2: | |
| 1728 | + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} | |
| 1729 | + | |
| 1730 | + tinyglobby@0.2.17: | |
| 1731 | + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} | |
| 1732 | + engines: {node: '>=12.0.0'} | |
| 1733 | + | |
| 1734 | + tinypool@1.1.1: | |
| 1735 | + resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} | |
| 1736 | + engines: {node: ^18.0.0 || >=20.0.0} | |
| 1737 | + | |
| 1738 | + tinyrainbow@1.2.0: | |
| 1739 | + resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} | |
| 1740 | + engines: {node: '>=14.0.0'} | |
| 1741 | + | |
| 1742 | + tinyspy@3.0.2: | |
| 1743 | + resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} | |
| 1744 | + engines: {node: '>=14.0.0'} | |
| 1745 | + | |
| 1746 | + to-regex-range@5.0.1: | |
| 1747 | + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} | |
| 1748 | + engines: {node: '>=8.0'} | |
| 1749 | + | |
| 1750 | + ts-algebra@2.0.0: | |
| 1751 | + resolution: {integrity: sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==} | |
| 1752 | + | |
| 1753 | + ts-api-utils@2.5.0: | |
| 1754 | + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} | |
| 1755 | + engines: {node: '>=18.12'} | |
| 1756 | + peerDependencies: | |
| 1757 | + typescript: '>=4.8.4' | |
| 1758 | + | |
| 1759 | + ts-interface-checker@0.1.13: | |
| 1760 | + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} | |
| 1761 | + | |
| 1762 | + tslib@2.8.1: | |
| 1763 | + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} | |
| 1764 | + | |
| 1765 | + tsx@4.23.6: | |
| 1766 | + resolution: {integrity: sha512-D/YYGUDqKlLvXhM5fBBbiENaGICxLfU4viHnZEkgmgplnDFa+Kczy34VV7AmLJgdzisv0I/J3zitfC26JH3GXg==} | |
| 1767 | + engines: {node: '>=18.0.0'} | |
| 1768 | + hasBin: true | |
| 1769 | + | |
| 1770 | + turbo@2.10.8: | |
| 1771 | + resolution: {integrity: sha512-9+8YX5QOkGXzZxcIykTHgaooRHGMWO+jfdyRK0o+rN0U7hBIig2MrJ8r/aNzIPDPhdA73SGb0O+tIztaModTMg==} | |
| 1772 | + hasBin: true | |
| 1773 | + | |
| 1774 | + type-check@0.4.0: | |
| 1775 | + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} | |
| 1776 | + engines: {node: '>= 0.8.0'} | |
| 1777 | + | |
| 1778 | + typescript-eslint@8.66.0: | |
| 1779 | + resolution: {integrity: sha512-QlEbBPz/RuJ1XUHj29nm3t0F/O/cSlEnntozqPOYHnnTGAXFamnMBu5i9Vn6vhUPHGAjR+Vl+5J8vPN/BMUrJw==} | |
| 1780 | + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} | |
| 1781 | + peerDependencies: | |
| 1782 | + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 | |
| 1783 | + typescript: '>=4.8.4 <6.1.0' | |
| 1784 | + | |
| 1785 | + typescript@5.9.3: | |
| 1786 | + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} | |
| 1787 | + engines: {node: '>=14.17'} | |
| 1788 | + hasBin: true | |
| 1789 | + | |
| 1790 | + undici-types@6.21.0: | |
| 1791 | + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} | |
| 1792 | + | |
| 1793 | + update-browserslist-db@1.2.3: | |
| 1794 | + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} | |
| 1795 | + hasBin: true | |
| 1796 | + peerDependencies: | |
| 1797 | + browserslist: '>= 4.21.0' | |
| 1798 | + | |
| 1799 | + uri-js@4.4.1: | |
| 1800 | + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} | |
| 1801 | + | |
| 1802 | + util-deprecate@1.0.2: | |
| 1803 | + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} | |
| 1804 | + | |
| 1805 | + vite-node@2.1.9: | |
| 1806 | + resolution: {integrity: sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==} | |
| 1807 | + engines: {node: ^18.0.0 || >=20.0.0} | |
| 1808 | + hasBin: true | |
| 1809 | + | |
| 1810 | + vite@5.4.21: | |
| 1811 | + resolution: {integrity: sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==} | |
| 1812 | + engines: {node: ^18.0.0 || >=20.0.0} | |
| 1813 | + hasBin: true | |
| 1814 | + peerDependencies: | |
| 1815 | + '@types/node': ^18.0.0 || >=20.0.0 | |
| 1816 | + less: '*' | |
| 1817 | + lightningcss: ^1.21.0 | |
| 1818 | + sass: '*' | |
| 1819 | + sass-embedded: '*' | |
| 1820 | + stylus: '*' | |
| 1821 | + sugarss: '*' | |
| 1822 | + terser: ^5.4.0 | |
| 1823 | + peerDependenciesMeta: | |
| 1824 | + '@types/node': | |
| 1825 | + optional: true | |
| 1826 | + less: | |
| 1827 | + optional: true | |
| 1828 | + lightningcss: | |
| 1829 | + optional: true | |
| 1830 | + sass: | |
| 1831 | + optional: true | |
| 1832 | + sass-embedded: | |
| 1833 | + optional: true | |
| 1834 | + stylus: | |
| 1835 | + optional: true | |
| 1836 | + sugarss: | |
| 1837 | + optional: true | |
| 1838 | + terser: | |
| 1839 | + optional: true | |
| 1840 | + | |
| 1841 | + vitest@2.1.9: | |
| 1842 | + resolution: {integrity: sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==} | |
| 1843 | + engines: {node: ^18.0.0 || >=20.0.0} | |
| 1844 | + hasBin: true | |
| 1845 | + peerDependencies: | |
| 1846 | + '@edge-runtime/vm': '*' | |
| 1847 | + '@types/node': ^18.0.0 || >=20.0.0 | |
| 1848 | + '@vitest/browser': 2.1.9 | |
| 1849 | + '@vitest/ui': 2.1.9 | |
| 1850 | + happy-dom: '*' | |
| 1851 | + jsdom: '*' | |
| 1852 | + peerDependenciesMeta: | |
| 1853 | + '@edge-runtime/vm': | |
| 1854 | + optional: true | |
| 1855 | + '@types/node': | |
| 1856 | + optional: true | |
| 1857 | + '@vitest/browser': | |
| 1858 | + optional: true | |
| 1859 | + '@vitest/ui': | |
| 1860 | + optional: true | |
| 1861 | + happy-dom: | |
| 1862 | + optional: true | |
| 1863 | + jsdom: | |
| 1864 | + optional: true | |
| 1865 | + | |
| 1866 | + which@2.0.2: | |
| 1867 | + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} | |
| 1868 | + engines: {node: '>= 8'} | |
| 1869 | + hasBin: true | |
| 1870 | + | |
| 1871 | + why-is-node-running@2.3.0: | |
| 1872 | + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} | |
| 1873 | + engines: {node: '>=8'} | |
| 1874 | + hasBin: true | |
| 1875 | + | |
| 1876 | + word-wrap@1.2.5: | |
| 1877 | + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} | |
| 1878 | + engines: {node: '>=0.10.0'} | |
| 1879 | + | |
| 1880 | + yocto-queue@0.1.0: | |
| 1881 | + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} | |
| 1882 | + engines: {node: '>=10'} | |
| 1883 | + | |
| 1884 | + zod@3.25.76: | |
| 1885 | + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} | |
| 1886 | + | |
| 1887 | +snapshots: | |
| 1888 | + | |
| 1889 | + '@alloc/quick-lru@5.2.0': {} | |
| 1890 | + | |
| 1891 | + '@anthropic-ai/sdk@0.115.0(zod@3.25.76)': | |
| 1892 | + dependencies: | |
| 1893 | + json-schema-to-ts: 3.1.1 | |
| 1894 | + standardwebhooks: 1.0.0 | |
| 1895 | + optionalDependencies: | |
| 1896 | + zod: 3.25.76 | |
| 1897 | + | |
| 1898 | + '@babel/runtime@7.29.7': {} | |
| 1899 | + | |
| 1900 | + '@esbuild/aix-ppc64@0.21.5': | |
| 1901 | + optional: true | |
| 1902 | + | |
| 1903 | + '@esbuild/aix-ppc64@0.28.1': | |
| 1904 | + optional: true | |
| 1905 | + | |
| 1906 | + '@esbuild/android-arm64@0.21.5': | |
| 1907 | + optional: true | |
| 1908 | + | |
| 1909 | + '@esbuild/android-arm64@0.28.1': | |
| 1910 | + optional: true | |
| 1911 | + | |
| 1912 | + '@esbuild/android-arm@0.21.5': | |
| 1913 | + optional: true | |
| 1914 | + | |
| 1915 | + '@esbuild/android-arm@0.28.1': | |
| 1916 | + optional: true | |
| 1917 | + | |
| 1918 | + '@esbuild/android-x64@0.21.5': | |
| 1919 | + optional: true | |
| 1920 | + | |
| 1921 | + '@esbuild/android-x64@0.28.1': | |
| 1922 | + optional: true | |
| 1923 | + | |
| 1924 | + '@esbuild/darwin-arm64@0.21.5': | |
| 1925 | + optional: true | |
| 1926 | + | |
| 1927 | + '@esbuild/darwin-arm64@0.28.1': | |
| 1928 | + optional: true | |
| 1929 | + | |
| 1930 | + '@esbuild/darwin-x64@0.21.5': | |
| 1931 | + optional: true | |
| 1932 | + | |
| 1933 | + '@esbuild/darwin-x64@0.28.1': | |
| 1934 | + optional: true | |
| 1935 | + | |
| 1936 | + '@esbuild/freebsd-arm64@0.21.5': | |
| 1937 | + optional: true | |
| 1938 | + | |
| 1939 | + '@esbuild/freebsd-arm64@0.28.1': | |
| 1940 | + optional: true | |
| 1941 | + | |
| 1942 | + '@esbuild/freebsd-x64@0.21.5': | |
| 1943 | + optional: true | |
| 1944 | + | |
| 1945 | + '@esbuild/freebsd-x64@0.28.1': | |
| 1946 | + optional: true | |
| 1947 | + | |
| 1948 | + '@esbuild/linux-arm64@0.21.5': | |
| 1949 | + optional: true | |
| 1950 | + | |
| 1951 | + '@esbuild/linux-arm64@0.28.1': | |
| 1952 | + optional: true | |
| 1953 | + | |
| 1954 | + '@esbuild/linux-arm@0.21.5': | |
| 1955 | + optional: true | |
| 1956 | + | |
| 1957 | + '@esbuild/linux-arm@0.28.1': | |
| 1958 | + optional: true | |
| 1959 | + | |
| 1960 | + '@esbuild/linux-ia32@0.21.5': | |
| 1961 | + optional: true | |
| 1962 | + | |
| 1963 | + '@esbuild/linux-ia32@0.28.1': | |
| 1964 | + optional: true | |
| 1965 | + | |
| 1966 | + '@esbuild/linux-loong64@0.21.5': | |
| 1967 | + optional: true | |
| 1968 | + | |
| 1969 | + '@esbuild/linux-loong64@0.28.1': | |
| 1970 | + optional: true | |
| 1971 | + | |
| 1972 | + '@esbuild/linux-mips64el@0.21.5': | |
| 1973 | + optional: true | |
| 1974 | + | |
| 1975 | + '@esbuild/linux-mips64el@0.28.1': | |
| 1976 | + optional: true | |
| 1977 | + | |
| 1978 | + '@esbuild/linux-ppc64@0.21.5': | |
| 1979 | + optional: true | |
| 1980 | + | |
| 1981 | + '@esbuild/linux-ppc64@0.28.1': | |
| 1982 | + optional: true | |
| 1983 | + | |
| 1984 | + '@esbuild/linux-riscv64@0.21.5': | |
| 1985 | + optional: true | |
| 1986 | + | |
| 1987 | + '@esbuild/linux-riscv64@0.28.1': | |
| 1988 | + optional: true | |
| 1989 | + | |
| 1990 | + '@esbuild/linux-s390x@0.21.5': | |
| 1991 | + optional: true | |
| 1992 | + | |
| 1993 | + '@esbuild/linux-s390x@0.28.1': | |
| 1994 | + optional: true | |
| 1995 | + | |
| 1996 | + '@esbuild/linux-x64@0.21.5': | |
| 1997 | + optional: true | |
| 1998 | + | |
| 1999 | + '@esbuild/linux-x64@0.28.1': | |
| 2000 | + optional: true | |
| 2001 | + | |
| 2002 | + '@esbuild/netbsd-arm64@0.28.1': | |
| 2003 | + optional: true | |
| 2004 | + | |
| 2005 | + '@esbuild/netbsd-x64@0.21.5': | |
| 2006 | + optional: true | |
| 2007 | + | |
| 2008 | + '@esbuild/netbsd-x64@0.28.1': | |
| 2009 | + optional: true | |
| 2010 | + | |
| 2011 | + '@esbuild/openbsd-arm64@0.28.1': | |
| 2012 | + optional: true | |
| 2013 | + | |
| 2014 | + '@esbuild/openbsd-x64@0.21.5': | |
| 2015 | + optional: true | |
| 2016 | + | |
| 2017 | + '@esbuild/openbsd-x64@0.28.1': | |
| 2018 | + optional: true | |
| 2019 | + | |
| 2020 | + '@esbuild/openharmony-arm64@0.28.1': | |
| 2021 | + optional: true | |
| 2022 | + | |
| 2023 | + '@esbuild/sunos-x64@0.21.5': | |
| 2024 | + optional: true | |
| 2025 | + | |
| 2026 | + '@esbuild/sunos-x64@0.28.1': | |
| 2027 | + optional: true | |
| 2028 | + | |
| 2029 | + '@esbuild/win32-arm64@0.21.5': | |
| 2030 | + optional: true | |
| 2031 | + | |
| 2032 | + '@esbuild/win32-arm64@0.28.1': | |
| 2033 | + optional: true | |
| 2034 | + | |
| 2035 | + '@esbuild/win32-ia32@0.21.5': | |
| 2036 | + optional: true | |
| 2037 | + | |
| 2038 | + '@esbuild/win32-ia32@0.28.1': | |
| 2039 | + optional: true | |
| 2040 | + | |
| 2041 | + '@esbuild/win32-x64@0.21.5': | |
| 2042 | + optional: true | |
| 2043 | + | |
| 2044 | + '@esbuild/win32-x64@0.28.1': | |
| 2045 | + optional: true | |
| 2046 | + | |
| 2047 | + '@eslint-community/eslint-utils@4.10.1(eslint@9.39.5(jiti@1.21.7))': | |
| 2048 | + dependencies: | |
| 2049 | + eslint: 9.39.5(jiti@1.21.7) | |
| 2050 | + eslint-visitor-keys: 3.4.3 | |
| 2051 | + | |
| 2052 | + '@eslint-community/regexpp@4.12.2': {} | |
| 2053 | + | |
| 2054 | + '@eslint/config-array@0.21.2': | |
| 2055 | + dependencies: | |
| 2056 | + '@eslint/object-schema': 2.1.7 | |
| 2057 | + debug: 4.4.3 | |
| 2058 | + minimatch: 3.1.5 | |
| 2059 | + transitivePeerDependencies: | |
| 2060 | + - supports-color | |
| 2061 | + | |
| 2062 | + '@eslint/config-helpers@0.4.2': | |
| 2063 | + dependencies: | |
| 2064 | + '@eslint/core': 0.17.0 | |
| 2065 | + | |
| 2066 | + '@eslint/core@0.17.0': | |
| 2067 | + dependencies: | |
| 2068 | + '@types/json-schema': 7.0.15 | |
| 2069 | + | |
| 2070 | + '@eslint/eslintrc@3.3.6': | |
| 2071 | + dependencies: | |
| 2072 | + ajv: 6.15.0 | |
| 2073 | + debug: 4.4.3 | |
| 2074 | + espree: 10.4.0 | |
| 2075 | + globals: 14.0.0 | |
| 2076 | + ignore: 5.3.2 | |
| 2077 | + import-fresh: 3.3.1 | |
| 2078 | + js-yaml: 4.3.1 | |
| 2079 | + minimatch: 3.1.5 | |
| 2080 | + strip-json-comments: 3.1.1 | |
| 2081 | + transitivePeerDependencies: | |
| 2082 | + - supports-color | |
| 2083 | + | |
| 2084 | + '@eslint/js@9.39.5': {} | |
| 2085 | + | |
| 2086 | + '@eslint/object-schema@2.1.7': {} | |
| 2087 | + | |
| 2088 | + '@eslint/plugin-kit@0.4.1': | |
| 2089 | + dependencies: | |
| 2090 | + '@eslint/core': 0.17.0 | |
| 2091 | + levn: 0.4.1 | |
| 2092 | + | |
| 2093 | + '@humanfs/core@0.19.2': | |
| 2094 | + dependencies: | |
| 2095 | + '@humanfs/types': 0.15.0 | |
| 2096 | + | |
| 2097 | + '@humanfs/node@0.16.8': | |
| 2098 | + dependencies: | |
| 2099 | + '@humanfs/core': 0.19.2 | |
| 2100 | + '@humanfs/types': 0.15.0 | |
| 2101 | + '@humanwhocodes/retry': 0.4.3 | |
| 2102 | + | |
| 2103 | + '@humanfs/types@0.15.0': {} | |
| 2104 | + | |
| 2105 | + '@humanwhocodes/module-importer@1.0.1': {} | |
| 2106 | + | |
| 2107 | + '@humanwhocodes/retry@0.4.3': {} | |
| 2108 | + | |
| 2109 | + '@ioredis/commands@1.10.0': {} | |
| 2110 | + | |
| 2111 | + '@jridgewell/gen-mapping@0.3.13': | |
| 2112 | + dependencies: | |
| 2113 | + '@jridgewell/sourcemap-codec': 1.5.5 | |
| 2114 | + '@jridgewell/trace-mapping': 0.3.31 | |
| 2115 | + | |
| 2116 | + '@jridgewell/resolve-uri@3.1.2': {} | |
| 2117 | + | |
| 2118 | + '@jridgewell/sourcemap-codec@1.5.5': {} | |
| 2119 | + | |
| 2120 | + '@jridgewell/trace-mapping@0.3.31': | |
| 2121 | + dependencies: | |
| 2122 | + '@jridgewell/resolve-uri': 3.1.2 | |
| 2123 | + '@jridgewell/sourcemap-codec': 1.5.5 | |
| 2124 | + | |
| 2125 | + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4': | |
| 2126 | + optional: true | |
| 2127 | + | |
| 2128 | + '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.4': | |
| 2129 | + optional: true | |
| 2130 | + | |
| 2131 | + '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.4': | |
| 2132 | + optional: true | |
| 2133 | + | |
| 2134 | + '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.4': | |
| 2135 | + optional: true | |
| 2136 | + | |
| 2137 | + '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.4': | |
| 2138 | + optional: true | |
| 2139 | + | |
| 2140 | + '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.4': | |
| 2141 | + optional: true | |
| 2142 | + | |
| 2143 | + '@napi-rs/lzma-linux-x64-gnu@1.5.1': | |
| 2144 | + optional: true | |
| 2145 | + | |
| 2146 | + '@next/env@14.2.35': {} | |
| 2147 | + | |
| 2148 | + '@next/swc-darwin-arm64@14.2.33': | |
| 2149 | + optional: true | |
| 2150 | + | |
| 2151 | + '@next/swc-darwin-x64@14.2.33': | |
| 2152 | + optional: true | |
| 2153 | + | |
| 2154 | + '@next/swc-linux-arm64-gnu@14.2.33': | |
| 2155 | + optional: true | |
| 2156 | + | |
| 2157 | + '@next/swc-linux-arm64-musl@14.2.33': | |
| 2158 | + optional: true | |
| 2159 | + | |
| 2160 | + '@next/swc-linux-x64-gnu@14.2.33': | |
| 2161 | + optional: true | |
| 2162 | + | |
| 2163 | + '@next/swc-linux-x64-musl@14.2.33': | |
| 2164 | + optional: true | |
| 2165 | + | |
| 2166 | + '@next/swc-win32-arm64-msvc@14.2.33': | |
| 2167 | + optional: true | |
| 2168 | + | |
| 2169 | + '@next/swc-win32-ia32-msvc@14.2.33': | |
| 2170 | + optional: true | |
| 2171 | + | |
| 2172 | + '@next/swc-win32-x64-msvc@14.2.33': | |
| 2173 | + optional: true | |
| 2174 | + | |
| 2175 | + '@nodelib/fs.scandir@2.1.5': | |
| 2176 | + dependencies: | |
| 2177 | + '@nodelib/fs.stat': 2.0.5 | |
| 2178 | + run-parallel: 1.2.0 | |
| 2179 | + | |
| 2180 | + '@nodelib/fs.stat@2.0.5': {} | |
| 2181 | + | |
| 2182 | + '@nodelib/fs.walk@1.2.8': | |
| 2183 | + dependencies: | |
| 2184 | + '@nodelib/fs.scandir': 2.1.5 | |
| 2185 | + fastq: 1.20.1 | |
| 2186 | + | |
| 2187 | + '@prisma/client@5.22.0(prisma@5.22.0)': | |
| 2188 | + optionalDependencies: | |
| 2189 | + prisma: 5.22.0 | |
| 2190 | + | |
| 2191 | + '@prisma/debug@5.22.0': {} | |
| 2192 | + | |
| 2193 | + '@prisma/engines-version@5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2': {} | |
| 2194 | + | |
| 2195 | + '@prisma/engines@5.22.0': | |
| 2196 | + dependencies: | |
| 2197 | + '@prisma/debug': 5.22.0 | |
| 2198 | + '@prisma/engines-version': 5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2 | |
| 2199 | + '@prisma/fetch-engine': 5.22.0 | |
| 2200 | + '@prisma/get-platform': 5.22.0 | |
| 2201 | + | |
| 2202 | + '@prisma/fetch-engine@5.22.0': | |
| 2203 | + dependencies: | |
| 2204 | + '@prisma/debug': 5.22.0 | |
| 2205 | + '@prisma/engines-version': 5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2 | |
| 2206 | + '@prisma/get-platform': 5.22.0 | |
| 2207 | + | |
| 2208 | + '@prisma/get-platform@5.22.0': | |
| 2209 | + dependencies: | |
| 2210 | + '@prisma/debug': 5.22.0 | |
| 2211 | + | |
| 2212 | + '@rollup/rollup-android-arm-eabi@4.62.4': | |
| 2213 | + optional: true | |
| 2214 | + | |
| 2215 | + '@rollup/rollup-android-arm64@4.62.4': | |
| 2216 | + optional: true | |
| 2217 | + | |
| 2218 | + '@rollup/rollup-darwin-arm64@4.62.4': | |
| 2219 | + optional: true | |
| 2220 | + | |
| 2221 | + '@rollup/rollup-darwin-x64@4.62.4': | |
| 2222 | + optional: true | |
| 2223 | + | |
| 2224 | + '@rollup/rollup-freebsd-arm64@4.62.4': | |
| 2225 | + optional: true | |
| 2226 | + | |
| 2227 | + '@rollup/rollup-freebsd-x64@4.62.4': | |
| 2228 | + optional: true | |
| 2229 | + | |
| 2230 | + '@rollup/rollup-linux-arm-gnueabihf@4.62.4': | |
| 2231 | + optional: true | |
| 2232 | + | |
| 2233 | + '@rollup/rollup-linux-arm-musleabihf@4.62.4': | |
| 2234 | + optional: true | |
| 2235 | + | |
| 2236 | + '@rollup/rollup-linux-arm64-gnu@4.62.4': | |
| 2237 | + optional: true | |
| 2238 | + | |
| 2239 | + '@rollup/rollup-linux-arm64-musl@4.62.4': | |
| 2240 | + optional: true | |
| 2241 | + | |
| 2242 | + '@rollup/rollup-linux-loong64-gnu@4.62.4': | |
| 2243 | + optional: true | |
| 2244 | + | |
| 2245 | + '@rollup/rollup-linux-loong64-musl@4.62.4': | |
| 2246 | + optional: true | |
| 2247 | + | |
| 2248 | + '@rollup/rollup-linux-ppc64-gnu@4.62.4': | |
| 2249 | + optional: true | |
| 2250 | + | |
| 2251 | + '@rollup/rollup-linux-ppc64-musl@4.62.4': | |
| 2252 | + optional: true | |
| 2253 | + | |
| 2254 | + '@rollup/rollup-linux-riscv64-gnu@4.62.4': | |
| 2255 | + optional: true | |
| 2256 | + | |
| 2257 | + '@rollup/rollup-linux-riscv64-musl@4.62.4': | |
| 2258 | + optional: true | |
| 2259 | + | |
| 2260 | + '@rollup/rollup-linux-s390x-gnu@4.62.4': | |
| 2261 | + optional: true | |
| 2262 | + | |
| 2263 | + '@rollup/rollup-linux-x64-gnu@4.62.4': | |
| 2264 | + optional: true | |
| 2265 | + | |
| 2266 | + '@rollup/rollup-linux-x64-musl@4.62.4': | |
| 2267 | + optional: true | |
| 2268 | + | |
| 2269 | + '@rollup/rollup-openbsd-x64@4.62.4': | |
| 2270 | + optional: true | |
| 2271 | + | |
| 2272 | + '@rollup/rollup-openharmony-arm64@4.62.4': | |
| 2273 | + optional: true | |
| 2274 | + | |
| 2275 | + '@rollup/rollup-win32-arm64-msvc@4.62.4': | |
| 2276 | + optional: true | |
| 2277 | + | |
| 2278 | + '@rollup/rollup-win32-ia32-msvc@4.62.4': | |
| 2279 | + optional: true | |
| 2280 | + | |
| 2281 | + '@rollup/rollup-win32-x64-gnu@4.62.4': | |
| 2282 | + optional: true | |
| 2283 | + | |
| 2284 | + '@rollup/rollup-win32-x64-msvc@4.62.4': | |
| 2285 | + optional: true | |
| 2286 | + | |
| 2287 | + '@stablelib/base64@1.0.1': {} | |
| 2288 | + | |
| 2289 | + '@swc/counter@0.1.3': {} | |
| 2290 | + | |
| 2291 | + '@swc/helpers@0.5.5': | |
| 2292 | + dependencies: | |
| 2293 | + '@swc/counter': 0.1.3 | |
| 2294 | + tslib: 2.8.1 | |
| 2295 | + | |
| 2296 | + '@turbo/darwin-64@2.10.8': | |
| 2297 | + optional: true | |
| 2298 | + | |
| 2299 | + '@turbo/darwin-arm64@2.10.8': | |
| 2300 | + optional: true | |
| 2301 | + | |
| 2302 | + '@turbo/linux-64@2.10.8': | |
| 2303 | + optional: true | |
| 2304 | + | |
| 2305 | + '@turbo/linux-arm64@2.10.8': | |
| 2306 | + optional: true | |
| 2307 | + | |
| 2308 | + '@turbo/windows-64@2.10.8': | |
| 2309 | + optional: true | |
| 2310 | + | |
| 2311 | + '@turbo/windows-arm64@2.10.8': | |
| 2312 | + optional: true | |
| 2313 | + | |
| 2314 | + '@types/estree@1.0.9': {} | |
| 2315 | + | |
| 2316 | + '@types/json-schema@7.0.15': {} | |
| 2317 | + | |
| 2318 | + '@types/node@20.19.43': | |
| 2319 | + dependencies: | |
| 2320 | + undici-types: 6.21.0 | |
| 2321 | + | |
| 2322 | + '@types/prop-types@15.7.15': {} | |
| 2323 | + | |
| 2324 | + '@types/react-dom@18.3.7(@types/react@18.3.31)': | |
| 2325 | + dependencies: | |
| 2326 | + '@types/react': 18.3.31 | |
| 2327 | + | |
| 2328 | + '@types/react@18.3.31': | |
| 2329 | + dependencies: | |
| 2330 | + '@types/prop-types': 15.7.15 | |
| 2331 | + csstype: 3.2.3 | |
| 2332 | + | |
| 2333 | + '@typescript-eslint/eslint-plugin@8.66.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.5(jiti@1.21.7))(typescript@5.9.3)': | |
| 2334 | + dependencies: | |
| 2335 | + '@eslint-community/regexpp': 4.12.2 | |
| 2336 | + '@typescript-eslint/parser': 8.66.0(eslint@9.39.5(jiti@1.21.7))(typescript@5.9.3) | |
| 2337 | + '@typescript-eslint/scope-manager': 8.66.0 | |
| 2338 | + '@typescript-eslint/type-utils': 8.66.0(eslint@9.39.5(jiti@1.21.7))(typescript@5.9.3) | |
| 2339 | + '@typescript-eslint/utils': 8.66.0(eslint@9.39.5(jiti@1.21.7))(typescript@5.9.3) | |
| 2340 | + '@typescript-eslint/visitor-keys': 8.66.0 | |
| 2341 | + eslint: 9.39.5(jiti@1.21.7) | |
| 2342 | + ignore: 7.0.6 | |
| 2343 | + natural-compare: 1.4.0 | |
| 2344 | + ts-api-utils: 2.5.0(typescript@5.9.3) | |
| 2345 | + typescript: 5.9.3 | |
| 2346 | + transitivePeerDependencies: | |
| 2347 | + - supports-color | |
| 2348 | + | |
| 2349 | + '@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@1.21.7))(typescript@5.9.3)': | |
| 2350 | + dependencies: | |
| 2351 | + '@typescript-eslint/scope-manager': 8.66.0 | |
| 2352 | + '@typescript-eslint/types': 8.66.0 | |
| 2353 | + '@typescript-eslint/typescript-estree': 8.66.0(typescript@5.9.3) | |
| 2354 | + '@typescript-eslint/visitor-keys': 8.66.0 | |
| 2355 | + debug: 4.4.3 | |
| 2356 | + eslint: 9.39.5(jiti@1.21.7) | |
| 2357 | + typescript: 5.9.3 | |
| 2358 | + transitivePeerDependencies: | |
| 2359 | + - supports-color | |
| 2360 | + | |
| 2361 | + '@typescript-eslint/project-service@8.66.0(typescript@5.9.3)': | |
| 2362 | + dependencies: | |
| 2363 | + '@typescript-eslint/tsconfig-utils': 8.66.0(typescript@5.9.3) | |
| 2364 | + '@typescript-eslint/types': 8.66.0 | |
| 2365 | + debug: 4.4.3 | |
| 2366 | + typescript: 5.9.3 | |
| 2367 | + transitivePeerDependencies: | |
| 2368 | + - supports-color | |
| 2369 | + | |
| 2370 | + '@typescript-eslint/scope-manager@8.66.0': | |
| 2371 | + dependencies: | |
| 2372 | + '@typescript-eslint/types': 8.66.0 | |
| 2373 | + '@typescript-eslint/visitor-keys': 8.66.0 | |
| 2374 | + | |
| 2375 | + '@typescript-eslint/tsconfig-utils@8.66.0(typescript@5.9.3)': | |
| 2376 | + dependencies: | |
| 2377 | + typescript: 5.9.3 | |
| 2378 | + | |
| 2379 | + '@typescript-eslint/type-utils@8.66.0(eslint@9.39.5(jiti@1.21.7))(typescript@5.9.3)': | |
| 2380 | + dependencies: | |
| 2381 | + '@typescript-eslint/types': 8.66.0 | |
| 2382 | + '@typescript-eslint/typescript-estree': 8.66.0(typescript@5.9.3) | |
| 2383 | + '@typescript-eslint/utils': 8.66.0(eslint@9.39.5(jiti@1.21.7))(typescript@5.9.3) | |
| 2384 | + debug: 4.4.3 | |
| 2385 | + eslint: 9.39.5(jiti@1.21.7) | |
| 2386 | + ts-api-utils: 2.5.0(typescript@5.9.3) | |
| 2387 | + typescript: 5.9.3 | |
| 2388 | + transitivePeerDependencies: | |
| 2389 | + - supports-color | |
| 2390 | + | |
| 2391 | + '@typescript-eslint/types@8.66.0': {} | |
| 2392 | + | |
| 2393 | + '@typescript-eslint/typescript-estree@8.66.0(typescript@5.9.3)': | |
| 2394 | + dependencies: | |
| 2395 | + '@typescript-eslint/project-service': 8.66.0(typescript@5.9.3) | |
| 2396 | + '@typescript-eslint/tsconfig-utils': 8.66.0(typescript@5.9.3) | |
| 2397 | + '@typescript-eslint/types': 8.66.0 | |
| 2398 | + '@typescript-eslint/visitor-keys': 8.66.0 | |
| 2399 | + debug: 4.4.3 | |
| 2400 | + minimatch: 10.2.6 | |
| 2401 | + semver: 7.8.5 | |
| 2402 | + tinyglobby: 0.2.17 | |
| 2403 | + ts-api-utils: 2.5.0(typescript@5.9.3) | |
| 2404 | + typescript: 5.9.3 | |
| 2405 | + transitivePeerDependencies: | |
| 2406 | + - supports-color | |
| 2407 | + | |
| 2408 | + '@typescript-eslint/utils@8.66.0(eslint@9.39.5(jiti@1.21.7))(typescript@5.9.3)': | |
| 2409 | + dependencies: | |
| 2410 | + '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5(jiti@1.21.7)) | |
| 2411 | + '@typescript-eslint/scope-manager': 8.66.0 | |
| 2412 | + '@typescript-eslint/types': 8.66.0 | |
| 2413 | + '@typescript-eslint/typescript-estree': 8.66.0(typescript@5.9.3) | |
| 2414 | + eslint: 9.39.5(jiti@1.21.7) | |
| 2415 | + typescript: 5.9.3 | |
| 2416 | + transitivePeerDependencies: | |
| 2417 | + - supports-color | |
| 2418 | + | |
| 2419 | + '@typescript-eslint/visitor-keys@8.66.0': | |
| 2420 | + dependencies: | |
| 2421 | + '@typescript-eslint/types': 8.66.0 | |
| 2422 | + eslint-visitor-keys: 5.0.1 | |
| 2423 | + | |
| 2424 | + '@vitest/expect@2.1.9': | |
| 2425 | + dependencies: | |
| 2426 | + '@vitest/spy': 2.1.9 | |
| 2427 | + '@vitest/utils': 2.1.9 | |
| 2428 | + chai: 5.3.3 | |
| 2429 | + tinyrainbow: 1.2.0 | |
| 2430 | + | |
| 2431 | + '@vitest/mocker@2.1.9(vite@5.4.21(@types/node@20.19.43))': | |
| 2432 | + dependencies: | |
| 2433 | + '@vitest/spy': 2.1.9 | |
| 2434 | + estree-walker: 3.0.3 | |
| 2435 | + magic-string: 0.30.21 | |
| 2436 | + optionalDependencies: | |
| 2437 | + vite: 5.4.21(@types/node@20.19.43) | |
| 2438 | + | |
| 2439 | + '@vitest/pretty-format@2.1.9': | |
| 2440 | + dependencies: | |
| 2441 | + tinyrainbow: 1.2.0 | |
| 2442 | + | |
| 2443 | + '@vitest/runner@2.1.9': | |
| 2444 | + dependencies: | |
| 2445 | + '@vitest/utils': 2.1.9 | |
| 2446 | + pathe: 1.1.2 | |
| 2447 | + | |
| 2448 | + '@vitest/snapshot@2.1.9': | |
| 2449 | + dependencies: | |
| 2450 | + '@vitest/pretty-format': 2.1.9 | |
| 2451 | + magic-string: 0.30.21 | |
| 2452 | + pathe: 1.1.2 | |
| 2453 | + | |
| 2454 | + '@vitest/spy@2.1.9': | |
| 2455 | + dependencies: | |
| 2456 | + tinyspy: 3.0.2 | |
| 2457 | + | |
| 2458 | + '@vitest/utils@2.1.9': | |
| 2459 | + dependencies: | |
| 2460 | + '@vitest/pretty-format': 2.1.9 | |
| 2461 | + loupe: 3.2.1 | |
| 2462 | + tinyrainbow: 1.2.0 | |
| 2463 | + | |
| 2464 | + acorn-jsx@5.3.2(acorn@8.18.0): | |
| 2465 | + dependencies: | |
| 2466 | + acorn: 8.18.0 | |
| 2467 | + | |
| 2468 | + acorn@8.18.0: {} | |
| 2469 | + | |
| 2470 | + ajv@6.15.0: | |
| 2471 | + dependencies: | |
| 2472 | + fast-deep-equal: 3.1.3 | |
| 2473 | + fast-json-stable-stringify: 2.1.0 | |
| 2474 | + json-schema-traverse: 0.4.1 | |
| 2475 | + uri-js: 4.4.1 | |
| 2476 | + | |
| 2477 | + ansi-styles@4.3.0: | |
| 2478 | + dependencies: | |
| 2479 | + color-convert: 2.0.1 | |
| 2480 | + | |
| 2481 | + any-promise@1.3.0: {} | |
| 2482 | + | |
| 2483 | + anymatch@3.1.3: | |
| 2484 | + dependencies: | |
| 2485 | + normalize-path: 3.0.0 | |
| 2486 | + picomatch: 2.3.2 | |
| 2487 | + | |
| 2488 | + arg@5.0.2: {} | |
| 2489 | + | |
| 2490 | + argparse@2.0.1: {} | |
| 2491 | + | |
| 2492 | + assertion-error@2.0.1: {} | |
| 2493 | + | |
| 2494 | + autoprefixer@10.5.4(postcss@8.5.25): | |
| 2495 | + dependencies: | |
| 2496 | + browserslist: 4.28.7 | |
| 2497 | + caniuse-lite: 1.0.30001806 | |
| 2498 | + fraction.js: 5.3.4 | |
| 2499 | + picocolors: 1.1.1 | |
| 2500 | + postcss: 8.5.25 | |
| 2501 | + postcss-value-parser: 4.2.0 | |
| 2502 | + | |
| 2503 | + balanced-match@1.0.2: {} | |
| 2504 | + | |
| 2505 | + balanced-match@4.0.4: {} | |
| 2506 | + | |
| 2507 | + baseline-browser-mapping@2.11.12: {} | |
| 2508 | + | |
| 2509 | + binary-extensions@2.3.0: {} | |
| 2510 | + | |
| 2511 | + brace-expansion@1.1.18: | |
| 2512 | + dependencies: | |
| 2513 | + balanced-match: 1.0.2 | |
| 2514 | + concat-map: 0.0.1 | |
| 2515 | + | |
| 2516 | + brace-expansion@5.0.9: | |
| 2517 | + dependencies: | |
| 2518 | + balanced-match: 4.0.4 | |
| 2519 | + | |
| 2520 | + braces@3.0.3: | |
| 2521 | + dependencies: | |
| 2522 | + fill-range: 7.1.1 | |
| 2523 | + | |
| 2524 | + browserslist@4.28.7: | |
| 2525 | + dependencies: | |
| 2526 | + baseline-browser-mapping: 2.11.12 | |
| 2527 | + caniuse-lite: 1.0.30001806 | |
| 2528 | + electron-to-chromium: 1.5.401 | |
| 2529 | + node-releases: 2.0.52 | |
| 2530 | + update-browserslist-db: 1.2.3(browserslist@4.28.7) | |
| 2531 | + | |
| 2532 | + bullmq@5.81.3: | |
| 2533 | + dependencies: | |
| 2534 | + cron-parser: 4.9.0 | |
| 2535 | + ioredis: 5.11.1 | |
| 2536 | + msgpackr: 2.0.5 | |
| 2537 | + node-abort-controller: 3.1.1 | |
| 2538 | + semver: 7.8.5 | |
| 2539 | + tslib: 2.8.1 | |
| 2540 | + transitivePeerDependencies: | |
| 2541 | + - supports-color | |
| 2542 | + | |
| 2543 | + busboy@1.6.0: | |
| 2544 | + dependencies: | |
| 2545 | + streamsearch: 1.1.0 | |
| 2546 | + | |
| 2547 | + cac@6.7.14: {} | |
| 2548 | + | |
| 2549 | + callsites@3.1.0: {} | |
| 2550 | + | |
| 2551 | + camelcase-css@2.0.1: {} | |
| 2552 | + | |
| 2553 | + caniuse-lite@1.0.30001806: {} | |
| 2554 | + | |
| 2555 | + chai@5.3.3: | |
| 2556 | + dependencies: | |
| 2557 | + assertion-error: 2.0.1 | |
| 2558 | + check-error: 2.1.3 | |
| 2559 | + deep-eql: 5.0.2 | |
| 2560 | + loupe: 3.2.1 | |
| 2561 | + pathval: 2.0.1 | |
| 2562 | + | |
| 2563 | + chalk@4.1.2: | |
| 2564 | + dependencies: | |
| 2565 | + ansi-styles: 4.3.0 | |
| 2566 | + supports-color: 7.2.0 | |
| 2567 | + | |
| 2568 | + check-error@2.1.3: {} | |
| 2569 | + | |
| 2570 | + chokidar@3.6.0: | |
| 2571 | + dependencies: | |
| 2572 | + anymatch: 3.1.3 | |
| 2573 | + braces: 3.0.3 | |
| 2574 | + glob-parent: 5.1.2 | |
| 2575 | + is-binary-path: 2.1.0 | |
| 2576 | + is-glob: 4.0.3 | |
| 2577 | + normalize-path: 3.0.0 | |
| 2578 | + readdirp: 3.6.0 | |
| 2579 | + optionalDependencies: | |
| 2580 | + fsevents: 2.3.3 | |
| 2581 | + | |
| 2582 | + client-only@0.0.1: {} | |
| 2583 | + | |
| 2584 | + cluster-key-slot@1.1.1: {} | |
| 2585 | + | |
| 2586 | + color-convert@2.0.1: | |
| 2587 | + dependencies: | |
| 2588 | + color-name: 1.1.4 | |
| 2589 | + | |
| 2590 | + color-name@1.1.4: {} | |
| 2591 | + | |
| 2592 | + commander@4.1.1: {} | |
| 2593 | + | |
| 2594 | + concat-map@0.0.1: {} | |
| 2595 | + | |
| 2596 | + cron-parser@4.9.0: | |
| 2597 | + dependencies: | |
| 2598 | + luxon: 3.7.2 | |
| 2599 | + | |
| 2600 | + cross-spawn@7.0.6: | |
| 2601 | + dependencies: | |
| 2602 | + path-key: 3.1.1 | |
| 2603 | + shebang-command: 2.0.0 | |
| 2604 | + which: 2.0.2 | |
| 2605 | + | |
| 2606 | + cssesc@3.0.0: {} | |
| 2607 | + | |
| 2608 | + csstype@3.2.3: {} | |
| 2609 | + | |
| 2610 | + debug@4.4.3: | |
| 2611 | + dependencies: | |
| 2612 | + ms: 2.1.3 | |
| 2613 | + | |
| 2614 | + deep-eql@5.0.2: {} | |
| 2615 | + | |
| 2616 | + deep-is@0.1.4: {} | |
| 2617 | + | |
| 2618 | + denque@2.1.0: {} | |
| 2619 | + | |
| 2620 | + detect-libc@2.1.2: | |
| 2621 | + optional: true | |
| 2622 | + | |
| 2623 | + didyoumean@1.2.2: {} | |
| 2624 | + | |
| 2625 | + dlv@1.1.3: {} | |
| 2626 | + | |
| 2627 | + electron-to-chromium@1.5.401: {} | |
| 2628 | + | |
| 2629 | + es-errors@1.3.0: {} | |
| 2630 | + | |
| 2631 | + es-module-lexer@1.7.0: {} | |
| 2632 | + | |
| 2633 | + esbuild@0.21.5: | |
| 2634 | + optionalDependencies: | |
| 2635 | + '@esbuild/aix-ppc64': 0.21.5 | |
| 2636 | + '@esbuild/android-arm': 0.21.5 | |
| 2637 | + '@esbuild/android-arm64': 0.21.5 | |
| 2638 | + '@esbuild/android-x64': 0.21.5 | |
| 2639 | + '@esbuild/darwin-arm64': 0.21.5 | |
| 2640 | + '@esbuild/darwin-x64': 0.21.5 | |
| 2641 | + '@esbuild/freebsd-arm64': 0.21.5 | |
| 2642 | + '@esbuild/freebsd-x64': 0.21.5 | |
| 2643 | + '@esbuild/linux-arm': 0.21.5 | |
| 2644 | + '@esbuild/linux-arm64': 0.21.5 | |
| 2645 | + '@esbuild/linux-ia32': 0.21.5 | |
| 2646 | + '@esbuild/linux-loong64': 0.21.5 | |
| 2647 | + '@esbuild/linux-mips64el': 0.21.5 | |
| 2648 | + '@esbuild/linux-ppc64': 0.21.5 | |
| 2649 | + '@esbuild/linux-riscv64': 0.21.5 | |
| 2650 | + '@esbuild/linux-s390x': 0.21.5 | |
| 2651 | + '@esbuild/linux-x64': 0.21.5 | |
| 2652 | + '@esbuild/netbsd-x64': 0.21.5 | |
| 2653 | + '@esbuild/openbsd-x64': 0.21.5 | |
| 2654 | + '@esbuild/sunos-x64': 0.21.5 | |
| 2655 | + '@esbuild/win32-arm64': 0.21.5 | |
| 2656 | + '@esbuild/win32-ia32': 0.21.5 | |
| 2657 | + '@esbuild/win32-x64': 0.21.5 | |
| 2658 | + | |
| 2659 | + esbuild@0.28.1: | |
| 2660 | + optionalDependencies: | |
| 2661 | + '@esbuild/aix-ppc64': 0.28.1 | |
| 2662 | + '@esbuild/android-arm': 0.28.1 | |
| 2663 | + '@esbuild/android-arm64': 0.28.1 | |
| 2664 | + '@esbuild/android-x64': 0.28.1 | |
| 2665 | + '@esbuild/darwin-arm64': 0.28.1 | |
| 2666 | + '@esbuild/darwin-x64': 0.28.1 | |
| 2667 | + '@esbuild/freebsd-arm64': 0.28.1 | |
| 2668 | + '@esbuild/freebsd-x64': 0.28.1 | |
| 2669 | + '@esbuild/linux-arm': 0.28.1 | |
| 2670 | + '@esbuild/linux-arm64': 0.28.1 | |
| 2671 | + '@esbuild/linux-ia32': 0.28.1 | |
| 2672 | + '@esbuild/linux-loong64': 0.28.1 | |
| 2673 | + '@esbuild/linux-mips64el': 0.28.1 | |
| 2674 | + '@esbuild/linux-ppc64': 0.28.1 | |
| 2675 | + '@esbuild/linux-riscv64': 0.28.1 | |
| 2676 | + '@esbuild/linux-s390x': 0.28.1 | |
| 2677 | + '@esbuild/linux-x64': 0.28.1 | |
| 2678 | + '@esbuild/netbsd-arm64': 0.28.1 | |
| 2679 | + '@esbuild/netbsd-x64': 0.28.1 | |
| 2680 | + '@esbuild/openbsd-arm64': 0.28.1 | |
| 2681 | + '@esbuild/openbsd-x64': 0.28.1 | |
| 2682 | + '@esbuild/openharmony-arm64': 0.28.1 | |
| 2683 | + '@esbuild/sunos-x64': 0.28.1 | |
| 2684 | + '@esbuild/win32-arm64': 0.28.1 | |
| 2685 | + '@esbuild/win32-ia32': 0.28.1 | |
| 2686 | + '@esbuild/win32-x64': 0.28.1 | |
| 2687 | + | |
| 2688 | + escalade@3.2.0: {} | |
| 2689 | + | |
| 2690 | + escape-string-regexp@4.0.0: {} | |
| 2691 | + | |
| 2692 | + eslint-scope@8.4.0: | |
| 2693 | + dependencies: | |
| 2694 | + esrecurse: 4.3.0 | |
| 2695 | + estraverse: 5.3.0 | |
| 2696 | + | |
| 2697 | + eslint-visitor-keys@3.4.3: {} | |
| 2698 | + | |
| 2699 | + eslint-visitor-keys@4.2.1: {} | |
| 2700 | + | |
| 2701 | + eslint-visitor-keys@5.0.1: {} | |
| 2702 | + | |
| 2703 | + eslint@9.39.5(jiti@1.21.7): | |
| 2704 | + dependencies: | |
| 2705 | + '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5(jiti@1.21.7)) | |
| 2706 | + '@eslint-community/regexpp': 4.12.2 | |
| 2707 | + '@eslint/config-array': 0.21.2 | |
| 2708 | + '@eslint/config-helpers': 0.4.2 | |
| 2709 | + '@eslint/core': 0.17.0 | |
| 2710 | + '@eslint/eslintrc': 3.3.6 | |
| 2711 | + '@eslint/js': 9.39.5 | |
| 2712 | + '@eslint/plugin-kit': 0.4.1 | |
| 2713 | + '@humanfs/node': 0.16.8 | |
| 2714 | + '@humanwhocodes/module-importer': 1.0.1 | |
| 2715 | + '@humanwhocodes/retry': 0.4.3 | |
| 2716 | + '@types/estree': 1.0.9 | |
| 2717 | + ajv: 6.15.0 | |
| 2718 | + chalk: 4.1.2 | |
| 2719 | + cross-spawn: 7.0.6 | |
| 2720 | + debug: 4.4.3 | |
| 2721 | + escape-string-regexp: 4.0.0 | |
| 2722 | + eslint-scope: 8.4.0 | |
| 2723 | + eslint-visitor-keys: 4.2.1 | |
| 2724 | + espree: 10.4.0 | |
| 2725 | + esquery: 1.7.0 | |
| 2726 | + esutils: 2.0.3 | |
| 2727 | + fast-deep-equal: 3.1.3 | |
| 2728 | + file-entry-cache: 8.0.0 | |
| 2729 | + find-up: 5.0.0 | |
| 2730 | + glob-parent: 6.0.2 | |
| 2731 | + ignore: 5.3.2 | |
| 2732 | + imurmurhash: 0.1.4 | |
| 2733 | + is-glob: 4.0.3 | |
| 2734 | + json-stable-stringify-without-jsonify: 1.0.1 | |
| 2735 | + lodash.merge: 4.6.2 | |
| 2736 | + minimatch: 3.1.5 | |
| 2737 | + natural-compare: 1.4.0 | |
| 2738 | + optionator: 0.9.4 | |
| 2739 | + optionalDependencies: | |
| 2740 | + jiti: 1.21.7 | |
| 2741 | + transitivePeerDependencies: | |
| 2742 | + - supports-color | |
| 2743 | + | |
| 2744 | + espree@10.4.0: | |
| 2745 | + dependencies: | |
| 2746 | + acorn: 8.18.0 | |
| 2747 | + acorn-jsx: 5.3.2(acorn@8.18.0) | |
| 2748 | + eslint-visitor-keys: 4.2.1 | |
| 2749 | + | |
| 2750 | + esquery@1.7.0: | |
| 2751 | + dependencies: | |
| 2752 | + estraverse: 5.3.0 | |
| 2753 | + | |
| 2754 | + esrecurse@4.3.0: | |
| 2755 | + dependencies: | |
| 2756 | + estraverse: 5.3.0 | |
| 2757 | + | |
| 2758 | + estraverse@5.3.0: {} | |
| 2759 | + | |
| 2760 | + estree-walker@3.0.3: | |
| 2761 | + dependencies: | |
| 2762 | + '@types/estree': 1.0.9 | |
| 2763 | + | |
| 2764 | + esutils@2.0.3: {} | |
| 2765 | + | |
| 2766 | + expect-type@1.4.0: {} | |
| 2767 | + | |
| 2768 | + fast-check@3.23.2: | |
| 2769 | + dependencies: | |
| 2770 | + pure-rand: 6.1.0 | |
| 2771 | + | |
| 2772 | + fast-deep-equal@3.1.3: {} | |
| 2773 | + | |
| 2774 | + fast-glob@3.3.3: | |
| 2775 | + dependencies: | |
| 2776 | + '@nodelib/fs.stat': 2.0.5 | |
| 2777 | + '@nodelib/fs.walk': 1.2.8 | |
| 2778 | + glob-parent: 5.1.2 | |
| 2779 | + merge2: 1.4.1 | |
| 2780 | + micromatch: 4.0.8 | |
| 2781 | + | |
| 2782 | + fast-json-stable-stringify@2.1.0: {} | |
| 2783 | + | |
| 2784 | + fast-levenshtein@2.0.6: {} | |
| 2785 | + | |
| 2786 | + fast-sha256@1.3.0: {} | |
| 2787 | + | |
| 2788 | + fastq@1.20.1: | |
| 2789 | + dependencies: | |
| 2790 | + reusify: 1.1.0 | |
| 2791 | + | |
| 2792 | + fdir@6.5.0(picomatch@4.0.5): | |
| 2793 | + optionalDependencies: | |
| 2794 | + picomatch: 4.0.5 | |
| 2795 | + | |
| 2796 | + file-entry-cache@8.0.0: | |
| 2797 | + dependencies: | |
| 2798 | + flat-cache: 4.0.1 | |
| 2799 | + | |
| 2800 | + fill-range@7.1.1: | |
| 2801 | + dependencies: | |
| 2802 | + to-regex-range: 5.0.1 | |
| 2803 | + | |
| 2804 | + find-up@5.0.0: | |
| 2805 | + dependencies: | |
| 2806 | + locate-path: 6.0.0 | |
| 2807 | + path-exists: 4.0.0 | |
| 2808 | + | |
| 2809 | + flat-cache@4.0.1: | |
| 2810 | + dependencies: | |
| 2811 | + flatted: 3.4.4 | |
| 2812 | + keyv: 4.5.4 | |
| 2813 | + | |
| 2814 | + flatted@3.4.4: {} | |
| 2815 | + | |
| 2816 | + fraction.js@5.3.4: {} | |
| 2817 | + | |
| 2818 | + fsevents@2.3.3: | |
| 2819 | + optional: true | |
| 2820 | + | |
| 2821 | + function-bind@1.1.2: {} | |
| 2822 | + | |
| 2823 | + glob-parent@5.1.2: | |
| 2824 | + dependencies: | |
| 2825 | + is-glob: 4.0.3 | |
| 2826 | + | |
| 2827 | + glob-parent@6.0.2: | |
| 2828 | + dependencies: | |
| 2829 | + is-glob: 4.0.3 | |
| 2830 | + | |
| 2831 | + globals@14.0.0: {} | |
| 2832 | + | |
| 2833 | + graceful-fs@4.2.11: {} | |
| 2834 | + | |
| 2835 | + has-flag@4.0.0: {} | |
| 2836 | + | |
| 2837 | + hasown@2.0.4: | |
| 2838 | + dependencies: | |
| 2839 | + function-bind: 1.1.2 | |
| 2840 | + | |
| 2841 | + ignore@5.3.2: {} | |
| 2842 | + | |
| 2843 | + ignore@7.0.6: {} | |
| 2844 | + | |
| 2845 | + import-fresh@3.3.1: | |
| 2846 | + dependencies: | |
| 2847 | + parent-module: 1.0.1 | |
| 2848 | + resolve-from: 4.0.0 | |
| 2849 | + | |
| 2850 | + imurmurhash@0.1.4: {} | |
| 2851 | + | |
| 2852 | + ioredis@5.11.1: | |
| 2853 | + dependencies: | |
| 2854 | + '@ioredis/commands': 1.10.0 | |
| 2855 | + cluster-key-slot: 1.1.1 | |
| 2856 | + debug: 4.4.3 | |
| 2857 | + denque: 2.1.0 | |
| 2858 | + redis-errors: 1.2.0 | |
| 2859 | + redis-parser: 3.0.0 | |
| 2860 | + standard-as-callback: 2.1.0 | |
| 2861 | + transitivePeerDependencies: | |
| 2862 | + - supports-color | |
| 2863 | + | |
| 2864 | + is-binary-path@2.1.0: | |
| 2865 | + dependencies: | |
| 2866 | + binary-extensions: 2.3.0 | |
| 2867 | + | |
| 2868 | + is-core-module@2.16.2: | |
| 2869 | + dependencies: | |
| 2870 | + hasown: 2.0.4 | |
| 2871 | + | |
| 2872 | + is-extglob@2.1.1: {} | |
| 2873 | + | |
| 2874 | + is-glob@4.0.3: | |
| 2875 | + dependencies: | |
| 2876 | + is-extglob: 2.1.1 | |
| 2877 | + | |
| 2878 | + is-number@7.0.0: {} | |
| 2879 | + | |
| 2880 | + isexe@2.0.0: {} | |
| 2881 | + | |
| 2882 | + jiti@1.21.7: {} | |
| 2883 | + | |
| 2884 | + js-tokens@4.0.0: {} | |
| 2885 | + | |
| 2886 | + js-yaml@4.3.1: | |
| 2887 | + dependencies: | |
| 2888 | + argparse: 2.0.1 | |
| 2889 | + | |
| 2890 | + json-buffer@3.0.1: {} | |
| 2891 | + | |
| 2892 | + json-schema-to-ts@3.1.1: | |
| 2893 | + dependencies: | |
| 2894 | + '@babel/runtime': 7.29.7 | |
| 2895 | + ts-algebra: 2.0.0 | |
| 2896 | + | |
| 2897 | + json-schema-traverse@0.4.1: {} | |
| 2898 | + | |
| 2899 | + json-stable-stringify-without-jsonify@1.0.1: {} | |
| 2900 | + | |
| 2901 | + keyv@4.5.4: | |
| 2902 | + dependencies: | |
| 2903 | + json-buffer: 3.0.1 | |
| 2904 | + | |
| 2905 | + levn@0.4.1: | |
| 2906 | + dependencies: | |
| 2907 | + prelude-ls: 1.2.1 | |
| 2908 | + type-check: 0.4.0 | |
| 2909 | + | |
| 2910 | + lilconfig@3.1.3: {} | |
| 2911 | + | |
| 2912 | + lines-and-columns@1.2.4: {} | |
| 2913 | + | |
| 2914 | + locate-path@6.0.0: | |
| 2915 | + dependencies: | |
| 2916 | + p-locate: 5.0.0 | |
| 2917 | + | |
| 2918 | + lodash.merge@4.6.2: {} | |
| 2919 | + | |
| 2920 | + loose-envify@1.4.0: | |
| 2921 | + dependencies: | |
| 2922 | + js-tokens: 4.0.0 | |
| 2923 | + | |
| 2924 | + loupe@3.2.1: {} | |
| 2925 | + | |
| 2926 | + luxon@3.7.2: {} | |
| 2927 | + | |
| 2928 | + magic-string@0.30.21: | |
| 2929 | + dependencies: | |
| 2930 | + '@jridgewell/sourcemap-codec': 1.5.5 | |
| 2931 | + | |
| 2932 | + merge2@1.4.1: {} | |
| 2933 | + | |
| 2934 | + micromatch@4.0.8: | |
| 2935 | + dependencies: | |
| 2936 | + braces: 3.0.3 | |
| 2937 | + picomatch: 2.3.2 | |
| 2938 | + | |
| 2939 | + minimatch@10.2.6: | |
| 2940 | + dependencies: | |
| 2941 | + brace-expansion: 5.0.9 | |
| 2942 | + | |
| 2943 | + minimatch@3.1.5: | |
| 2944 | + dependencies: | |
| 2945 | + brace-expansion: 1.1.18 | |
| 2946 | + | |
| 2947 | + ms@2.1.3: {} | |
| 2948 | + | |
| 2949 | + msgpackr-extract@3.0.4: | |
| 2950 | + dependencies: | |
| 2951 | + node-gyp-build-optional-packages: 5.2.2 | |
| 2952 | + optionalDependencies: | |
| 2953 | + '@msgpackr-extract/msgpackr-extract-darwin-arm64': 3.0.4 | |
| 2954 | + '@msgpackr-extract/msgpackr-extract-darwin-x64': 3.0.4 | |
| 2955 | + '@msgpackr-extract/msgpackr-extract-linux-arm': 3.0.4 | |
| 2956 | + '@msgpackr-extract/msgpackr-extract-linux-arm64': 3.0.4 | |
| 2957 | + '@msgpackr-extract/msgpackr-extract-linux-x64': 3.0.4 | |
| 2958 | + '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.4 | |
| 2959 | + optional: true | |
| 2960 | + | |
| 2961 | + msgpackr@2.0.5: | |
| 2962 | + optionalDependencies: | |
| 2963 | + msgpackr-extract: 3.0.4 | |
| 2964 | + | |
| 2965 | + mz@2.7.0: | |
| 2966 | + dependencies: | |
| 2967 | + any-promise: 1.3.0 | |
| 2968 | + object-assign: 4.1.1 | |
| 2969 | + thenify-all: 1.6.0 | |
| 2970 | + | |
| 2971 | + nanoid@3.3.17: {} | |
| 2972 | + | |
| 2973 | + natural-compare@1.4.0: {} | |
| 2974 | + | |
| 2975 | + next@14.2.35(react-dom@18.3.1(react@18.3.1))(react@18.3.1): | |
| 2976 | + dependencies: | |
| 2977 | + '@next/env': 14.2.35 | |
| 2978 | + '@swc/helpers': 0.5.5 | |
| 2979 | + busboy: 1.6.0 | |
| 2980 | + caniuse-lite: 1.0.30001806 | |
| 2981 | + graceful-fs: 4.2.11 | |
| 2982 | + postcss: 8.4.31 | |
| 2983 | + react: 18.3.1 | |
| 2984 | + react-dom: 18.3.1(react@18.3.1) | |
| 2985 | + styled-jsx: 5.1.1(react@18.3.1) | |
| 2986 | + optionalDependencies: | |
| 2987 | + '@next/swc-darwin-arm64': 14.2.33 | |
| 2988 | + '@next/swc-darwin-x64': 14.2.33 | |
| 2989 | + '@next/swc-linux-arm64-gnu': 14.2.33 | |
| 2990 | + '@next/swc-linux-arm64-musl': 14.2.33 | |
| 2991 | + '@next/swc-linux-x64-gnu': 14.2.33 | |
| 2992 | + '@next/swc-linux-x64-musl': 14.2.33 | |
| 2993 | + '@next/swc-win32-arm64-msvc': 14.2.33 | |
| 2994 | + '@next/swc-win32-ia32-msvc': 14.2.33 | |
| 2995 | + '@next/swc-win32-x64-msvc': 14.2.33 | |
| 2996 | + transitivePeerDependencies: | |
| 2997 | + - '@babel/core' | |
| 2998 | + - babel-plugin-macros | |
| 2999 | + | |
| 3000 | + node-abort-controller@3.1.1: {} | |
| 3001 | + | |
| 3002 | + node-gyp-build-optional-packages@5.2.2: | |
| 3003 | + dependencies: | |
| 3004 | + detect-libc: 2.1.2 | |
| 3005 | + optional: true | |
| 3006 | + | |
| 3007 | + node-releases@2.0.52: {} | |
| 3008 | + | |
| 3009 | + normalize-path@3.0.0: {} | |
| 3010 | + | |
| 3011 | + object-assign@4.1.1: {} | |
| 3012 | + | |
| 3013 | + object-hash@3.0.0: {} | |
| 3014 | + | |
| 3015 | + optionator@0.9.4: | |
| 3016 | + dependencies: | |
| 3017 | + deep-is: 0.1.4 | |
| 3018 | + fast-levenshtein: 2.0.6 | |
| 3019 | + levn: 0.4.1 | |
| 3020 | + prelude-ls: 1.2.1 | |
| 3021 | + type-check: 0.4.0 | |
| 3022 | + word-wrap: 1.2.5 | |
| 3023 | + | |
| 3024 | + p-limit@3.1.0: | |
| 3025 | + dependencies: | |
| 3026 | + yocto-queue: 0.1.0 | |
| 3027 | + | |
| 3028 | + p-locate@5.0.0: | |
| 3029 | + dependencies: | |
| 3030 | + p-limit: 3.1.0 | |
| 3031 | + | |
| 3032 | + parent-module@1.0.1: | |
| 3033 | + dependencies: | |
| 3034 | + callsites: 3.1.0 | |
| 3035 | + | |
| 3036 | + path-exists@4.0.0: {} | |
| 3037 | + | |
| 3038 | + path-key@3.1.1: {} | |
| 3039 | + | |
| 3040 | + path-parse@1.0.7: {} | |
| 3041 | + | |
| 3042 | + pathe@1.1.2: {} | |
| 3043 | + | |
| 3044 | + pathval@2.0.1: {} | |
| 3045 | + | |
| 3046 | + picocolors@1.1.1: {} | |
| 3047 | + | |
| 3048 | + picomatch@2.3.2: {} | |
| 3049 | + | |
| 3050 | + picomatch@4.0.5: {} | |
| 3051 | + | |
| 3052 | + pify@2.3.0: {} | |
| 3053 | + | |
| 3054 | + pirates@4.0.7: {} | |
| 3055 | + | |
| 3056 | + postcss-import@15.1.0(postcss@8.5.25): | |
| 3057 | + dependencies: | |
| 3058 | + postcss: 8.5.25 | |
| 3059 | + postcss-value-parser: 4.2.0 | |
| 3060 | + read-cache: 1.0.0 | |
| 3061 | + resolve: 1.22.12 | |
| 3062 | + | |
| 3063 | + postcss-js@4.1.0(postcss@8.5.25): | |
| 3064 | + dependencies: | |
| 3065 | + camelcase-css: 2.0.1 | |
| 3066 | + postcss: 8.5.25 | |
| 3067 | + | |
| 3068 | + postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.25)(tsx@4.23.6): | |
| 3069 | + dependencies: | |
| 3070 | + lilconfig: 3.1.3 | |
| 3071 | + optionalDependencies: | |
| 3072 | + jiti: 1.21.7 | |
| 3073 | + postcss: 8.5.25 | |
| 3074 | + tsx: 4.23.6 | |
| 3075 | + | |
| 3076 | + postcss-nested@6.2.0(postcss@8.5.25): | |
| 3077 | + dependencies: | |
| 3078 | + postcss: 8.5.25 | |
| 3079 | + postcss-selector-parser: 6.1.4 | |
| 3080 | + | |
| 3081 | + postcss-selector-parser@6.1.4: | |
| 3082 | + dependencies: | |
| 3083 | + cssesc: 3.0.0 | |
| 3084 | + util-deprecate: 1.0.2 | |
| 3085 | + | |
| 3086 | + postcss-value-parser@4.2.0: {} | |
| 3087 | + | |
| 3088 | + postcss@8.4.31: | |
| 3089 | + dependencies: | |
| 3090 | + nanoid: 3.3.17 | |
| 3091 | + picocolors: 1.1.1 | |
| 3092 | + source-map-js: 1.2.1 | |
| 3093 | + | |
| 3094 | + postcss@8.5.25: | |
| 3095 | + dependencies: | |
| 3096 | + nanoid: 3.3.17 | |
| 3097 | + picocolors: 1.1.1 | |
| 3098 | + source-map-js: 1.2.1 | |
| 3099 | + | |
| 3100 | + prelude-ls@1.2.1: {} | |
| 3101 | + | |
| 3102 | + prettier@3.9.6: {} | |
| 3103 | + | |
| 3104 | + prisma@5.22.0: | |
| 3105 | + dependencies: | |
| 3106 | + '@prisma/engines': 5.22.0 | |
| 3107 | + optionalDependencies: | |
| 3108 | + fsevents: 2.3.3 | |
| 3109 | + | |
| 3110 | + punycode@2.3.1: {} | |
| 3111 | + | |
| 3112 | + pure-rand@6.1.0: {} | |
| 3113 | + | |
| 3114 | + queue-microtask@1.2.3: {} | |
| 3115 | + | |
| 3116 | + react-dom@18.3.1(react@18.3.1): | |
| 3117 | + dependencies: | |
| 3118 | + loose-envify: 1.4.0 | |
| 3119 | + react: 18.3.1 | |
| 3120 | + scheduler: 0.23.2 | |
| 3121 | + | |
| 3122 | + react@18.3.1: | |
| 3123 | + dependencies: | |
| 3124 | + loose-envify: 1.4.0 | |
| 3125 | + | |
| 3126 | + read-cache@1.0.0: | |
| 3127 | + dependencies: | |
| 3128 | + pify: 2.3.0 | |
| 3129 | + | |
| 3130 | + readdirp@3.6.0: | |
| 3131 | + dependencies: | |
| 3132 | + picomatch: 2.3.2 | |
| 3133 | + | |
| 3134 | + redis-errors@1.2.0: {} | |
| 3135 | + | |
| 3136 | + redis-parser@3.0.0: | |
| 3137 | + dependencies: | |
| 3138 | + redis-errors: 1.2.0 | |
| 3139 | + | |
| 3140 | + resolve-from@4.0.0: {} | |
| 3141 | + | |
| 3142 | + resolve@1.22.12: | |
| 3143 | + dependencies: | |
| 3144 | + es-errors: 1.3.0 | |
| 3145 | + is-core-module: 2.16.2 | |
| 3146 | + path-parse: 1.0.7 | |
| 3147 | + supports-preserve-symlinks-flag: 1.0.0 | |
| 3148 | + | |
| 3149 | + reusify@1.1.0: {} | |
| 3150 | + | |
| 3151 | + rollup@4.62.4: | |
| 3152 | + dependencies: | |
| 3153 | + '@types/estree': 1.0.9 | |
| 3154 | + optionalDependencies: | |
| 3155 | + '@napi-rs/lzma-linux-x64-gnu': 1.5.1 | |
| 3156 | + '@rollup/rollup-android-arm-eabi': 4.62.4 | |
| 3157 | + '@rollup/rollup-android-arm64': 4.62.4 | |
| 3158 | + '@rollup/rollup-darwin-arm64': 4.62.4 | |
| 3159 | + '@rollup/rollup-darwin-x64': 4.62.4 | |
| 3160 | + '@rollup/rollup-freebsd-arm64': 4.62.4 | |
| 3161 | + '@rollup/rollup-freebsd-x64': 4.62.4 | |
| 3162 | + '@rollup/rollup-linux-arm-gnueabihf': 4.62.4 | |
| 3163 | + '@rollup/rollup-linux-arm-musleabihf': 4.62.4 | |
| 3164 | + '@rollup/rollup-linux-arm64-gnu': 4.62.4 | |
| 3165 | + '@rollup/rollup-linux-arm64-musl': 4.62.4 | |
| 3166 | + '@rollup/rollup-linux-loong64-gnu': 4.62.4 | |
| 3167 | + '@rollup/rollup-linux-loong64-musl': 4.62.4 | |
| 3168 | + '@rollup/rollup-linux-ppc64-gnu': 4.62.4 | |
| 3169 | + '@rollup/rollup-linux-ppc64-musl': 4.62.4 | |
| 3170 | + '@rollup/rollup-linux-riscv64-gnu': 4.62.4 | |
| 3171 | + '@rollup/rollup-linux-riscv64-musl': 4.62.4 | |
| 3172 | + '@rollup/rollup-linux-s390x-gnu': 4.62.4 | |
| 3173 | + '@rollup/rollup-linux-x64-gnu': 4.62.4 | |
| 3174 | + '@rollup/rollup-linux-x64-musl': 4.62.4 | |
| 3175 | + '@rollup/rollup-openbsd-x64': 4.62.4 | |
| 3176 | + '@rollup/rollup-openharmony-arm64': 4.62.4 | |
| 3177 | + '@rollup/rollup-win32-arm64-msvc': 4.62.4 | |
| 3178 | + '@rollup/rollup-win32-ia32-msvc': 4.62.4 | |
| 3179 | + '@rollup/rollup-win32-x64-gnu': 4.62.4 | |
| 3180 | + '@rollup/rollup-win32-x64-msvc': 4.62.4 | |
| 3181 | + fsevents: 2.3.3 | |
| 3182 | + | |
| 3183 | + run-parallel@1.2.0: | |
| 3184 | + dependencies: | |
| 3185 | + queue-microtask: 1.2.3 | |
| 3186 | + | |
| 3187 | + scheduler@0.23.2: | |
| 3188 | + dependencies: | |
| 3189 | + loose-envify: 1.4.0 | |
| 3190 | + | |
| 3191 | + semver@7.8.5: {} | |
| 3192 | + | |
| 3193 | + shebang-command@2.0.0: | |
| 3194 | + dependencies: | |
| 3195 | + shebang-regex: 3.0.0 | |
| 3196 | + | |
| 3197 | + shebang-regex@3.0.0: {} | |
| 3198 | + | |
| 3199 | + siginfo@2.0.0: {} | |
| 3200 | + | |
| 3201 | + source-map-js@1.2.1: {} | |
| 3202 | + | |
| 3203 | + stackback@0.0.2: {} | |
| 3204 | + | |
| 3205 | + standard-as-callback@2.1.0: {} | |
| 3206 | + | |
| 3207 | + standardwebhooks@1.0.0: | |
| 3208 | + dependencies: | |
| 3209 | + '@stablelib/base64': 1.0.1 | |
| 3210 | + fast-sha256: 1.3.0 | |
| 3211 | + | |
| 3212 | + std-env@3.10.0: {} | |
| 3213 | + | |
| 3214 | + streamsearch@1.1.0: {} | |
| 3215 | + | |
| 3216 | + strip-json-comments@3.1.1: {} | |
| 3217 | + | |
| 3218 | + styled-jsx@5.1.1(react@18.3.1): | |
| 3219 | + dependencies: | |
| 3220 | + client-only: 0.0.1 | |
| 3221 | + react: 18.3.1 | |
| 3222 | + | |
| 3223 | + sucrase@3.35.1: | |
| 3224 | + dependencies: | |
| 3225 | + '@jridgewell/gen-mapping': 0.3.13 | |
| 3226 | + commander: 4.1.1 | |
| 3227 | + lines-and-columns: 1.2.4 | |
| 3228 | + mz: 2.7.0 | |
| 3229 | + pirates: 4.0.7 | |
| 3230 | + tinyglobby: 0.2.17 | |
| 3231 | + ts-interface-checker: 0.1.13 | |
| 3232 | + | |
| 3233 | + supports-color@7.2.0: | |
| 3234 | + dependencies: | |
| 3235 | + has-flag: 4.0.0 | |
| 3236 | + | |
| 3237 | + supports-preserve-symlinks-flag@1.0.0: {} | |
| 3238 | + | |
| 3239 | + tailwindcss@3.4.19(tsx@4.23.6): | |
| 3240 | + dependencies: | |
| 3241 | + '@alloc/quick-lru': 5.2.0 | |
| 3242 | + arg: 5.0.2 | |
| 3243 | + chokidar: 3.6.0 | |
| 3244 | + didyoumean: 1.2.2 | |
| 3245 | + dlv: 1.1.3 | |
| 3246 | + fast-glob: 3.3.3 | |
| 3247 | + glob-parent: 6.0.2 | |
| 3248 | + is-glob: 4.0.3 | |
| 3249 | + jiti: 1.21.7 | |
| 3250 | + lilconfig: 3.1.3 | |
| 3251 | + micromatch: 4.0.8 | |
| 3252 | + normalize-path: 3.0.0 | |
| 3253 | + object-hash: 3.0.0 | |
| 3254 | + picocolors: 1.1.1 | |
| 3255 | + postcss: 8.5.25 | |
| 3256 | + postcss-import: 15.1.0(postcss@8.5.25) | |
| 3257 | + postcss-js: 4.1.0(postcss@8.5.25) | |
| 3258 | + postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.25)(tsx@4.23.6) | |
| 3259 | + postcss-nested: 6.2.0(postcss@8.5.25) | |
| 3260 | + postcss-selector-parser: 6.1.4 | |
| 3261 | + resolve: 1.22.12 | |
| 3262 | + sucrase: 3.35.1 | |
| 3263 | + transitivePeerDependencies: | |
| 3264 | + - tsx | |
| 3265 | + - yaml | |
| 3266 | + | |
| 3267 | + thenify-all@1.6.0: | |
| 3268 | + dependencies: | |
| 3269 | + thenify: 3.3.1 | |
| 3270 | + | |
| 3271 | + thenify@3.3.1: | |
| 3272 | + dependencies: | |
| 3273 | + any-promise: 1.3.0 | |
| 3274 | + | |
| 3275 | + tinybench@2.9.0: {} | |
| 3276 | + | |
| 3277 | + tinyexec@0.3.2: {} | |
| 3278 | + | |
| 3279 | + tinyglobby@0.2.17: | |
| 3280 | + dependencies: | |
| 3281 | + fdir: 6.5.0(picomatch@4.0.5) | |
| 3282 | + picomatch: 4.0.5 | |
| 3283 | + | |
| 3284 | + tinypool@1.1.1: {} | |
| 3285 | + | |
| 3286 | + tinyrainbow@1.2.0: {} | |
| 3287 | + | |
| 3288 | + tinyspy@3.0.2: {} | |
| 3289 | + | |
| 3290 | + to-regex-range@5.0.1: | |
| 3291 | + dependencies: | |
| 3292 | + is-number: 7.0.0 | |
| 3293 | + | |
| 3294 | + ts-algebra@2.0.0: {} | |
| 3295 | + | |
| 3296 | + ts-api-utils@2.5.0(typescript@5.9.3): | |
| 3297 | + dependencies: | |
| 3298 | + typescript: 5.9.3 | |
| 3299 | + | |
| 3300 | + ts-interface-checker@0.1.13: {} | |
| 3301 | + | |
| 3302 | + tslib@2.8.1: {} | |
| 3303 | + | |
| 3304 | + tsx@4.23.6: | |
| 3305 | + dependencies: | |
| 3306 | + esbuild: 0.28.1 | |
| 3307 | + optionalDependencies: | |
| 3308 | + fsevents: 2.3.3 | |
| 3309 | + | |
| 3310 | + turbo@2.10.8: | |
| 3311 | + optionalDependencies: | |
| 3312 | + '@turbo/darwin-64': 2.10.8 | |
| 3313 | + '@turbo/darwin-arm64': 2.10.8 | |
| 3314 | + '@turbo/linux-64': 2.10.8 | |
| 3315 | + '@turbo/linux-arm64': 2.10.8 | |
| 3316 | + '@turbo/windows-64': 2.10.8 | |
| 3317 | + '@turbo/windows-arm64': 2.10.8 | |
| 3318 | + | |
| 3319 | + type-check@0.4.0: | |
| 3320 | + dependencies: | |
| 3321 | + prelude-ls: 1.2.1 | |
| 3322 | + | |
| 3323 | + typescript-eslint@8.66.0(eslint@9.39.5(jiti@1.21.7))(typescript@5.9.3): | |
| 3324 | + dependencies: | |
| 3325 | + '@typescript-eslint/eslint-plugin': 8.66.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.5(jiti@1.21.7))(typescript@5.9.3) | |
| 3326 | + '@typescript-eslint/parser': 8.66.0(eslint@9.39.5(jiti@1.21.7))(typescript@5.9.3) | |
| 3327 | + '@typescript-eslint/typescript-estree': 8.66.0(typescript@5.9.3) | |
| 3328 | + '@typescript-eslint/utils': 8.66.0(eslint@9.39.5(jiti@1.21.7))(typescript@5.9.3) | |
| 3329 | + eslint: 9.39.5(jiti@1.21.7) | |
| 3330 | + typescript: 5.9.3 | |
| 3331 | + transitivePeerDependencies: | |
| 3332 | + - supports-color | |
| 3333 | + | |
| 3334 | + typescript@5.9.3: {} | |
| 3335 | + | |
| 3336 | + undici-types@6.21.0: {} | |
| 3337 | + | |
| 3338 | + update-browserslist-db@1.2.3(browserslist@4.28.7): | |
| 3339 | + dependencies: | |
| 3340 | + browserslist: 4.28.7 | |
| 3341 | + escalade: 3.2.0 | |
| 3342 | + picocolors: 1.1.1 | |
| 3343 | + | |
| 3344 | + uri-js@4.4.1: | |
| 3345 | + dependencies: | |
| 3346 | + punycode: 2.3.1 | |
| 3347 | + | |
| 3348 | + util-deprecate@1.0.2: {} | |
| 3349 | + | |
| 3350 | + vite-node@2.1.9(@types/node@20.19.43): | |
| 3351 | + dependencies: | |
| 3352 | + cac: 6.7.14 | |
| 3353 | + debug: 4.4.3 | |
| 3354 | + es-module-lexer: 1.7.0 | |
| 3355 | + pathe: 1.1.2 | |
| 3356 | + vite: 5.4.21(@types/node@20.19.43) | |
| 3357 | + transitivePeerDependencies: | |
| 3358 | + - '@types/node' | |
| 3359 | + - less | |
| 3360 | + - lightningcss | |
| 3361 | + - sass | |
| 3362 | + - sass-embedded | |
| 3363 | + - stylus | |
| 3364 | + - sugarss | |
| 3365 | + - supports-color | |
| 3366 | + - terser | |
| 3367 | + | |
| 3368 | + vite@5.4.21(@types/node@20.19.43): | |
| 3369 | + dependencies: | |
| 3370 | + esbuild: 0.21.5 | |
| 3371 | + postcss: 8.5.25 | |
| 3372 | + rollup: 4.62.4 | |
| 3373 | + optionalDependencies: | |
| 3374 | + '@types/node': 20.19.43 | |
| 3375 | + fsevents: 2.3.3 | |
| 3376 | + | |
| 3377 | + vitest@2.1.9(@types/node@20.19.43): | |
| 3378 | + dependencies: | |
| 3379 | + '@vitest/expect': 2.1.9 | |
| 3380 | + '@vitest/mocker': 2.1.9(vite@5.4.21(@types/node@20.19.43)) | |
| 3381 | + '@vitest/pretty-format': 2.1.9 | |
| 3382 | + '@vitest/runner': 2.1.9 | |
| 3383 | + '@vitest/snapshot': 2.1.9 | |
| 3384 | + '@vitest/spy': 2.1.9 | |
| 3385 | + '@vitest/utils': 2.1.9 | |
| 3386 | + chai: 5.3.3 | |
| 3387 | + debug: 4.4.3 | |
| 3388 | + expect-type: 1.4.0 | |
| 3389 | + magic-string: 0.30.21 | |
| 3390 | + pathe: 1.1.2 | |
| 3391 | + std-env: 3.10.0 | |
| 3392 | + tinybench: 2.9.0 | |
| 3393 | + tinyexec: 0.3.2 | |
| 3394 | + tinypool: 1.1.1 | |
| 3395 | + tinyrainbow: 1.2.0 | |
| 3396 | + vite: 5.4.21(@types/node@20.19.43) | |
| 3397 | + vite-node: 2.1.9(@types/node@20.19.43) | |
| 3398 | + why-is-node-running: 2.3.0 | |
| 3399 | + optionalDependencies: | |
| 3400 | + '@types/node': 20.19.43 | |
| 3401 | + transitivePeerDependencies: | |
| 3402 | + - less | |
| 3403 | + - lightningcss | |
| 3404 | + - msw | |
| 3405 | + - sass | |
| 3406 | + - sass-embedded | |
| 3407 | + - stylus | |
| 3408 | + - sugarss | |
| 3409 | + - supports-color | |
| 3410 | + - terser | |
| 3411 | + | |
| 3412 | + which@2.0.2: | |
| 3413 | + dependencies: | |
| 3414 | + isexe: 2.0.0 | |
| 3415 | + | |
| 3416 | + why-is-node-running@2.3.0: | |
| 3417 | + dependencies: | |
| 3418 | + siginfo: 2.0.0 | |
| 3419 | + stackback: 0.0.2 | |
| 3420 | + | |
| 3421 | + word-wrap@1.2.5: {} | |
| 3422 | + | |
| 3423 | + yocto-queue@0.1.0: {} | |
| 3424 | + | |
| 3425 | + zod@3.25.76: {} | |
added
pnpm-workspace.yaml
+3 −0
@@ -0,0 +1,3 @@ | ||
| 1 | +packages: | |
| 2 | + - "apps/*" | |
| 3 | + - "packages/*" | |
added
turbo.json
+19 −0
@@ -0,0 +1,19 @@ | ||
| 1 | +{ | |
| 2 | + "$schema": "https://turbo.build/schema.json", | |
| 3 | + "tasks": { | |
| 4 | + "build": { | |
| 5 | + "dependsOn": ["^build"], | |
| 6 | + "outputs": [".next/**", "!.next/cache/**", "dist/**"] | |
| 7 | + }, | |
| 8 | + "dev": { | |
| 9 | + "cache": false, | |
| 10 | + "persistent": true | |
| 11 | + }, | |
| 12 | + "test": { | |
| 13 | + "outputs": [] | |
| 14 | + }, | |
| 15 | + "typecheck": { | |
| 16 | + "outputs": [] | |
| 17 | + } | |
| 18 | + } | |
| 19 | +} | |
| 20 | ||