Foundation: monorepo, schema, provenance SDK, ontology resolver, ranking engine, OncoTree connector
74 changed files +13,851 −0
added
.env.example
+34 −0
@@ -0,0 +1,34 @@ | ||
| 1 | +# CancerIndex — environment (never commit real values) | |
| 2 | +NODE_ENV=development | |
| 3 | +DATABASE_URL=postgres://localhost:5432/cancerindex | |
| 4 | +DB_POOL_MAX=10 | |
| 5 | + | |
| 6 | +WEB_PORT=8250 | |
| 7 | +API_PORT=8251 | |
| 8 | +API_HOST=127.0.0.1 | |
| 9 | +CI_API_URL=http://127.0.0.1:8251 | |
| 10 | +NEXT_PUBLIC_SITE_URL=http://localhost:8250 | |
| 11 | +NEXT_TELEMETRY_DISABLED=1 | |
| 12 | + | |
| 13 | +# Data lake (raw connector payloads, gzip JSON) — outside git | |
| 14 | +CI_DATA_DIR=./data | |
| 15 | + | |
| 16 | +# Admin console (/admin) bearer token | |
| 17 | +ADMIN_TOKEN=change-me | |
| 18 | + | |
| 19 | +# NCBI E-utilities (PubMed, ClinVar) — tool/email are mandatory, api key raises the limit to 10 req/s | |
| 20 | +NCBI_TOOL=cancerindex | |
| 21 | +NCBI_EMAIL= | |
| 22 | +NCBI_API_KEY= | |
| 23 | + | |
| 24 | +# SEER API (free key from https://api.seer.cancer.gov/) — connector stays "awaiting_credentials" without it | |
| 25 | +SEER_API_KEY= | |
| 26 | + | |
| 27 | +# Optional AI provider for /ask (Phase 5) — the database works without it | |
| 28 | +ANTHROPIC_API_KEY= | |
| 29 | +OPENAI_API_KEY= | |
| 30 | +OPENAI_BASE_URL= | |
| 31 | + | |
| 32 | +WORKER_CONCURRENCY=4 | |
| 33 | +CI_MAX_RUN_MINUTES=45 | |
| 34 | +LOG_LEVEL=info | |
added
.gitignore
+15 −0
@@ -0,0 +1,15 @@ | ||
| 1 | +node_modules/ | |
| 2 | +.next/ | |
| 3 | +dist/ | |
| 4 | +*.tsbuildinfo | |
| 5 | +.env | |
| 6 | +.env.* | |
| 7 | +!.env.example | |
| 8 | +data/raw/ | |
| 9 | +data/cache/ | |
| 10 | +data/tmp/ | |
| 11 | +logs/ | |
| 12 | +tmp/ | |
| 13 | +coverage/ | |
| 14 | +.DS_Store | |
| 15 | +.claude/ | |
added
.npmrc
+2 −0
@@ -0,0 +1,2 @@ | ||
| 1 | +auto-install-peers=true | |
| 2 | +strict-peer-dependencies=false | |
added
CLAUDE.md
+82 −0
@@ -0,0 +1,82 @@ | ||
| 1 | +# CLAUDE.md — CancerIndex.io (repository guide) | |
| 2 | + | |
| 3 | +The full product specification (364 sections) lives in `docs/SPEC-original.md`. This file is the | |
| 4 | +condensed, operational version every contributor (human or agent) must follow. | |
| 5 | + | |
| 6 | +## Mission | |
| 7 | +Build **the global index of cancer**: a provenance-first, continuously updated, transparently | |
| 8 | +sourced oncology ontology connecting epidemiology, genomics, biomarkers, therapies, trials, | |
| 9 | +regulatory evidence and literature — for every recognized malignant disease entity, not a list of | |
| 10 | +30 common cancers. Not a physician, not a diagnostic tool, no treatment recommendations. | |
| 11 | + | |
| 12 | +## Non-negotiables (from the spec) | |
| 13 | +1. **Provenance first** — no scientific number without a `provenance` row; derived values carry a | |
| 14 | + `formula_version` and their inputs (§2, §250-253). Raw payloads go to the data lake (§26). | |
| 15 | +2. **Layers stay separable**: RAW → NORMALIZED → CANONICAL → DERIVED → RANKED → AI (§2). | |
| 16 | +3. **Never fake data**: no mock/hardcoded statistics in UI; show "Data not yet available" (§281-283). | |
| 17 | +4. **Identifiers are first-class**: keep every upstream ID in `*_codes` / dedicated columns (§347); | |
| 18 | + public IDs are `CI-<NS>-00000001` minted via `mintId` (§6), never DB integers. | |
| 19 | +5. **Reconciliation before ingestion**: IDs → curated aliases → normalized strings; LLM only as a | |
| 20 | + *candidate* generator; unknown labels go to `unresolved_labels`, never dropped (§69, §222, §340). | |
| 21 | +6. **Time-aware observations**: never overwrite year X with year Y; store both (§71). | |
| 22 | +7. **Context on every edge**: cancer context, direction, evidence level, provenance (§29, §244). | |
| 23 | +8. **Licensing gate**: no connector goes live before manifest `licenseStatus` is reviewed; IARC/ | |
| 24 | + GLOBOCAN stays `review`, SEER `awaiting_credentials` until keys/terms are settled (§10.4, §142). | |
| 25 | +9. **Verify docs before coding a connector** — official docs, smoke test with curl, fixture, tests, | |
| 26 | + prove on 10/100/1000 records before a full sync (§224-227). Record `documentationVerifiedAt`. | |
| 27 | +10. **Rate limits, idempotency, restartability, anomaly guard** (never mass-delete on a shrunken | |
| 28 | + response) (§90-92, §171, §229). | |
| 29 | +11. **Rankings need scope + year + source + formula version + "Why this rank?"** (§33, §183). | |
| 30 | + Composite scores are Phase 2+ (§353). | |
| 31 | +12. **Scientific safety labels**: observed / published / curated / regulatory / guideline / | |
| 32 | + computed / AI-generated — never merged (§3). Population survival ≠ individual prognosis (§325). | |
| 33 | + | |
| 34 | +## Repository layout | |
| 35 | +``` | |
| 36 | +apps/web Next.js 16 (webpack build, --webpack), Tailwind v4, server components, /api/v1 proxied to apps/api | |
| 37 | +apps/api Fastify /v1 public API (JSON envelope { data, sources, dataRelease }) | |
| 38 | +workers/ pg-boss scheduler: connector schedules, counters, rankings | |
| 39 | +packages/shared ids, provenance types, normalization, logger | |
| 40 | +packages/database Drizzle schema (snake_case casing), migrations, seed (metrics + geographies only) | |
| 41 | +packages/ontology qualifier rules, CancerResolver (alias/code reconciliation), TOP_LEVEL_CANCERS | |
| 42 | +packages/connectors SDK (manifest, HttpClient, RawLake, RunContext, validators) + connectors/<id>/{manifest.ts,index.ts,fixtures/,*.test.ts} | |
| 43 | +packages/ranking counters (entity_counters), ranking engine (snapshots + lineage), trace | |
| 44 | +scripts/ci.ts operator CLI: `pnpm cix connectors|run <id>|run-all|health|sources:sync|counters|rank|stats|trace` | |
| 45 | +docs/ architecture, data model, methodology, source policy, ADRs, connector docs | |
| 46 | +data/raw data lake (gitignored) — {source}/{date}/{entity}/{runId}-{part}.jsonl.gz | |
| 47 | +``` | |
| 48 | + | |
| 49 | +## Conventions | |
| 50 | +- TypeScript strict, ESM, NodeNext imports with `.js` suffix inside packages; web app uses | |
| 51 | + `moduleResolution: bundler` + webpack `extensionAlias` (never import `@cancerindex/connectors` in web). | |
| 52 | +- Database: `getDb()` singleton; Drizzle `casing: 'snake_case'`; raw SQL via `sql` template for | |
| 53 | + aggregates. Schema changes = new migration via `pnpm db:generate` (one per integration), never | |
| 54 | + manual prod mutation. Agents developing in parallel use `drizzle-kit push` on their own DB and do | |
| 55 | + **not** commit migration files; the integrator generates the migration. | |
| 56 | +- Connector = `class extends Connector` with `manifest`, `healthCheck(ctx)`, `sync(ctx)`; register | |
| 57 | + in `packages/connectors/src/registry.ts`. Use `ctx.http` (rate-limited), `ctx.upsertSourceRecord` | |
| 58 | + (idempotent + raw lake), `ctx.addProvenance`, `ctx.recordUnresolved`, `ctx.cursor` (restart), | |
| 59 | + `ctx.shouldStop()` between pages (time budget), validators from `sdk/validate.ts`. | |
| 60 | +- Reconciliation: `new CancerResolver(db)` → `warm()` → `byCode()/byLabel()/resolve()`; store | |
| 61 | + `match_type` on every mapping; enqueue misses with `ctx.recordUnresolved`. | |
| 62 | +- Tests: vitest; connector tests run against **fixtures** (sanitized JSON in `fixtures/`), never | |
| 63 | + live APIs; cover normal / empty / pagination / rate-limit / server error / malformed record. | |
| 64 | +- UI: scientific-editorial aesthetic (off-white, charcoal, muted neutrals, dense tables, no | |
| 65 | + gradients/cards-everywhere), WCAG-minded, mobile first-class. Every number shows source badge, | |
| 66 | + unit, population, period and a freshness line. Empty state = "Data not yet available". | |
| 67 | +- Language: English UI; code comments English; commit messages English. | |
| 68 | + | |
| 69 | +## Running locally | |
| 70 | +``` | |
| 71 | +createdb cancerindex && cp .env.example .env | |
| 72 | +pnpm install && pnpm db:migrate && pnpm db:seed && pnpm cix sources:sync | |
| 73 | +pnpm cix run oncotree --mode dry_run # smoke; then without --mode | |
| 74 | +pnpm cix counters && pnpm cix rank | |
| 75 | +pnpm dev:api & pnpm dev:web | |
| 76 | +``` | |
| 77 | + | |
| 78 | +## Deployment | |
| 79 | +MacLustr cluster via `mld` (gateway M1M32): manifest `~/dispatch/apps/cancerindex.json`, | |
| 80 | +`mld stage <dir> cancerindex` then `mld deploy cancerindex`. Web :8250, API :8251, PM2 processes | |
| 81 | +`cancerindex-web|api|worker|ngrok`, domain www.cancerindex.io (ngrok reserved). Postgres 17 + | |
| 82 | +pgvector on the node, database `cancerindex`. Source of truth for the code: spbgit `cancerindex.git`. | |
added
package.json
+40 −0
@@ -0,0 +1,40 @@ | ||
| 1 | +{ | |
| 2 | + "name": "cancerindex", | |
| 3 | + "version": "0.1.0", | |
| 4 | + "private": true, | |
| 5 | + "description": "CancerIndex.io — the global index of cancer: provenance-first oncology ontology, epidemiology, genomics, trials, literature, rankings", | |
| 6 | + "type": "module", | |
| 7 | + "packageManager": "pnpm@11.1.2", | |
| 8 | + "engines": { | |
| 9 | + "node": ">=22" | |
| 10 | + }, | |
| 11 | + "scripts": { | |
| 12 | + "build": "pnpm -r --filter './packages/**' run build && pnpm --filter @cancerindex/api run build && pnpm --filter @cancerindex/web run build", | |
| 13 | + "typecheck": "pnpm -r run typecheck", | |
| 14 | + "test": "pnpm -r run test", | |
| 15 | + "db:generate": "pnpm --filter @cancerindex/database run generate", | |
| 16 | + "db:migrate": "pnpm --filter @cancerindex/database run migrate", | |
| 17 | + "db:seed": "pnpm --filter @cancerindex/database run seed", | |
| 18 | + "dev:web": "pnpm --filter @cancerindex/web run dev", | |
| 19 | + "dev:api": "pnpm --filter @cancerindex/api run dev", | |
| 20 | + "worker": "tsx workers/main.ts", | |
| 21 | + "cix": "tsx scripts/ci.ts", | |
| 22 | + "registry": "tsx scripts/build-registry.ts", | |
| 23 | + "registry:check": "tsx scripts/build-registry.ts --check" | |
| 24 | + }, | |
| 25 | + "dependencies": { | |
| 26 | + "@cancerindex/connectors": "workspace:*", | |
| 27 | + "@cancerindex/database": "workspace:*", | |
| 28 | + "@cancerindex/ontology": "workspace:*", | |
| 29 | + "@cancerindex/ranking": "workspace:*", | |
| 30 | + "@cancerindex/shared": "workspace:*", | |
| 31 | + "drizzle-orm": "^0.45.0", | |
| 32 | + "postgres": "^3.4.7" | |
| 33 | + }, | |
| 34 | + "devDependencies": { | |
| 35 | + "@types/node": "^24.0.0", | |
| 36 | + "tsx": "^4.20.0", | |
| 37 | + "typescript": "^5.9.3", | |
| 38 | + "vitest": "^3.2.0" | |
| 39 | + } | |
| 40 | +} | |
added
packages/connectors/package.json
+36 −0
@@ -0,0 +1,36 @@ | ||
| 1 | +{ | |
| 2 | + "name": "@cancerindex/connectors", | |
| 3 | + "version": "0.1.0", | |
| 4 | + "private": true, | |
| 5 | + "type": "module", | |
| 6 | + "exports": { | |
| 7 | + ".": { | |
| 8 | + "types": "./src/index.ts", | |
| 9 | + "default": "./src/index.ts" | |
| 10 | + }, | |
| 11 | + "./sdk": { | |
| 12 | + "types": "./src/sdk/index.ts", | |
| 13 | + "default": "./src/sdk/index.ts" | |
| 14 | + } | |
| 15 | + }, | |
| 16 | + "scripts": { | |
| 17 | + "build": "tsc -p tsconfig.json --noEmit", | |
| 18 | + "typecheck": "tsc -p tsconfig.json --noEmit", | |
| 19 | + "test": "vitest run --passWithNoTests" | |
| 20 | + }, | |
| 21 | + "dependencies": { | |
| 22 | + "@cancerindex/database": "workspace:*", | |
| 23 | + "@cancerindex/ontology": "workspace:*", | |
| 24 | + "@cancerindex/shared": "workspace:*", | |
| 25 | + "drizzle-orm": "^0.45.0", | |
| 26 | + "fast-xml-parser": "^5.2.0", | |
| 27 | + "postgres": "^3.4.7", | |
| 28 | + "yaml": "^2.8.0", | |
| 29 | + "zod": "^4.0.0" | |
| 30 | + }, | |
| 31 | + "devDependencies": { | |
| 32 | + "@types/node": "^24.0.0", | |
| 33 | + "typescript": "^5.9.3", | |
| 34 | + "vitest": "^3.2.0" | |
| 35 | + } | |
| 36 | +} | |
added
packages/connectors/src/connectors/oncotree/index.ts
+185 −0
@@ -0,0 +1,185 @@ | ||
| 1 | +import { eq, sql } from 'drizzle-orm'; | |
| 2 | +import { z } from 'zod'; | |
| 3 | +import { normalizeLabel, slugify } from '@cancerindex/shared'; | |
| 4 | +import { anatomicalSites, cancerAliases, cancerAnatomy, cancerCodes, cancerHierarchy, cancers, mintId } from '@cancerindex/database'; | |
| 5 | +import { CancerResolver } from '@cancerindex/ontology'; | |
| 6 | +import { Connector, type ConnectorHealth, type RunContext } from '../../sdk/run.js'; | |
| 7 | +import { manifest } from './manifest.js'; | |
| 8 | + | |
| 9 | +const TumorType = z.object({ | |
| 10 | + code: z.string(), | |
| 11 | + name: z.string(), | |
| 12 | + mainType: z.string().nullable().optional(), | |
| 13 | + tissue: z.string().nullable().optional(), | |
| 14 | + parent: z.string().nullable().optional(), | |
| 15 | + level: z.number().int(), | |
| 16 | + externalReferences: z.record(z.string(), z.array(z.string())).default({}), | |
| 17 | + history: z.array(z.string()).default([]), | |
| 18 | + revocations: z.array(z.string()).default([]), | |
| 19 | + precursors: z.array(z.string()).default([]), | |
| 20 | + color: z.string().nullable().optional(), | |
| 21 | +}); | |
| 22 | +type TumorType = z.infer<typeof TumorType>; | |
| 23 | + | |
| 24 | +const HEME_RE = /lymphoid|myeloid|blood|leukemia|lymphoma|myeloma|histiocyt|mastocyt|plasma cell/i; | |
| 25 | +const BENIGN_RE = /\bbenign\b|borderline|\bnevus\b|\bnevi\b|\badenoma\b|hyperplasia|\bcyst\b|hamartoma|\blipoma\b|leiomyoma\b|\bfibroma\b|hemangioma\b|papilloma\b|dysplasia|in situ|\bpolyp\b|schwannoma\b|meningioma\b|paraganglioma\b|pheochromocytoma\b|teratoma\b|thymoma\b/i; | |
| 26 | + | |
| 27 | +/** | |
| 28 | + * OncoTree connector: maps every OncoTree node onto the canonical cancer set. | |
| 29 | + * - level-1 "tissue" nodes (parent TISSUE) are anatomical sites, not cancers → anatomical_sites | |
| 30 | + * - NCIt cross-reference → EXACT_IDENTIFIER match to the NCIt-derived canonical entity | |
| 31 | + * - otherwise preferred-name match → ONTOLOGY_EXACT | |
| 32 | + * - otherwise a new canonical entity is created *only* because OncoTree is a recognized ontology | |
| 33 | + * (CLAUDE.md §340) — flagged with primaryOncotreeCode and no NCIt code. | |
| 34 | + * The OncoTree parent/child structure is stored as hierarchy_type = "oncotree" (levels ≥ 2), and | |
| 35 | + * level-2 nodes are linked to their tissue via cancer_anatomy. | |
| 36 | + */ | |
| 37 | +export class OncotreeConnector extends Connector { | |
| 38 | + readonly manifest = manifest; | |
| 39 | + | |
| 40 | + async healthCheck(ctx: RunContext): Promise<ConnectorHealth> { | |
| 41 | + try { | |
| 42 | + const versions = await ctx.http.json<Array<{ api_identifier: string; release_date: string }>>(`${manifest.access.baseUrl}/versions`); | |
| 43 | + const stable = versions.find((v) => v.api_identifier === 'oncotree_latest_stable'); | |
| 44 | + return { status: 'healthy', detail: `latest stable ${stable?.release_date ?? '?'}` }; | |
| 45 | + } catch (e) { | |
| 46 | + return { status: 'failing', detail: (e as Error).message }; | |
| 47 | + } | |
| 48 | + } | |
| 49 | + | |
| 50 | + async sync(ctx: RunContext): Promise<void> { | |
| 51 | + const versions = await ctx.http.json<Array<{ api_identifier: string; release_date: string; visible: boolean }>>(`${manifest.access.baseUrl}/versions`); | |
| 52 | + const stable = versions.find((v) => v.api_identifier === 'oncotree_latest_stable'); | |
| 53 | + // Pin to the dated release identifier so the dataset version is reproducible. | |
| 54 | + const dated = versions.find((v) => v.release_date === stable?.release_date && v.api_identifier !== 'oncotree_latest_stable'); | |
| 55 | + const version = dated?.api_identifier ?? 'oncotree_latest_stable'; | |
| 56 | + ctx.datasetVersion = version; | |
| 57 | + ctx.info(`OncoTree version ${version}`); | |
| 58 | + | |
| 59 | + const raw = await ctx.http.json<unknown[]>(`${manifest.access.baseUrl}/tumorTypes?version=${encodeURIComponent(version)}`); | |
| 60 | + const nodes: TumorType[] = []; | |
| 61 | + for (const r of raw) { | |
| 62 | + const parsed = TumorType.safeParse(r); | |
| 63 | + if (!parsed.success) { | |
| 64 | + ctx.counters.validationFailures++; | |
| 65 | + ctx.counters.rejected++; | |
| 66 | + ctx.warn(`invalid tumor type record: ${parsed.error.issues[0]?.message}`); | |
| 67 | + continue; | |
| 68 | + } | |
| 69 | + nodes.push(parsed.data); | |
| 70 | + } | |
| 71 | + ctx.info(`fetched ${nodes.length} tumor types`); | |
| 72 | + if (nodes.length < 500) throw new Error(`anomaly: only ${nodes.length} OncoTree nodes returned (expected ~900) — refusing to persist (CLAUDE.md §171)`); | |
| 73 | + if (ctx.mode === 'dry_run') { | |
| 74 | + ctx.counters.fetched = nodes.length; | |
| 75 | + return; | |
| 76 | + } | |
| 77 | + | |
| 78 | + const resolver = new CancerResolver(ctx.db); | |
| 79 | + await resolver.warm(); | |
| 80 | + const byCode = new Map(nodes.map((n) => [n.code, n])); | |
| 81 | + const canonicalByOncotree = new Map<string, string>(); | |
| 82 | + const siteByOncotree = new Map<string, string>(); | |
| 83 | + await ctx.addProvenance({ dataset: 'OncoTree tumor types', datasetVersion: version, sourceUrl: `${manifest.access.baseUrl}/tumorTypes?version=${version}`, evidenceType: 'expert_curation', accessLevel: 'open', methodology: 'OncoTree curated tumor type hierarchy' }); | |
| 84 | + | |
| 85 | + // Pass 0: tissue roots → anatomical sites | |
| 86 | + for (const node of nodes.filter((n) => n.parent === 'TISSUE')) { | |
| 87 | + await ctx.upsertSourceRecord('tissue', node.code, node); | |
| 88 | + const slug = slugify(node.name); | |
| 89 | + const ncit = node.externalReferences['NCI']?.[0] ?? null; | |
| 90 | + const [existing] = await ctx.db.select({ id: anatomicalSites.id }).from(anatomicalSites).where(eq(anatomicalSites.slug, slug)).limit(1); | |
| 91 | + let siteId = existing?.id; | |
| 92 | + if (!siteId) { | |
| 93 | + siteId = await mintId(ctx.db, 'ANAT'); | |
| 94 | + await ctx.db.insert(anatomicalSites).values({ id: siteId, name: node.name, slug, ncitCode: ncit, system: node.tissue ?? node.name }); | |
| 95 | + } else await ctx.db.update(anatomicalSites).set({ name: node.name, ncitCode: ncit }).where(eq(anatomicalSites.id, siteId)); | |
| 96 | + siteByOncotree.set(node.code, siteId); | |
| 97 | + } | |
| 98 | + | |
| 99 | + // Pass 1: resolve / create canonical entities (parents before children thanks to level ordering). | |
| 100 | + for (const node of [...nodes].filter((n) => n.parent !== 'TISSUE').sort((a, b) => a.level - b.level)) { | |
| 101 | + const rec = await ctx.upsertSourceRecord('tumor_type', node.code, node); | |
| 102 | + let cancerId: string | null = null; | |
| 103 | + let matchType = 'UNRESOLVED'; | |
| 104 | + const ncitCodes = node.externalReferences['NCI'] ?? []; | |
| 105 | + for (const c of ncitCodes) { | |
| 106 | + const hit = resolver.byCode('ncit', c); | |
| 107 | + if (hit) { | |
| 108 | + cancerId = hit.cancerId; | |
| 109 | + matchType = hit.matchType; | |
| 110 | + break; | |
| 111 | + } | |
| 112 | + } | |
| 113 | + if (!cancerId) { | |
| 114 | + const hit = resolver.byCode('oncotree', node.code); | |
| 115 | + if (hit) { | |
| 116 | + cancerId = hit.cancerId; | |
| 117 | + matchType = hit.matchType; | |
| 118 | + } | |
| 119 | + } | |
| 120 | + if (!cancerId) { | |
| 121 | + const hit = resolver.byLabel(node.name); | |
| 122 | + if (hit && hit.matchType === 'ONTOLOGY_EXACT') { | |
| 123 | + cancerId = hit.cancerId; | |
| 124 | + matchType = hit.matchType; | |
| 125 | + } | |
| 126 | + } | |
| 127 | + if (!cancerId) { | |
| 128 | + // Recognized ontology node without NCIt anchor → new canonical entity (CLAUDE.md §340 satisfied: OncoTree is a recognized ontology). | |
| 129 | + const slugBase = slugify(node.name); | |
| 130 | + let slug = slugBase; | |
| 131 | + for (let i = 2; ; i++) { | |
| 132 | + const [clash] = await ctx.db.select({ id: cancers.id }).from(cancers).where(eq(cancers.slug, slug)).limit(1); | |
| 133 | + if (!clash) break; | |
| 134 | + slug = `${slugBase}-${i}`; | |
| 135 | + } | |
| 136 | + const heme = HEME_RE.test(`${node.tissue ?? ''} ${node.mainType ?? ''} ${node.name}`); | |
| 137 | + cancerId = await mintId(ctx.db, 'CAN'); | |
| 138 | + await ctx.db.insert(cancers).values({ | |
| 139 | + id: cancerId, | |
| 140 | + slug, | |
| 141 | + canonicalName: node.name, | |
| 142 | + entityType: node.level >= 3 ? 'subtype' : heme ? 'hematologic_malignancy' : 'cancer', | |
| 143 | + malignant: !BENIGN_RE.test(node.name), | |
| 144 | + solidTumor: !heme, | |
| 145 | + hematologic: heme, | |
| 146 | + primaryOncotreeCode: node.code, | |
| 147 | + depth: node.level, | |
| 148 | + classificationVersion: version, | |
| 149 | + }); | |
| 150 | + await ctx.db.insert(cancerAliases).values({ cancerId, alias: node.name, normalized: normalizeLabel(node.name), aliasType: 'preferred', sourceId: ctx.sourceId, sourceTerminology: 'OncoTree' }).onConflictDoNothing(); | |
| 151 | + matchType = 'EXACT_IDENTIFIER'; | |
| 152 | + await ctx.recordChange('cancer', cancerId, 'created', `Created from OncoTree ${node.code} (${node.name})`); | |
| 153 | + } else { | |
| 154 | + await ctx.db.insert(cancerAliases).values({ cancerId, alias: node.name, normalized: normalizeLabel(node.name), aliasType: 'synonym', sourceId: ctx.sourceId, sourceTerminology: 'OncoTree' }).onConflictDoNothing(); | |
| 155 | + } | |
| 156 | + canonicalByOncotree.set(node.code, cancerId); | |
| 157 | + await ctx.db.insert(cancerCodes).values({ cancerId, system: 'oncotree', code: node.code, matchType, sourceId: ctx.sourceId, validFrom: stable?.release_date }).onConflictDoNothing(); | |
| 158 | + for (const u of node.externalReferences['UMLS'] ?? []) await ctx.db.insert(cancerCodes).values({ cancerId, system: 'umls', code: u, matchType: 'EXACT_IDENTIFIER', sourceId: ctx.sourceId }).onConflictDoNothing(); | |
| 159 | + for (const c of ncitCodes) await ctx.db.insert(cancerCodes).values({ cancerId, system: 'ncit', code: c, matchType: 'EXACT_IDENTIFIER', sourceId: ctx.sourceId }).onConflictDoNothing(); | |
| 160 | + // Record the OncoTree code on the entity only when it has none yet (first mapping wins; others stay in cancer_codes). | |
| 161 | + await ctx.db.execute(sql`UPDATE cancers SET primary_oncotree_code = ${node.code} WHERE id = ${cancerId} AND primary_oncotree_code IS NULL`); | |
| 162 | + // Level-2 nodes hang directly under a tissue → anatomical relation. | |
| 163 | + if (node.parent && siteByOncotree.has(node.parent)) { | |
| 164 | + await ctx.db.insert(cancerAnatomy).values({ cancerId, siteId: siteByOncotree.get(node.parent)!, relation: 'primary', sourceId: ctx.sourceId }).onConflictDoNothing(); | |
| 165 | + } | |
| 166 | + void rec; | |
| 167 | + } | |
| 168 | + | |
| 169 | + // Pass 2: hierarchy edges (oncotree dimension), levels ≥ 2 only. | |
| 170 | + let edges = 0; | |
| 171 | + for (const node of nodes) { | |
| 172 | + if (!node.parent || node.parent === 'TISSUE' || siteByOncotree.has(node.parent)) continue; | |
| 173 | + const childId = canonicalByOncotree.get(node.code); | |
| 174 | + const parentId = canonicalByOncotree.get(node.parent); | |
| 175 | + if (!childId || !parentId || childId === parentId) continue; | |
| 176 | + if (!byCode.has(node.parent)) continue; | |
| 177 | + await ctx.db.insert(cancerHierarchy).values({ parentId, childId, hierarchyType: 'oncotree', sourceId: ctx.sourceId }).onConflictDoNothing(); | |
| 178 | + edges++; | |
| 179 | + } | |
| 180 | + ctx.info(`tissues ${siteByOncotree.size}; canonical mapped ${canonicalByOncotree.size}; hierarchy edges ${edges}`); | |
| 181 | + ctx.cursor = { version, syncedAt: new Date().toISOString() }; | |
| 182 | + } | |
| 183 | +} | |
| 184 | + | |
| 185 | +export const connector = new OncotreeConnector(); | |
added
packages/connectors/src/connectors/oncotree/manifest.ts
+34 −0
@@ -0,0 +1,34 @@ | ||
| 1 | +import { defineManifest } from '../../sdk/manifest.js'; | |
| 2 | + | |
| 3 | +/** | |
| 4 | + * Docs verified 2026-09-08: https://oncotree.mskcc.org (API tab) — GET /api/tumorTypes?version=…, | |
| 5 | + * GET /api/versions, GET /api/mainTypes. Response fields: code, name, mainType, tissue, parent, | |
| 6 | + * level, externalReferences{UMLS[],NCI[]}, history[], revocations[], precursors[], color. | |
| 7 | + * License (repo README, cBioPortal/oncotree): CC BY 4.0. | |
| 8 | + */ | |
| 9 | +export const manifest = defineManifest({ | |
| 10 | + id: 'oncotree', | |
| 11 | + name: 'OncoTree', | |
| 12 | + organization: 'Memorial Sloan Kettering Cancer Center / cBioPortal', | |
| 13 | + category: 'terminology', | |
| 14 | + tier: 0, | |
| 15 | + description: 'Open cancer classification used by cBioPortal and clinical sequencing programs. Provides a tissue-based tumor type hierarchy with NCIt and UMLS cross-references. Used as a second hierarchy dimension and to anchor cohort/genomic sources.', | |
| 16 | + homepage: 'https://oncotree.mskcc.org', | |
| 17 | + docsUrl: 'https://oncotree.mskcc.org/#/home?tab=api', | |
| 18 | + termsUrl: 'https://github.com/cBioPortal/oncotree/blob/master/LICENSE', | |
| 19 | + access: { type: 'api', auth: 'none', baseUrl: 'https://oncotree.mskcc.org/api' }, | |
| 20 | + license: 'CC BY 4.0', | |
| 21 | + licenseStatus: 'approved', | |
| 22 | + commercialUse: 'allowed', | |
| 23 | + redistribution: 'attribution', | |
| 24 | + attribution: 'OncoTree (Memorial Sloan Kettering Cancer Center), CC BY 4.0, https://oncotree.mskcc.org', | |
| 25 | + termsReviewedAt: '2026-09-08', | |
| 26 | + updateFrequency: 'Stable releases a few times per year', | |
| 27 | + supportsIncrementalSync: false, | |
| 28 | + entities: ['cancer', 'cancer_hierarchy', 'cancer_codes'], | |
| 29 | + rateLimits: { requestsPerSecond: 2, maxConcurrency: 1 }, | |
| 30 | + rawRetention: 'full', | |
| 31 | + documentationVerifiedAt: '2026-09-08', | |
| 32 | + status: 'active', | |
| 33 | + schedule: '0 4 * * 1', | |
| 34 | +}); | |
added
packages/connectors/src/index.ts
+4 −0
@@ -0,0 +1,4 @@ | ||
| 1 | +export * from './sdk/index.js'; | |
| 2 | +export * from './registry.js'; | |
| 3 | +export * from './sources-sync.js'; | |
| 4 | +export * from './planned.js'; | |
added
packages/connectors/src/planned.ts
+60 −0
@@ -0,0 +1,60 @@ | ||
| 1 | +import { defineManifest, type ConnectorManifest } from './sdk/manifest.js'; | |
| 2 | + | |
| 3 | +/** | |
| 4 | + * Sources that are registered (visible on /sources with their compliance status) but have no | |
| 5 | + * ingesting connector yet — either because license review is pending (CLAUDE.md §10.4, §142) or | |
| 6 | + * because credentials are required. Nothing here ingests data. | |
| 7 | + */ | |
| 8 | +export const PLANNED_MANIFESTS: ConnectorManifest[] = [ | |
| 9 | + defineManifest({ | |
| 10 | + id: 'iarc-globocan', | |
| 11 | + name: 'IARC Global Cancer Observatory (GLOBOCAN)', | |
| 12 | + organization: 'International Agency for Research on Cancer (WHO)', | |
| 13 | + category: 'epidemiology', | |
| 14 | + tier: 0, | |
| 15 | + description: 'Global estimates of cancer incidence, mortality and prevalence by country, sex, age and cancer site. The intended global epidemiology layer of CancerIndex.', | |
| 16 | + homepage: 'https://gco.iarc.who.int/', | |
| 17 | + docsUrl: 'https://gco.iarc.who.int/today/en/about', | |
| 18 | + termsUrl: 'https://gco.iarc.who.int/en/terms-of-use', | |
| 19 | + access: { type: 'api', auth: 'none', baseUrl: 'https://gco.iarc.who.int/' }, | |
| 20 | + license: 'IARC/WHO terms of use — redistribution rights under review', | |
| 21 | + licenseStatus: 'review', | |
| 22 | + commercialUse: 'unknown', | |
| 23 | + redistribution: 'unknown', | |
| 24 | + attribution: 'Ferlay J, Ervik M, Lam F, et al. Global Cancer Observatory: Cancer Today. Lyon, France: IARC.', | |
| 25 | + termsNotes: | |
| 26 | + 'Terms page is a JavaScript application; automated review on 2026-09-08 could not extract the text. Per CLAUDE.md §10.4 no automated bulk ingestion or republication of GLOBOCAN estimates until a human has reviewed the terms and, if required, obtained permission from IARC. Country/global rankings therefore remain unavailable and are shown as "awaiting license review".', | |
| 27 | + updateFrequency: 'Major releases every ~2 years (GLOBOCAN 2022 current)', | |
| 28 | + supportsIncrementalSync: false, | |
| 29 | + entities: ['epidemiology_observations'], | |
| 30 | + metrics: ['incidence_count', 'mortality_count', 'as_incidence_rate', 'as_mortality_rate', 'prevalence_5y'], | |
| 31 | + rateLimits: { requestsPerSecond: 1, maxConcurrency: 1 }, | |
| 32 | + documentationVerifiedAt: '2026-09-08', | |
| 33 | + status: 'review', | |
| 34 | + }), | |
| 35 | + defineManifest({ | |
| 36 | + id: 'seer', | |
| 37 | + name: 'SEER (Surveillance, Epidemiology, and End Results)', | |
| 38 | + organization: 'National Cancer Institute', | |
| 39 | + category: 'epidemiology', | |
| 40 | + tier: 0, | |
| 41 | + description: 'US population-based registry statistics: incidence, mortality, survival, stage, age, sex, race/ethnicity. Authoritative for US relative survival.', | |
| 42 | + homepage: 'https://seer.cancer.gov/', | |
| 43 | + docsUrl: 'https://api.seer.cancer.gov/', | |
| 44 | + termsUrl: 'https://seer.cancer.gov/about/terms.html', | |
| 45 | + access: { type: 'api', auth: 'api_key', baseUrl: 'https://api.seer.cancer.gov/rest/' }, | |
| 46 | + license: 'US Government work (public domain); SEER*Explorer statistics citable with attribution; API requires a free key', | |
| 47 | + licenseStatus: 'review', | |
| 48 | + commercialUse: 'unknown', | |
| 49 | + redistribution: 'attribution', | |
| 50 | + attribution: 'Surveillance, Epidemiology, and End Results (SEER) Program (www.seer.cancer.gov), National Cancer Institute.', | |
| 51 | + termsNotes: 'Connector is implemented against the SEER API only once a key is present (SEER_API_KEY). Status awaiting_credentials until then. Never present SEER (US) estimates as global.', | |
| 52 | + updateFrequency: 'Annual (April)', | |
| 53 | + supportsIncrementalSync: false, | |
| 54 | + entities: ['epidemiology_observations', 'survival_observations'], | |
| 55 | + metrics: ['incidence_count', 'as_incidence_rate', 'mortality_count', 'as_mortality_rate', 'five_year_survival'], | |
| 56 | + rateLimits: { requestsPerSecond: 2, maxConcurrency: 1 }, | |
| 57 | + documentationVerifiedAt: '2026-09-08', | |
| 58 | + status: 'awaiting_credentials', | |
| 59 | + }), | |
| 60 | +]; | |
added
packages/connectors/src/registry.ts
+13 −0
@@ -0,0 +1,13 @@ | ||
| 1 | +import type { Connector } from './sdk/run.js'; | |
| 2 | +import { connector as oncotree } from './connectors/oncotree/index.js'; | |
| 3 | + | |
| 4 | +/** | |
| 5 | + * Connector registry. Order = recommended first-run order (CLAUDE.md §351): terminology first, | |
| 6 | + * then genes, then genomics/trials/literature/variants/evidence, then epidemiology. | |
| 7 | + * Add new connectors here; `pnpm cix sources:sync` seeds their manifests into `sources`. | |
| 8 | + */ | |
| 9 | +export const CONNECTORS: Connector[] = [oncotree]; | |
| 10 | + | |
| 11 | +export function getConnector(id: string): Connector | undefined { | |
| 12 | + return CONNECTORS.find((c) => c.manifest.id === id); | |
| 13 | +} | |
added
packages/connectors/src/sdk/http.ts
+162 −0
@@ -0,0 +1,162 @@ | ||
| 1 | +import { setTimeout as sleep } from 'node:timers/promises'; | |
| 2 | +import type { ConnectorManifest } from './manifest.js'; | |
| 3 | + | |
| 4 | +export interface HttpStats { | |
| 5 | + requests: number; | |
| 6 | + failures: number; | |
| 7 | + rateLimitEvents: number; | |
| 8 | + retries: number; | |
| 9 | +} | |
| 10 | + | |
| 11 | +export class HttpError extends Error { | |
| 12 | + constructor( | |
| 13 | + public readonly status: number, | |
| 14 | + public readonly url: string, | |
| 15 | + public readonly bodySnippet: string, | |
| 16 | + ) { | |
| 17 | + super(`HTTP ${status} for ${url}: ${bodySnippet.slice(0, 200)}`); | |
| 18 | + } | |
| 19 | +} | |
| 20 | + | |
| 21 | +/** Token bucket rate limiter (CLAUDE.md §229). */ | |
| 22 | +class TokenBucket { | |
| 23 | + private tokens: number; | |
| 24 | + private last = Date.now(); | |
| 25 | + constructor( | |
| 26 | + private readonly ratePerSec: number, | |
| 27 | + private readonly burst: number, | |
| 28 | + ) { | |
| 29 | + this.tokens = burst; | |
| 30 | + } | |
| 31 | + async take(): Promise<void> { | |
| 32 | + for (;;) { | |
| 33 | + const now = Date.now(); | |
| 34 | + this.tokens = Math.min(this.burst, this.tokens + ((now - this.last) / 1000) * this.ratePerSec); | |
| 35 | + this.last = now; | |
| 36 | + if (this.tokens >= 1) { | |
| 37 | + this.tokens -= 1; | |
| 38 | + return; | |
| 39 | + } | |
| 40 | + await sleep(Math.ceil(((1 - this.tokens) / this.ratePerSec) * 1000)); | |
| 41 | + } | |
| 42 | + } | |
| 43 | +} | |
| 44 | + | |
| 45 | +class Semaphore { | |
| 46 | + private queue: Array<() => void> = []; | |
| 47 | + private active = 0; | |
| 48 | + constructor(private readonly max: number) {} | |
| 49 | + async acquire(): Promise<() => void> { | |
| 50 | + if (this.active >= this.max) await new Promise<void>((resolve) => this.queue.push(resolve)); | |
| 51 | + this.active++; | |
| 52 | + let released = false; | |
| 53 | + return () => { | |
| 54 | + if (released) return; | |
| 55 | + released = true; | |
| 56 | + this.active--; | |
| 57 | + const next = this.queue.shift(); | |
| 58 | + if (next) next(); | |
| 59 | + }; | |
| 60 | + } | |
| 61 | +} | |
| 62 | + | |
| 63 | +export interface HttpClientOptions { | |
| 64 | + userAgent?: string; | |
| 65 | + timeoutMs?: number; | |
| 66 | + headers?: Record<string, string>; | |
| 67 | +} | |
| 68 | + | |
| 69 | +/** | |
| 70 | + * Source-aware HTTP client: token bucket, bounded concurrency, exponential backoff with jitter, | |
| 71 | + * Retry-After respect, request accounting for the ingest run. | |
| 72 | + */ | |
| 73 | +export class HttpClient { | |
| 74 | + readonly stats: HttpStats = { requests: 0, failures: 0, rateLimitEvents: 0, retries: 0 }; | |
| 75 | + private readonly bucket: TokenBucket | null; | |
| 76 | + private readonly sem: Semaphore; | |
| 77 | + private readonly retry: NonNullable<ConnectorManifest['retryPolicy']>; | |
| 78 | + private readonly ua: string; | |
| 79 | + private readonly timeoutMs: number; | |
| 80 | + private readonly headers: Record<string, string>; | |
| 81 | + | |
| 82 | + constructor(manifest: ConnectorManifest, opts: HttpClientOptions = {}) { | |
| 83 | + const rps = manifest.rateLimits.requestsPerSecond ?? (manifest.rateLimits.requestsPerMinute ? manifest.rateLimits.requestsPerMinute / 60 : undefined); | |
| 84 | + this.bucket = rps ? new TokenBucket(rps, Math.max(1, Math.ceil(rps))) : null; | |
| 85 | + this.sem = new Semaphore(manifest.rateLimits.maxConcurrency); | |
| 86 | + this.retry = manifest.retryPolicy; | |
| 87 | + // Plain product token only: some WAFs (e.g. oncotree.mskcc.org, Akamai) reject UAs containing URLs/parentheses. | |
| 88 | + this.ua = opts.userAgent ?? `CancerIndex/0.1`; | |
| 89 | + this.timeoutMs = opts.timeoutMs ?? 60_000; | |
| 90 | + this.headers = opts.headers ?? {}; | |
| 91 | + } | |
| 92 | + | |
| 93 | + /** One attempt: returns the Response (any status) or throws on network error. */ | |
| 94 | + private async attempt(url: string, init: RequestInit): Promise<Response> { | |
| 95 | + const release = await this.sem.acquire(); | |
| 96 | + try { | |
| 97 | + if (this.bucket) await this.bucket.take(); | |
| 98 | + this.stats.requests++; | |
| 99 | + const controller = new AbortController(); | |
| 100 | + const timer = setTimeout(() => controller.abort(), this.timeoutMs); | |
| 101 | + try { | |
| 102 | + return await fetch(url, { | |
| 103 | + ...init, | |
| 104 | + headers: { 'user-agent': this.ua, accept: 'application/json, text/plain, */*', ...this.headers, ...(init.headers as Record<string, string> | undefined) }, | |
| 105 | + signal: controller.signal, | |
| 106 | + }); | |
| 107 | + } finally { | |
| 108 | + clearTimeout(timer); | |
| 109 | + } | |
| 110 | + } finally { | |
| 111 | + release(); | |
| 112 | + } | |
| 113 | + } | |
| 114 | + | |
| 115 | + async request(url: string, init: RequestInit = {}): Promise<Response> { | |
| 116 | + for (let attempt = 0; ; attempt++) { | |
| 117 | + let res: Response; | |
| 118 | + try { | |
| 119 | + res = await this.attempt(url, init); | |
| 120 | + } catch (err) { | |
| 121 | + if (attempt >= this.retry.maxRetries) { | |
| 122 | + this.stats.failures++; | |
| 123 | + throw err; | |
| 124 | + } | |
| 125 | + this.stats.retries++; | |
| 126 | + await sleep(this.backoff(attempt)); | |
| 127 | + continue; | |
| 128 | + } | |
| 129 | + if (res.ok) return res; | |
| 130 | + const body = await res.text().catch(() => ''); | |
| 131 | + if (res.status === 429) this.stats.rateLimitEvents++; | |
| 132 | + const retryable = res.status === 429 || res.status >= 500 || res.status === 408; | |
| 133 | + if (!retryable || attempt >= this.retry.maxRetries) { | |
| 134 | + this.stats.failures++; | |
| 135 | + throw new HttpError(res.status, url, body); | |
| 136 | + } | |
| 137 | + const retryAfter = Number(res.headers.get('retry-after')); | |
| 138 | + const delay = Number.isFinite(retryAfter) && retryAfter > 0 ? Math.min(retryAfter * 1000, 120_000) : this.backoff(attempt); | |
| 139 | + this.stats.retries++; | |
| 140 | + await sleep(delay); | |
| 141 | + } | |
| 142 | + } | |
| 143 | + | |
| 144 | + async json<T = unknown>(url: string, init?: RequestInit): Promise<T> { | |
| 145 | + const res = await this.request(url, init); | |
| 146 | + return (await res.json()) as T; | |
| 147 | + } | |
| 148 | + | |
| 149 | + async text(url: string, init?: RequestInit): Promise<string> { | |
| 150 | + const res = await this.request(url, init); | |
| 151 | + return await res.text(); | |
| 152 | + } | |
| 153 | + | |
| 154 | + async postJson<T = unknown>(url: string, body: unknown, init: RequestInit = {}): Promise<T> { | |
| 155 | + return this.json<T>(url, { ...init, method: 'POST', headers: { 'content-type': 'application/json', ...(init.headers as Record<string, string> | undefined) }, body: JSON.stringify(body) }); | |
| 156 | + } | |
| 157 | + | |
| 158 | + private backoff(attempt: number): number { | |
| 159 | + const base = Math.min(this.retry.maxDelayMs, this.retry.baseDelayMs * 2 ** attempt); | |
| 160 | + return Math.round(base / 2 + Math.random() * (base / 2)); | |
| 161 | + } | |
| 162 | +} | |
added
packages/connectors/src/sdk/index.ts
+5 −0
@@ -0,0 +1,5 @@ | ||
| 1 | +export * from './manifest.js'; | |
| 2 | +export * from './http.js'; | |
| 3 | +export * from './lake.js'; | |
| 4 | +export * from './run.js'; | |
| 5 | +export * from './validate.js'; | |
added
packages/connectors/src/sdk/lake.ts
+105 −0
@@ -0,0 +1,105 @@ | ||
| 1 | +import { createWriteStream, existsSync, mkdirSync, createReadStream } from 'node:fs'; | |
| 2 | +import { mkdir, stat } from 'node:fs/promises'; | |
| 3 | +import path from 'node:path'; | |
| 4 | +import { createGzip, createGunzip } from 'node:zlib'; | |
| 5 | +import { pipeline } from 'node:stream/promises'; | |
| 6 | +import { createInterface } from 'node:readline'; | |
| 7 | +import { dataDir } from '@cancerindex/shared'; | |
| 8 | + | |
| 9 | +/** | |
| 10 | + * Raw data lake (CLAUDE.md §26): every source payload retained when licensing allows. | |
| 11 | + * Layout: {CI_DATA_DIR}/raw/{source}/{YYYY-MM-DD}/{entity}/{runId}-{part}.jsonl.gz | |
| 12 | + * A record's `rawPath` is "<file>#<line>" so any canonical value can be traced to its raw payload. | |
| 13 | + */ | |
| 14 | +export class RawLake { | |
| 15 | + private readonly root: string; | |
| 16 | + private writers = new Map<string, { path: string; stream: ReturnType<typeof createWriteStream>; gz: ReturnType<typeof createGzip>; lines: number; part: number; bytes: number }>(); | |
| 17 | + constructor( | |
| 18 | + private readonly source: string, | |
| 19 | + private readonly runId: string, | |
| 20 | + private readonly date = new Date(), | |
| 21 | + root?: string, | |
| 22 | + ) { | |
| 23 | + this.root = root ?? path.join(dataDir(), 'raw'); | |
| 24 | + } | |
| 25 | + | |
| 26 | + dir(entity: string): string { | |
| 27 | + const day = this.date.toISOString().slice(0, 10); | |
| 28 | + return path.join(this.root, this.source, day, entity); | |
| 29 | + } | |
| 30 | + | |
| 31 | + /** Append one raw JSON payload; returns its rawPath reference. */ | |
| 32 | + async put(entity: string, payload: unknown): Promise<string> { | |
| 33 | + let w = this.writers.get(entity); | |
| 34 | + if (!w || w.bytes > 64 * 1024 * 1024) { | |
| 35 | + if (w) await this.closeWriter(entity); | |
| 36 | + const part = (w?.part ?? 0) + 1; | |
| 37 | + const dir = this.dir(entity); | |
| 38 | + if (!existsSync(dir)) mkdirSync(dir, { recursive: true }); | |
| 39 | + const file = path.join(dir, `${this.runId}-${String(part).padStart(3, '0')}.jsonl.gz`); | |
| 40 | + const stream = createWriteStream(file); | |
| 41 | + const gz = createGzip({ level: 6 }); | |
| 42 | + gz.pipe(stream); | |
| 43 | + w = { path: file, stream, gz, lines: 0, part, bytes: 0 }; | |
| 44 | + this.writers.set(entity, w); | |
| 45 | + } | |
| 46 | + const line = JSON.stringify(payload) + '\n'; | |
| 47 | + w.bytes += line.length; | |
| 48 | + const ref = `${w.path}#${w.lines}`; | |
| 49 | + w.lines++; | |
| 50 | + if (!w.gz.write(line)) await new Promise<void>((r) => w!.gz.once('drain', () => r())); | |
| 51 | + return ref; | |
| 52 | + } | |
| 53 | + | |
| 54 | + private async closeWriter(entity: string): Promise<void> { | |
| 55 | + const w = this.writers.get(entity); | |
| 56 | + if (!w) return; | |
| 57 | + await new Promise<void>((resolve, reject) => { | |
| 58 | + w.stream.once('finish', () => resolve()); | |
| 59 | + w.stream.once('error', reject); | |
| 60 | + w.gz.end(); | |
| 61 | + }); | |
| 62 | + this.writers.delete(entity); | |
| 63 | + } | |
| 64 | + | |
| 65 | + async close(): Promise<void> { | |
| 66 | + for (const entity of [...this.writers.keys()]) await this.closeWriter(entity); | |
| 67 | + } | |
| 68 | + | |
| 69 | + /** Read a raw payload back by reference (admin TRACE, reprocessing). */ | |
| 70 | + static async read(ref: string): Promise<unknown | null> { | |
| 71 | + const [file, lineStr] = ref.split('#'); | |
| 72 | + if (!file || lineStr === undefined) return null; | |
| 73 | + const target = Number(lineStr); | |
| 74 | + const rl = createInterface({ input: createReadStream(file).pipe(createGunzip()) }); | |
| 75 | + let i = 0; | |
| 76 | + for await (const line of rl) { | |
| 77 | + if (i === target) { | |
| 78 | + rl.close(); | |
| 79 | + return JSON.parse(line); | |
| 80 | + } | |
| 81 | + i++; | |
| 82 | + } | |
| 83 | + return null; | |
| 84 | + } | |
| 85 | +} | |
| 86 | + | |
| 87 | +/** Download a bulk file to the lake (CLAUDE.md §228-230) with checksum + size recorded. */ | |
| 88 | +export async function downloadBulk(url: string, dest: string, fetchImpl: typeof fetch = fetch, headers: Record<string, string> = {}): Promise<{ path: string; bytes: number; sha256: string }> { | |
| 89 | + await mkdir(path.dirname(dest), { recursive: true }); | |
| 90 | + const res = await fetchImpl(url, { headers: { 'user-agent': 'CancerIndex/0.1 (+https://www.cancerindex.io)', ...headers } }); | |
| 91 | + if (!res.ok || !res.body) throw new Error(`bulk download failed ${res.status} ${url}`); | |
| 92 | + const { createHash } = await import('node:crypto'); | |
| 93 | + const hash = createHash('sha256'); | |
| 94 | + const { Transform } = await import('node:stream'); | |
| 95 | + const tap = new Transform({ | |
| 96 | + transform(chunk, _enc, cb) { | |
| 97 | + hash.update(chunk); | |
| 98 | + cb(null, chunk); | |
| 99 | + }, | |
| 100 | + }); | |
| 101 | + const { Readable } = await import('node:stream'); | |
| 102 | + await pipeline(Readable.fromWeb(res.body as never), tap, createWriteStream(dest)); | |
| 103 | + const s = await stat(dest); | |
| 104 | + return { path: dest, bytes: s.size, sha256: hash.digest('hex') }; | |
| 105 | +} | |
added
packages/connectors/src/sdk/manifest.ts
+61 −0
@@ -0,0 +1,61 @@ | ||
| 1 | +import { z } from 'zod'; | |
| 2 | + | |
| 3 | +/** Connector manifest (CLAUDE.md §9). Seeds the `sources` table; drives the public /source pages. */ | |
| 4 | +export const ConnectorManifest = z.object({ | |
| 5 | + id: z.string().regex(/^[a-z0-9-]+$/), | |
| 6 | + name: z.string(), | |
| 7 | + organization: z.string(), | |
| 8 | + category: z.enum(['terminology', 'genes', 'genomics', 'variants', 'epidemiology', 'trials', 'literature', 'drugs', 'regulatory', 'evidence', 'pathways', 'funding', 'guidelines']), | |
| 9 | + tier: z.number().int().min(0).max(11), | |
| 10 | + description: z.string(), | |
| 11 | + homepage: z.string().url(), | |
| 12 | + docsUrl: z.string().url().optional(), | |
| 13 | + termsUrl: z.string().url().optional(), | |
| 14 | + access: z.object({ | |
| 15 | + type: z.enum(['api', 'bulk', 'rss', 'ftp', 'graphql', 'rest', 'scrape', 'manual']), | |
| 16 | + auth: z.enum(['none', 'api_key', 'oauth', 'account', 'controlled']), | |
| 17 | + baseUrl: z.string().url().optional(), | |
| 18 | + }), | |
| 19 | + license: z.string(), | |
| 20 | + licenseStatus: z.enum(['review', 'approved', 'restricted', 'blocked']), | |
| 21 | + commercialUse: z.enum(['allowed', 'restricted', 'prohibited', 'unknown']), | |
| 22 | + redistribution: z.enum(['allowed', 'attribution', 'restricted', 'prohibited', 'unknown']), | |
| 23 | + attribution: z.string().optional(), | |
| 24 | + termsReviewedAt: z.string().optional(), // YYYY-MM-DD, human review date | |
| 25 | + termsNotes: z.string().optional(), | |
| 26 | + updateFrequency: z.string(), | |
| 27 | + expectedLatency: z.string().optional(), | |
| 28 | + supportsIncrementalSync: z.boolean(), | |
| 29 | + entities: z.array(z.string()), | |
| 30 | + metrics: z.array(z.string()).default([]), | |
| 31 | + rateLimits: z.object({ | |
| 32 | + requestsPerSecond: z.number().positive().optional(), | |
| 33 | + requestsPerMinute: z.number().positive().optional(), | |
| 34 | + maxConcurrency: z.number().int().positive().default(1), | |
| 35 | + notes: z.string().optional(), | |
| 36 | + }), | |
| 37 | + retryPolicy: z | |
| 38 | + .object({ | |
| 39 | + maxRetries: z.number().int().min(0).default(4), | |
| 40 | + baseDelayMs: z.number().int().min(0).default(500), | |
| 41 | + maxDelayMs: z.number().int().min(0).default(30_000), | |
| 42 | + }) | |
| 43 | + .default({ maxRetries: 4, baseDelayMs: 500, maxDelayMs: 30_000 }), | |
| 44 | + rawRetention: z.enum(['full', 'summary', 'none']).default('full'), | |
| 45 | + schemaVersion: z.string().default('1'), | |
| 46 | + documentationVerifiedAt: z.string(), // YYYY-MM-DD, the date official docs were checked (CLAUDE.md §224-225) | |
| 47 | + owner: z.string().default('cancerindex'), | |
| 48 | + status: z.enum(['planned', 'active', 'paused', 'awaiting_credentials', 'review', 'retired']), | |
| 49 | + schedule: z.string().optional(), // cron expression for the worker scheduler | |
| 50 | + anomalyGuard: z | |
| 51 | + .object({ | |
| 52 | + /** Refuse to mark records source_missing when the fetched count drops below this ratio of the previous run. */ | |
| 53 | + minRatioOfPrevious: z.number().min(0).max(1).default(0.5), | |
| 54 | + }) | |
| 55 | + .default({ minRatioOfPrevious: 0.5 }), | |
| 56 | +}); | |
| 57 | +export type ConnectorManifest = z.infer<typeof ConnectorManifest>; | |
| 58 | + | |
| 59 | +export function defineManifest(m: z.input<typeof ConnectorManifest>): ConnectorManifest { | |
| 60 | + return ConnectorManifest.parse(m); | |
| 61 | +} | |
added
packages/connectors/src/sdk/run.ts
+0 −0
Binary file not shown.
added
packages/connectors/src/sdk/validate.ts
+49 −0
@@ -0,0 +1,49 @@ | ||
| 1 | +/** Scientific invariants applied to normalized records (CLAUDE.md §110, §154). */ | |
| 2 | + | |
| 3 | +export interface ValidationIssue { | |
| 4 | + field: string; | |
| 5 | + message: string; | |
| 6 | +} | |
| 7 | + | |
| 8 | +export function validateEpidemiology(o: { metric: string; value: number; year: number; lowerCi?: number | null; upperCi?: number | null; unit: string }): ValidationIssue[] { | |
| 9 | + const issues: ValidationIssue[] = []; | |
| 10 | + if (!Number.isFinite(o.value)) issues.push({ field: 'value', message: 'not finite' }); | |
| 11 | + if (o.value < 0) issues.push({ field: 'value', message: 'negative count/rate' }); | |
| 12 | + if (o.year < 1900 || o.year > new Date().getFullYear() + 30) issues.push({ field: 'year', message: `implausible year ${o.year}` }); | |
| 13 | + if (o.lowerCi != null && o.lowerCi > o.value) issues.push({ field: 'lowerCi', message: 'lowerCI > estimate' }); | |
| 14 | + if (o.upperCi != null && o.upperCi < o.value) issues.push({ field: 'upperCi', message: 'upperCI < estimate' }); | |
| 15 | + if (o.metric.endsWith('_count') && o.unit !== 'count') issues.push({ field: 'unit', message: 'count metric must use unit=count' }); | |
| 16 | + if (o.metric.endsWith('_rate') && o.unit !== 'per_100k') issues.push({ field: 'unit', message: 'rate metric must use unit=per_100k' }); | |
| 17 | + return issues; | |
| 18 | +} | |
| 19 | + | |
| 20 | +export function validateSurvival(o: { probability?: number | null; durationMonths: number; lowerCi?: number | null; upperCi?: number | null; cohortSize?: number | null }): ValidationIssue[] { | |
| 21 | + const issues: ValidationIssue[] = []; | |
| 22 | + if (o.probability != null && (o.probability < 0 || o.probability > 1)) issues.push({ field: 'probability', message: 'survival must be within [0,1]' }); | |
| 23 | + if (o.durationMonths <= 0) issues.push({ field: 'durationMonths', message: 'duration must be positive' }); | |
| 24 | + if (o.probability != null && o.lowerCi != null && o.lowerCi > o.probability) issues.push({ field: 'lowerCi', message: 'lowerCI > estimate' }); | |
| 25 | + if (o.probability != null && o.upperCi != null && o.upperCi < o.probability) issues.push({ field: 'upperCi', message: 'upperCI < estimate' }); | |
| 26 | + if (o.cohortSize != null && o.cohortSize < 0) issues.push({ field: 'cohortSize', message: 'negative cohort' }); | |
| 27 | + return issues; | |
| 28 | +} | |
| 29 | + | |
| 30 | +export const TRIAL_PHASES = new Set(['EARLY_PHASE1', 'PHASE1', 'PHASE2', 'PHASE3', 'PHASE4', 'NA']); | |
| 31 | +export const TRIAL_STATUSES = new Set(['ACTIVE_NOT_RECRUITING', 'COMPLETED', 'ENROLLING_BY_INVITATION', 'NOT_YET_RECRUITING', 'RECRUITING', 'SUSPENDED', 'TERMINATED', 'WITHDRAWN', 'AVAILABLE', 'NO_LONGER_AVAILABLE', 'TEMPORARILY_NOT_AVAILABLE', 'APPROVED_FOR_MARKETING', 'WITHHELD', 'UNKNOWN']); | |
| 32 | + | |
| 33 | +export function validateTrial(o: { nctId: string; phases: string[]; overallStatus?: string | null }): ValidationIssue[] { | |
| 34 | + const issues: ValidationIssue[] = []; | |
| 35 | + if (!/^NCT\d{8}$/.test(o.nctId)) issues.push({ field: 'nctId', message: 'malformed NCT id' }); | |
| 36 | + for (const p of o.phases) if (!TRIAL_PHASES.has(p)) issues.push({ field: 'phases', message: `unknown phase enum ${p}` }); | |
| 37 | + if (o.overallStatus && !TRIAL_STATUSES.has(o.overallStatus)) issues.push({ field: 'overallStatus', message: `unknown status enum ${o.overallStatus}` }); | |
| 38 | + return issues; | |
| 39 | +} | |
| 40 | + | |
| 41 | +export function validateFrequency(o: { casesAffected: number; casesProfiled: number }): ValidationIssue[] { | |
| 42 | + const issues: ValidationIssue[] = []; | |
| 43 | + if (o.casesProfiled <= 0) issues.push({ field: 'casesProfiled', message: 'denominator must be positive' }); | |
| 44 | + if (o.casesAffected < 0 || o.casesAffected > o.casesProfiled) issues.push({ field: 'casesAffected', message: 'numerator out of range' }); | |
| 45 | + return issues; | |
| 46 | +} | |
| 47 | + | |
| 48 | +/** Gene symbol sanity (HGNC style). */ | |
| 49 | +export const GENE_SYMBOL_RE = /^[A-Z][A-Z0-9-]{0,15}(?:@|orf\d+)?$/i; | |
added
packages/connectors/src/sources-sync.ts
+51 −0
@@ -0,0 +1,51 @@ | ||
| 1 | +import { eq } from 'drizzle-orm'; | |
| 2 | +import { type Database, sources, mintId } from '@cancerindex/database'; | |
| 3 | +import { CONNECTORS } from './registry.js'; | |
| 4 | +import type { ConnectorManifest } from './sdk/manifest.js'; | |
| 5 | + | |
| 6 | +/** Sync connector manifests into the `sources` registry (CLAUDE.md §95, §142, §307). Idempotent. */ | |
| 7 | +export async function syncSources(db: Database, extra: ConnectorManifest[] = []): Promise<{ created: number; updated: number }> { | |
| 8 | + let created = 0; | |
| 9 | + let updated = 0; | |
| 10 | + const manifests = [...CONNECTORS.map((c) => c.manifest), ...extra]; | |
| 11 | + for (const m of manifests) { | |
| 12 | + const values = { | |
| 13 | + slug: m.id, | |
| 14 | + name: m.name, | |
| 15 | + organization: m.organization, | |
| 16 | + category: m.category, | |
| 17 | + description: m.description, | |
| 18 | + homepage: m.homepage, | |
| 19 | + docsUrl: m.docsUrl ?? null, | |
| 20 | + termsUrl: m.termsUrl ?? null, | |
| 21 | + accessType: m.access.type, | |
| 22 | + accessAuth: m.access.auth, | |
| 23 | + license: m.license, | |
| 24 | + licenseStatus: m.licenseStatus, | |
| 25 | + commercialUse: m.commercialUse, | |
| 26 | + redistribution: m.redistribution, | |
| 27 | + attribution: m.attribution ?? null, | |
| 28 | + licenseReviewedAt: m.termsReviewedAt ? new Date(m.termsReviewedAt) : null, | |
| 29 | + approvedForProduction: m.licenseStatus === 'approved', | |
| 30 | + updateFrequency: m.updateFrequency, | |
| 31 | + supportsIncremental: m.supportsIncrementalSync, | |
| 32 | + entities: m.entities, | |
| 33 | + metrics: m.metrics, | |
| 34 | + rateLimit: m.rateLimits.requestsPerSecond ? `${m.rateLimits.requestsPerSecond} req/s` : m.rateLimits.requestsPerMinute ? `${m.rateLimits.requestsPerMinute} req/min` : null, | |
| 35 | + tier: m.tier, | |
| 36 | + manifest: m as unknown as Record<string, unknown>, | |
| 37 | + updatedAt: new Date(), | |
| 38 | + }; | |
| 39 | + const [existing] = await db.select({ id: sources.id, status: sources.status }).from(sources).where(eq(sources.slug, m.id)).limit(1); | |
| 40 | + if (existing) { | |
| 41 | + // Keep runtime status (active/degraded) unless manifest declares a gating status. | |
| 42 | + const gating = ['awaiting_credentials', 'review', 'retired', 'paused'].includes(m.status); | |
| 43 | + await db.update(sources).set({ ...values, ...(gating ? { status: m.status } : {}) }).where(eq(sources.id, existing.id)); | |
| 44 | + updated++; | |
| 45 | + } else { | |
| 46 | + await db.insert(sources).values({ id: await mintId(db, 'SOURCE'), ...values, status: m.status }); | |
| 47 | + created++; | |
| 48 | + } | |
| 49 | + } | |
| 50 | + return { created, updated }; | |
| 51 | +} | |
added
packages/connectors/tsconfig.json
+8 −0
@@ -0,0 +1,8 @@ | ||
| 1 | +{ | |
| 2 | + "extends": "../../tsconfig.base.json", | |
| 3 | + "compilerOptions": { | |
| 4 | + "rootDir": ".", | |
| 5 | + "noEmit": true | |
| 6 | + }, | |
| 7 | + "include": ["src", "test"] | |
| 8 | +} | |
added
packages/connectors/vitest.config.ts
+8 −0
@@ -0,0 +1,8 @@ | ||
| 1 | +import { defineConfig } from 'vitest/config'; | |
| 2 | + | |
| 3 | +export default defineConfig({ | |
| 4 | + test: { | |
| 5 | + include: ['src/**/*.test.ts', 'test/**/*.test.ts'], | |
| 6 | + testTimeout: 20_000, | |
| 7 | + }, | |
| 8 | +}); | |
added
packages/database/drizzle.config.ts
+11 −0
@@ -0,0 +1,11 @@ | ||
| 1 | +import { defineConfig } from 'drizzle-kit'; | |
| 2 | + | |
| 3 | +export default defineConfig({ | |
| 4 | + dialect: 'postgresql', | |
| 5 | + schema: './src/schema/index.ts', | |
| 6 | + out: './migrations', | |
| 7 | + dbCredentials: { url: process.env.DATABASE_URL ?? 'postgres://localhost:5432/cancerindex' }, | |
| 8 | + casing: 'snake_case', | |
| 9 | + strict: true, | |
| 10 | + verbose: true, | |
| 11 | +}); | |
added
packages/database/migrations/0000_small_blazing_skull.sql
+936 −0
@@ -0,0 +1,936 @@ | ||
| 1 | +CREATE TABLE "audit_log" ( | |
| 2 | + "id" bigserial PRIMARY KEY NOT NULL, | |
| 3 | + "actor" text NOT NULL, | |
| 4 | + "action" text NOT NULL, | |
| 5 | + "entity_type" text, | |
| 6 | + "entity_id" text, | |
| 7 | + "before" jsonb, | |
| 8 | + "after" jsonb, | |
| 9 | + "reason" text, | |
| 10 | + "created_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 11 | +); | |
| 12 | +--> statement-breakpoint | |
| 13 | +CREATE TABLE "change_events" ( | |
| 14 | + "id" bigserial PRIMARY KEY NOT NULL, | |
| 15 | + "entity_type" text NOT NULL, | |
| 16 | + "entity_id" text NOT NULL, | |
| 17 | + "kind" text NOT NULL, | |
| 18 | + "summary" text NOT NULL, | |
| 19 | + "before" jsonb, | |
| 20 | + "after" jsonb, | |
| 21 | + "ingest_run_id" text, | |
| 22 | + "created_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 23 | +); | |
| 24 | +--> statement-breakpoint | |
| 25 | +CREATE TABLE "connector_cursors" ( | |
| 26 | + "connector_id" text PRIMARY KEY NOT NULL, | |
| 27 | + "cursor" jsonb DEFAULT '{}'::jsonb NOT NULL, | |
| 28 | + "last_success_at" timestamp with time zone, | |
| 29 | + "last_attempt_at" timestamp with time zone, | |
| 30 | + "paused" boolean DEFAULT false NOT NULL, | |
| 31 | + "health" text DEFAULT 'unknown' NOT NULL, | |
| 32 | + "health_detail" text, | |
| 33 | + "updated_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 34 | +); | |
| 35 | +--> statement-breakpoint | |
| 36 | +CREATE TABLE "connector_field_stats" ( | |
| 37 | + "id" bigserial PRIMARY KEY NOT NULL, | |
| 38 | + "connector_id" text NOT NULL, | |
| 39 | + "entity" text NOT NULL, | |
| 40 | + "field" text NOT NULL, | |
| 41 | + "types" text[] DEFAULT '{}' NOT NULL, | |
| 42 | + "seen_count" integer DEFAULT 0 NOT NULL, | |
| 43 | + "null_count" integer DEFAULT 0 NOT NULL, | |
| 44 | + "first_seen_run" text, | |
| 45 | + "last_seen_run" text, | |
| 46 | + "updated_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 47 | +); | |
| 48 | +--> statement-breakpoint | |
| 49 | +CREATE TABLE "entity_merges" ( | |
| 50 | + "id" bigserial PRIMARY KEY NOT NULL, | |
| 51 | + "entity_type" text NOT NULL, | |
| 52 | + "keep_id" text NOT NULL, | |
| 53 | + "merge_id" text NOT NULL, | |
| 54 | + "evidence" jsonb DEFAULT '{}'::jsonb NOT NULL, | |
| 55 | + "status" text DEFAULT 'proposed' NOT NULL, | |
| 56 | + "decided_by" text, | |
| 57 | + "decided_at" timestamp with time zone, | |
| 58 | + "created_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 59 | +); | |
| 60 | +--> statement-breakpoint | |
| 61 | +CREATE TABLE "id_sequences" ( | |
| 62 | + "namespace" varchar(16) PRIMARY KEY NOT NULL, | |
| 63 | + "next" bigint DEFAULT 1 NOT NULL | |
| 64 | +); | |
| 65 | +--> statement-breakpoint | |
| 66 | +CREATE TABLE "ingest_runs" ( | |
| 67 | + "id" text PRIMARY KEY NOT NULL, | |
| 68 | + "connector_id" text NOT NULL, | |
| 69 | + "source_id" varchar(32) NOT NULL, | |
| 70 | + "mode" text DEFAULT 'incremental' NOT NULL, | |
| 71 | + "status" text DEFAULT 'running' NOT NULL, | |
| 72 | + "started_at" timestamp with time zone DEFAULT now() NOT NULL, | |
| 73 | + "finished_at" timestamp with time zone, | |
| 74 | + "duration_ms" integer, | |
| 75 | + "records_fetched" integer DEFAULT 0 NOT NULL, | |
| 76 | + "records_created" integer DEFAULT 0 NOT NULL, | |
| 77 | + "records_updated" integer DEFAULT 0 NOT NULL, | |
| 78 | + "records_unchanged" integer DEFAULT 0 NOT NULL, | |
| 79 | + "records_rejected" integer DEFAULT 0 NOT NULL, | |
| 80 | + "http_requests" integer DEFAULT 0 NOT NULL, | |
| 81 | + "http_failures" integer DEFAULT 0 NOT NULL, | |
| 82 | + "rate_limit_events" integer DEFAULT 0 NOT NULL, | |
| 83 | + "validation_failures" integer DEFAULT 0 NOT NULL, | |
| 84 | + "schema_drift" jsonb DEFAULT '[]'::jsonb NOT NULL, | |
| 85 | + "cursor_before" jsonb, | |
| 86 | + "cursor_after" jsonb, | |
| 87 | + "error" text, | |
| 88 | + "log" jsonb DEFAULT '[]'::jsonb NOT NULL, | |
| 89 | + "dataset_version" text, | |
| 90 | + "anomaly" text | |
| 91 | +); | |
| 92 | +--> statement-breakpoint | |
| 93 | +CREATE TABLE "provenance" ( | |
| 94 | + "id" bigserial PRIMARY KEY NOT NULL, | |
| 95 | + "public_id" varchar(32), | |
| 96 | + "source_id" varchar(32) NOT NULL, | |
| 97 | + "source_record_id" text, | |
| 98 | + "source_url" text, | |
| 99 | + "dataset" text, | |
| 100 | + "dataset_version" text, | |
| 101 | + "publication_id" varchar(32), | |
| 102 | + "pmid" text, | |
| 103 | + "doi" text, | |
| 104 | + "retrieved_at" timestamp with time zone NOT NULL, | |
| 105 | + "published_at" text, | |
| 106 | + "updated_at_source" text, | |
| 107 | + "geography" text, | |
| 108 | + "population" text, | |
| 109 | + "cohort_size" integer, | |
| 110 | + "methodology" text, | |
| 111 | + "evidence_type" text NOT NULL, | |
| 112 | + "access_level" text DEFAULT 'open' NOT NULL, | |
| 113 | + "confidence" real, | |
| 114 | + "license" text, | |
| 115 | + "ingest_run_id" text, | |
| 116 | + "created_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 117 | +); | |
| 118 | +--> statement-breakpoint | |
| 119 | +CREATE TABLE "source_records" ( | |
| 120 | + "id" bigserial PRIMARY KEY NOT NULL, | |
| 121 | + "source_id" varchar(32) NOT NULL, | |
| 122 | + "entity_kind" text NOT NULL, | |
| 123 | + "source_record_id" text NOT NULL, | |
| 124 | + "payload_hash" text NOT NULL, | |
| 125 | + "raw_path" text, | |
| 126 | + "status" text DEFAULT 'active' NOT NULL, | |
| 127 | + "first_seen_run" text, | |
| 128 | + "last_seen_run" text, | |
| 129 | + "retrieved_at" timestamp with time zone DEFAULT now() NOT NULL, | |
| 130 | + "source_updated_at" timestamp with time zone, | |
| 131 | + "canonical_type" text, | |
| 132 | + "canonical_id" text, | |
| 133 | + "created_at" timestamp with time zone DEFAULT now() NOT NULL, | |
| 134 | + "updated_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 135 | +); | |
| 136 | +--> statement-breakpoint | |
| 137 | +CREATE TABLE "sources" ( | |
| 138 | + "id" varchar(32) PRIMARY KEY NOT NULL, | |
| 139 | + "slug" text NOT NULL, | |
| 140 | + "name" text NOT NULL, | |
| 141 | + "organization" text, | |
| 142 | + "category" text NOT NULL, | |
| 143 | + "description" text, | |
| 144 | + "homepage" text, | |
| 145 | + "docs_url" text, | |
| 146 | + "terms_url" text, | |
| 147 | + "access_type" text NOT NULL, | |
| 148 | + "access_auth" text NOT NULL, | |
| 149 | + "license" text, | |
| 150 | + "license_status" text DEFAULT 'review' NOT NULL, | |
| 151 | + "commercial_use" text DEFAULT 'unknown' NOT NULL, | |
| 152 | + "redistribution" text DEFAULT 'unknown' NOT NULL, | |
| 153 | + "attribution" text, | |
| 154 | + "license_reviewed_at" timestamp with time zone, | |
| 155 | + "approved_for_production" boolean DEFAULT false NOT NULL, | |
| 156 | + "update_frequency" text, | |
| 157 | + "supports_incremental" boolean DEFAULT false NOT NULL, | |
| 158 | + "entities" text[] DEFAULT '{}' NOT NULL, | |
| 159 | + "metrics" text[] DEFAULT '{}' NOT NULL, | |
| 160 | + "rate_limit" text, | |
| 161 | + "status" text DEFAULT 'planned' NOT NULL, | |
| 162 | + "tier" integer DEFAULT 0 NOT NULL, | |
| 163 | + "manifest" jsonb DEFAULT '{}'::jsonb NOT NULL, | |
| 164 | + "created_at" timestamp with time zone DEFAULT now() NOT NULL, | |
| 165 | + "updated_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 166 | +); | |
| 167 | +--> statement-breakpoint | |
| 168 | +CREATE TABLE "unresolved_labels" ( | |
| 169 | + "id" bigserial PRIMARY KEY NOT NULL, | |
| 170 | + "source_id" varchar(32) NOT NULL, | |
| 171 | + "entity_kind" text NOT NULL, | |
| 172 | + "source_text" text NOT NULL, | |
| 173 | + "normalized" text NOT NULL, | |
| 174 | + "context" jsonb DEFAULT '{}'::jsonb NOT NULL, | |
| 175 | + "count" integer DEFAULT 1 NOT NULL, | |
| 176 | + "status" text DEFAULT 'open' NOT NULL, | |
| 177 | + "suggested_id" text, | |
| 178 | + "suggested_match_type" text, | |
| 179 | + "suggested_score" real, | |
| 180 | + "resolved_id" text, | |
| 181 | + "resolved_by" text, | |
| 182 | + "created_at" timestamp with time zone DEFAULT now() NOT NULL, | |
| 183 | + "updated_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 184 | +); | |
| 185 | +--> statement-breakpoint | |
| 186 | +CREATE TABLE "anatomical_sites" ( | |
| 187 | + "id" varchar(32) PRIMARY KEY NOT NULL, | |
| 188 | + "name" text NOT NULL, | |
| 189 | + "slug" text NOT NULL, | |
| 190 | + "ncit_code" text, | |
| 191 | + "uberon_id" text, | |
| 192 | + "parent_id" varchar(32), | |
| 193 | + "system" text | |
| 194 | +); | |
| 195 | +--> statement-breakpoint | |
| 196 | +CREATE TABLE "cancer_aliases" ( | |
| 197 | + "id" bigserial PRIMARY KEY NOT NULL, | |
| 198 | + "cancer_id" varchar(32) NOT NULL, | |
| 199 | + "alias" text NOT NULL, | |
| 200 | + "normalized" text NOT NULL, | |
| 201 | + "alias_type" text DEFAULT 'synonym' NOT NULL, | |
| 202 | + "source_id" varchar(32), | |
| 203 | + "source_terminology" text, | |
| 204 | + "language" text DEFAULT 'en' NOT NULL | |
| 205 | +); | |
| 206 | +--> statement-breakpoint | |
| 207 | +CREATE TABLE "cancer_anatomy" ( | |
| 208 | + "id" bigserial PRIMARY KEY NOT NULL, | |
| 209 | + "cancer_id" varchar(32) NOT NULL, | |
| 210 | + "site_id" varchar(32) NOT NULL, | |
| 211 | + "relation" text DEFAULT 'primary' NOT NULL, | |
| 212 | + "source_id" varchar(32) | |
| 213 | +); | |
| 214 | +--> statement-breakpoint | |
| 215 | +CREATE TABLE "cancer_codes" ( | |
| 216 | + "id" bigserial PRIMARY KEY NOT NULL, | |
| 217 | + "cancer_id" varchar(32) NOT NULL, | |
| 218 | + "system" text NOT NULL, | |
| 219 | + "code" text NOT NULL, | |
| 220 | + "match_type" text DEFAULT 'EXACT_IDENTIFIER' NOT NULL, | |
| 221 | + "source_id" varchar(32), | |
| 222 | + "valid_from" text, | |
| 223 | + "valid_to" text | |
| 224 | +); | |
| 225 | +--> statement-breakpoint | |
| 226 | +CREATE TABLE "cancer_hierarchy" ( | |
| 227 | + "id" bigserial PRIMARY KEY NOT NULL, | |
| 228 | + "parent_id" varchar(32) NOT NULL, | |
| 229 | + "child_id" varchar(32) NOT NULL, | |
| 230 | + "hierarchy_type" text NOT NULL, | |
| 231 | + "source_id" varchar(32) | |
| 232 | +); | |
| 233 | +--> statement-breakpoint | |
| 234 | +CREATE TABLE "cancers" ( | |
| 235 | + "id" varchar(32) PRIMARY KEY NOT NULL, | |
| 236 | + "slug" text NOT NULL, | |
| 237 | + "canonical_name" text NOT NULL, | |
| 238 | + "short_name" text, | |
| 239 | + "entity_type" text DEFAULT 'cancer' NOT NULL, | |
| 240 | + "malignant" boolean DEFAULT true NOT NULL, | |
| 241 | + "solid_tumor" boolean DEFAULT true NOT NULL, | |
| 242 | + "hematologic" boolean DEFAULT false NOT NULL, | |
| 243 | + "pediatric_relevant" boolean DEFAULT false NOT NULL, | |
| 244 | + "rare_cancer" boolean, | |
| 245 | + "top_level" boolean DEFAULT false NOT NULL, | |
| 246 | + "description" text, | |
| 247 | + "description_provenance_id" integer, | |
| 248 | + "primary_ncit_code" text, | |
| 249 | + "primary_oncotree_code" text, | |
| 250 | + "depth" integer DEFAULT 0 NOT NULL, | |
| 251 | + "status" text DEFAULT 'active' NOT NULL, | |
| 252 | + "merged_into" varchar(32), | |
| 253 | + "deprecated_reason" text, | |
| 254 | + "classification_version" text, | |
| 255 | + "semantic_types" text[] DEFAULT '{}' NOT NULL, | |
| 256 | + "created_at" timestamp with time zone DEFAULT now() NOT NULL, | |
| 257 | + "updated_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 258 | +); | |
| 259 | +--> statement-breakpoint | |
| 260 | +CREATE TABLE "cohort_definitions" ( | |
| 261 | + "id" bigserial PRIMARY KEY NOT NULL, | |
| 262 | + "name" text NOT NULL, | |
| 263 | + "cancer_id" varchar(32) NOT NULL, | |
| 264 | + "biomarker_ids" text[] DEFAULT '{}' NOT NULL, | |
| 265 | + "variant_ids" text[] DEFAULT '{}' NOT NULL, | |
| 266 | + "stage" text, | |
| 267 | + "attributes" jsonb DEFAULT '{}'::jsonb NOT NULL, | |
| 268 | + "confidence" real, | |
| 269 | + "created_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 270 | +); | |
| 271 | +--> statement-breakpoint | |
| 272 | +CREATE TABLE "geographies" ( | |
| 273 | + "id" varchar(32) PRIMARY KEY NOT NULL, | |
| 274 | + "slug" text NOT NULL, | |
| 275 | + "name" text NOT NULL, | |
| 276 | + "kind" text NOT NULL, | |
| 277 | + "iso2" text, | |
| 278 | + "iso3" text, | |
| 279 | + "parent_id" varchar(32), | |
| 280 | + "who_region" text, | |
| 281 | + "population" integer, | |
| 282 | + "population_year" integer | |
| 283 | +); | |
| 284 | +--> statement-breakpoint | |
| 285 | +CREATE TABLE "biomarkers" ( | |
| 286 | + "id" varchar(32) PRIMARY KEY NOT NULL, | |
| 287 | + "slug" text NOT NULL, | |
| 288 | + "name" text NOT NULL, | |
| 289 | + "kind" text NOT NULL, | |
| 290 | + "gene_id" varchar(32), | |
| 291 | + "ncit_code" text, | |
| 292 | + "description" text, | |
| 293 | + "measurement" jsonb DEFAULT '{}'::jsonb NOT NULL, | |
| 294 | + "created_at" timestamp with time zone DEFAULT now() NOT NULL, | |
| 295 | + "updated_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 296 | +); | |
| 297 | +--> statement-breakpoint | |
| 298 | +CREATE TABLE "cancer_gene_frequencies" ( | |
| 299 | + "id" bigserial PRIMARY KEY NOT NULL, | |
| 300 | + "cohort_id" varchar(32) NOT NULL, | |
| 301 | + "cancer_id" varchar(32), | |
| 302 | + "gene_id" varchar(32), | |
| 303 | + "gene_symbol" text NOT NULL, | |
| 304 | + "alteration_type" text DEFAULT 'ssm' NOT NULL, | |
| 305 | + "cases_affected" integer NOT NULL, | |
| 306 | + "cases_profiled" integer NOT NULL, | |
| 307 | + "frequency" real NOT NULL, | |
| 308 | + "rank" integer, | |
| 309 | + "data_release" text, | |
| 310 | + "provenance_id" integer NOT NULL, | |
| 311 | + "updated_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 312 | +); | |
| 313 | +--> statement-breakpoint | |
| 314 | +CREATE TABLE "entity_embeddings" ( | |
| 315 | + "id" bigserial PRIMARY KEY NOT NULL, | |
| 316 | + "entity_type" text NOT NULL, | |
| 317 | + "entity_id" text NOT NULL, | |
| 318 | + "model" text NOT NULL, | |
| 319 | + "dimensions" integer NOT NULL, | |
| 320 | + "text_hash" text NOT NULL, | |
| 321 | + "embedding" text, | |
| 322 | + "created_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 323 | +); | |
| 324 | +--> statement-breakpoint | |
| 325 | +CREATE TABLE "gene_aliases" ( | |
| 326 | + "id" bigserial PRIMARY KEY NOT NULL, | |
| 327 | + "gene_id" varchar(32) NOT NULL, | |
| 328 | + "alias" text NOT NULL, | |
| 329 | + "alias_type" text NOT NULL, | |
| 330 | + "source_id" varchar(32) | |
| 331 | +); | |
| 332 | +--> statement-breakpoint | |
| 333 | +CREATE TABLE "genes" ( | |
| 334 | + "id" varchar(32) PRIMARY KEY NOT NULL, | |
| 335 | + "hgnc_id" text, | |
| 336 | + "symbol" text NOT NULL, | |
| 337 | + "name" text, | |
| 338 | + "locus_type" text, | |
| 339 | + "locus_group" text, | |
| 340 | + "location" text, | |
| 341 | + "chromosome" text, | |
| 342 | + "ensembl_gene_id" text, | |
| 343 | + "ncbi_gene_id" text, | |
| 344 | + "omim_ids" text[] DEFAULT '{}' NOT NULL, | |
| 345 | + "uniprot_ids" text[] DEFAULT '{}' NOT NULL, | |
| 346 | + "refseq_accession" text, | |
| 347 | + "prev_symbols" text[] DEFAULT '{}' NOT NULL, | |
| 348 | + "alias_symbols" text[] DEFAULT '{}' NOT NULL, | |
| 349 | + "gene_families" text[] DEFAULT '{}' NOT NULL, | |
| 350 | + "status" text DEFAULT 'Approved' NOT NULL, | |
| 351 | + "is_cancer_gene" boolean DEFAULT false NOT NULL, | |
| 352 | + "civic_gene_id" integer, | |
| 353 | + "description" text, | |
| 354 | + "created_at" timestamp with time zone DEFAULT now() NOT NULL, | |
| 355 | + "updated_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 356 | +); | |
| 357 | +--> statement-breakpoint | |
| 358 | +CREATE TABLE "genomic_cohorts" ( | |
| 359 | + "id" varchar(32) PRIMARY KEY NOT NULL, | |
| 360 | + "source_id" varchar(32) NOT NULL, | |
| 361 | + "study_id" text NOT NULL, | |
| 362 | + "name" text NOT NULL, | |
| 363 | + "program" text, | |
| 364 | + "primary_sites" text[] DEFAULT '{}' NOT NULL, | |
| 365 | + "disease_types" text[] DEFAULT '{}' NOT NULL, | |
| 366 | + "cancer_id" varchar(32), | |
| 367 | + "cancer_match_type" text, | |
| 368 | + "case_count" integer, | |
| 369 | + "cases_with_ssm" integer, | |
| 370 | + "data_release" text, | |
| 371 | + "access_level" text DEFAULT 'open' NOT NULL, | |
| 372 | + "url" text, | |
| 373 | + "provenance_id" integer, | |
| 374 | + "updated_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 375 | +); | |
| 376 | +--> statement-breakpoint | |
| 377 | +CREATE TABLE "variant_aliases" ( | |
| 378 | + "id" bigserial PRIMARY KEY NOT NULL, | |
| 379 | + "variant_id" varchar(32) NOT NULL, | |
| 380 | + "alias" text NOT NULL, | |
| 381 | + "source_id" varchar(32) | |
| 382 | +); | |
| 383 | +--> statement-breakpoint | |
| 384 | +CREATE TABLE "variant_clinical_significance" ( | |
| 385 | + "id" bigserial PRIMARY KEY NOT NULL, | |
| 386 | + "variant_id" varchar(32) NOT NULL, | |
| 387 | + "clinvar_variation_id" text NOT NULL, | |
| 388 | + "clinical_significance" text NOT NULL, | |
| 389 | + "review_status" text, | |
| 390 | + "star_rating" integer, | |
| 391 | + "last_evaluated" text, | |
| 392 | + "conditions" text[] DEFAULT '{}' NOT NULL, | |
| 393 | + "condition_cancer_ids" text[] DEFAULT '{}' NOT NULL, | |
| 394 | + "origin_simple" text, | |
| 395 | + "number_submitters" integer, | |
| 396 | + "provenance_id" integer NOT NULL, | |
| 397 | + "ingest_run_id" text, | |
| 398 | + "updated_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 399 | +); | |
| 400 | +--> statement-breakpoint | |
| 401 | +CREATE TABLE "variants" ( | |
| 402 | + "id" varchar(32) PRIMARY KEY NOT NULL, | |
| 403 | + "slug" text NOT NULL, | |
| 404 | + "gene_id" varchar(32), | |
| 405 | + "gene_symbol" text, | |
| 406 | + "name" text NOT NULL, | |
| 407 | + "variant_type" text, | |
| 408 | + "hgvs_g" text, | |
| 409 | + "hgvs_c" text, | |
| 410 | + "hgvs_p" text, | |
| 411 | + "assembly" text, | |
| 412 | + "chromosome" text, | |
| 413 | + "start" integer, | |
| 414 | + "end" integer, | |
| 415 | + "reference_bases" text, | |
| 416 | + "alternate_bases" text, | |
| 417 | + "coordinates" jsonb DEFAULT '[]'::jsonb NOT NULL, | |
| 418 | + "clinvar_variation_id" text, | |
| 419 | + "civic_variant_id" integer, | |
| 420 | + "dbsnp_ids" text[] DEFAULT '{}' NOT NULL, | |
| 421 | + "fusion_partners" text[] DEFAULT '{}' NOT NULL, | |
| 422 | + "created_at" timestamp with time zone DEFAULT now() NOT NULL, | |
| 423 | + "updated_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 424 | +); | |
| 425 | +--> statement-breakpoint | |
| 426 | +CREATE TABLE "drug_aliases" ( | |
| 427 | + "id" bigserial PRIMARY KEY NOT NULL, | |
| 428 | + "drug_id" varchar(32) NOT NULL, | |
| 429 | + "alias" text NOT NULL, | |
| 430 | + "normalized" text NOT NULL, | |
| 431 | + "alias_type" text DEFAULT 'synonym' NOT NULL, | |
| 432 | + "source_id" varchar(32) | |
| 433 | +); | |
| 434 | +--> statement-breakpoint | |
| 435 | +CREATE TABLE "drug_approvals" ( | |
| 436 | + "id" bigserial PRIMARY KEY NOT NULL, | |
| 437 | + "drug_id" varchar(32) NOT NULL, | |
| 438 | + "cancer_id" varchar(32), | |
| 439 | + "biomarker_ids" text[] DEFAULT '{}' NOT NULL, | |
| 440 | + "tumor_agnostic" boolean DEFAULT false NOT NULL, | |
| 441 | + "jurisdiction" text NOT NULL, | |
| 442 | + "authority" text NOT NULL, | |
| 443 | + "indication" text NOT NULL, | |
| 444 | + "line_of_therapy" text, | |
| 445 | + "disease_stage" text, | |
| 446 | + "approval_type" text, | |
| 447 | + "accelerated" boolean, | |
| 448 | + "conditional" boolean, | |
| 449 | + "approval_date" text, | |
| 450 | + "withdrawal_date" text, | |
| 451 | + "status" text NOT NULL, | |
| 452 | + "application_number" text, | |
| 453 | + "source_id" varchar(32) NOT NULL, | |
| 454 | + "provenance_id" integer NOT NULL, | |
| 455 | + "raw" jsonb, | |
| 456 | + "created_at" timestamp with time zone DEFAULT now() NOT NULL, | |
| 457 | + "updated_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 458 | +); | |
| 459 | +--> statement-breakpoint | |
| 460 | +CREATE TABLE "drugs" ( | |
| 461 | + "id" varchar(32) PRIMARY KEY NOT NULL, | |
| 462 | + "slug" text NOT NULL, | |
| 463 | + "name" text NOT NULL, | |
| 464 | + "kind" text, | |
| 465 | + "ncit_code" text, | |
| 466 | + "chembl_id" text, | |
| 467 | + "civic_therapy_id" integer, | |
| 468 | + "drugbank_id" text, | |
| 469 | + "pubchem_cid" text, | |
| 470 | + "unii" text, | |
| 471 | + "mechanism" text, | |
| 472 | + "target_gene_ids" text[] DEFAULT '{}' NOT NULL, | |
| 473 | + "development_status" text, | |
| 474 | + "description" text, | |
| 475 | + "created_at" timestamp with time zone DEFAULT now() NOT NULL, | |
| 476 | + "updated_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 477 | +); | |
| 478 | +--> statement-breakpoint | |
| 479 | +CREATE TABLE "treatment_regimens" ( | |
| 480 | + "id" varchar(32) PRIMARY KEY NOT NULL, | |
| 481 | + "slug" text NOT NULL, | |
| 482 | + "name" text NOT NULL, | |
| 483 | + "component_drug_ids" text[] DEFAULT '{}' NOT NULL, | |
| 484 | + "modality" text DEFAULT 'drug_combination' NOT NULL, | |
| 485 | + "description" text, | |
| 486 | + "created_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 487 | +); | |
| 488 | +--> statement-breakpoint | |
| 489 | +CREATE TABLE "clinical_trials" ( | |
| 490 | + "id" varchar(32) PRIMARY KEY NOT NULL, | |
| 491 | + "nct_id" text NOT NULL, | |
| 492 | + "brief_title" text NOT NULL, | |
| 493 | + "official_title" text, | |
| 494 | + "acronym" text, | |
| 495 | + "study_type" text, | |
| 496 | + "phases" text[] DEFAULT '{}' NOT NULL, | |
| 497 | + "overall_status" text, | |
| 498 | + "why_stopped" text, | |
| 499 | + "start_date" text, | |
| 500 | + "primary_completion_date" text, | |
| 501 | + "completion_date" text, | |
| 502 | + "first_posted_date" text, | |
| 503 | + "last_update_posted_date" text, | |
| 504 | + "results_first_posted_date" text, | |
| 505 | + "has_results" boolean DEFAULT false NOT NULL, | |
| 506 | + "enrollment_count" integer, | |
| 507 | + "enrollment_type" text, | |
| 508 | + "lead_sponsor" text, | |
| 509 | + "lead_sponsor_class" text, | |
| 510 | + "collaborators" text[] DEFAULT '{}' NOT NULL, | |
| 511 | + "conditions" text[] DEFAULT '{}' NOT NULL, | |
| 512 | + "keywords" text[] DEFAULT '{}' NOT NULL, | |
| 513 | + "interventions" jsonb DEFAULT '[]'::jsonb NOT NULL, | |
| 514 | + "arms" jsonb DEFAULT '[]'::jsonb NOT NULL, | |
| 515 | + "primary_outcomes" jsonb DEFAULT '[]'::jsonb NOT NULL, | |
| 516 | + "secondary_outcomes" jsonb DEFAULT '[]'::jsonb NOT NULL, | |
| 517 | + "eligibility" jsonb DEFAULT '{}'::jsonb NOT NULL, | |
| 518 | + "sex" text, | |
| 519 | + "minimum_age" text, | |
| 520 | + "maximum_age" text, | |
| 521 | + "countries" text[] DEFAULT '{}' NOT NULL, | |
| 522 | + "locations_count" integer DEFAULT 0 NOT NULL, | |
| 523 | + "references" jsonb DEFAULT '[]'::jsonb NOT NULL, | |
| 524 | + "brief_summary" text, | |
| 525 | + "is_oncology" boolean DEFAULT true NOT NULL, | |
| 526 | + "source_record_id" integer, | |
| 527 | + "ingest_run_id" text, | |
| 528 | + "created_at" timestamp with time zone DEFAULT now() NOT NULL, | |
| 529 | + "updated_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 530 | +); | |
| 531 | +--> statement-breakpoint | |
| 532 | +CREATE TABLE "trial_conditions" ( | |
| 533 | + "id" bigserial PRIMARY KEY NOT NULL, | |
| 534 | + "trial_id" varchar(32) NOT NULL, | |
| 535 | + "condition_text" text NOT NULL, | |
| 536 | + "normalized" text NOT NULL, | |
| 537 | + "cancer_id" varchar(32), | |
| 538 | + "match_type" text DEFAULT 'UNRESOLVED' NOT NULL, | |
| 539 | + "confidence" real | |
| 540 | +); | |
| 541 | +--> statement-breakpoint | |
| 542 | +CREATE TABLE "trial_interventions" ( | |
| 543 | + "id" bigserial PRIMARY KEY NOT NULL, | |
| 544 | + "trial_id" varchar(32) NOT NULL, | |
| 545 | + "name" text NOT NULL, | |
| 546 | + "normalized" text NOT NULL, | |
| 547 | + "intervention_type" text, | |
| 548 | + "drug_id" varchar(32), | |
| 549 | + "match_type" text DEFAULT 'UNRESOLVED' NOT NULL | |
| 550 | +); | |
| 551 | +--> statement-breakpoint | |
| 552 | +CREATE TABLE "trial_locations" ( | |
| 553 | + "id" bigserial PRIMARY KEY NOT NULL, | |
| 554 | + "trial_id" varchar(32) NOT NULL, | |
| 555 | + "facility" text, | |
| 556 | + "city" text, | |
| 557 | + "state" text, | |
| 558 | + "zip" text, | |
| 559 | + "country" text, | |
| 560 | + "status" text, | |
| 561 | + "lat" real, | |
| 562 | + "lng" real | |
| 563 | +); | |
| 564 | +--> statement-breakpoint | |
| 565 | +CREATE TABLE "trial_pulse" ( | |
| 566 | + "id" bigserial PRIMARY KEY NOT NULL, | |
| 567 | + "day" date NOT NULL, | |
| 568 | + "cancer_id" varchar(32), | |
| 569 | + "phase" text, | |
| 570 | + "new_trials" integer NOT NULL, | |
| 571 | + "updated_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 572 | +); | |
| 573 | +--> statement-breakpoint | |
| 574 | +CREATE TABLE "literature_counts" ( | |
| 575 | + "id" bigserial PRIMARY KEY NOT NULL, | |
| 576 | + "cancer_id" varchar(32) NOT NULL, | |
| 577 | + "window_key" text NOT NULL, | |
| 578 | + "window_start" text, | |
| 579 | + "window_end" text, | |
| 580 | + "query" text NOT NULL, | |
| 581 | + "count" integer NOT NULL, | |
| 582 | + "provenance_id" integer NOT NULL, | |
| 583 | + "updated_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 584 | +); | |
| 585 | +--> statement-breakpoint | |
| 586 | +CREATE TABLE "publication_entity_edges" ( | |
| 587 | + "id" bigserial PRIMARY KEY NOT NULL, | |
| 588 | + "publication_id" varchar(32) NOT NULL, | |
| 589 | + "entity_type" text NOT NULL, | |
| 590 | + "entity_id" text NOT NULL, | |
| 591 | + "method" text NOT NULL, | |
| 592 | + "confidence" real, | |
| 593 | + "status" text DEFAULT 'candidate' NOT NULL, | |
| 594 | + "source_id" varchar(32), | |
| 595 | + "ingest_run_id" text, | |
| 596 | + "created_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 597 | +); | |
| 598 | +--> statement-breakpoint | |
| 599 | +CREATE TABLE "publications" ( | |
| 600 | + "id" varchar(32) PRIMARY KEY NOT NULL, | |
| 601 | + "pmid" text, | |
| 602 | + "doi" text, | |
| 603 | + "pmcid" text, | |
| 604 | + "title" text NOT NULL, | |
| 605 | + "abstract" text, | |
| 606 | + "journal" text, | |
| 607 | + "journal_iso" text, | |
| 608 | + "pub_date" text, | |
| 609 | + "pub_year" integer, | |
| 610 | + "publication_types" text[] DEFAULT '{}' NOT NULL, | |
| 611 | + "mesh_terms" jsonb DEFAULT '[]'::jsonb NOT NULL, | |
| 612 | + "authors" jsonb DEFAULT '[]'::jsonb NOT NULL, | |
| 613 | + "language" text, | |
| 614 | + "is_preprint" boolean DEFAULT false NOT NULL, | |
| 615 | + "retracted" boolean DEFAULT false NOT NULL, | |
| 616 | + "retraction_notice" text, | |
| 617 | + "nct_ids" text[] DEFAULT '{}' NOT NULL, | |
| 618 | + "cited_by_count" integer, | |
| 619 | + "source_record_id" integer, | |
| 620 | + "ingest_run_id" text, | |
| 621 | + "created_at" timestamp with time zone DEFAULT now() NOT NULL, | |
| 622 | + "updated_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 623 | +); | |
| 624 | +--> statement-breakpoint | |
| 625 | +CREATE TABLE "civic_evidence_items" ( | |
| 626 | + "id" bigserial PRIMARY KEY NOT NULL, | |
| 627 | + "civic_id" integer NOT NULL, | |
| 628 | + "name" text, | |
| 629 | + "molecular_profile_id" integer, | |
| 630 | + "molecular_profile_name" text, | |
| 631 | + "gene_symbols" text[] DEFAULT '{}' NOT NULL, | |
| 632 | + "gene_ids" text[] DEFAULT '{}' NOT NULL, | |
| 633 | + "variant_ids" text[] DEFAULT '{}' NOT NULL, | |
| 634 | + "civic_variant_ids" integer[] DEFAULT '{}' NOT NULL, | |
| 635 | + "disease_name" text, | |
| 636 | + "doid" text, | |
| 637 | + "cancer_id" varchar(32), | |
| 638 | + "cancer_match_type" text, | |
| 639 | + "therapy_names" text[] DEFAULT '{}' NOT NULL, | |
| 640 | + "therapy_ids" text[] DEFAULT '{}' NOT NULL, | |
| 641 | + "therapy_interaction_type" text, | |
| 642 | + "evidence_type" text, | |
| 643 | + "evidence_level" text, | |
| 644 | + "evidence_direction" text, | |
| 645 | + "significance" text, | |
| 646 | + "evidence_rating" integer, | |
| 647 | + "status" text, | |
| 648 | + "description" text, | |
| 649 | + "pmid" text, | |
| 650 | + "source_citation" text, | |
| 651 | + "phenotypes" text[] DEFAULT '{}' NOT NULL, | |
| 652 | + "provenance_id" integer NOT NULL, | |
| 653 | + "ingest_run_id" text, | |
| 654 | + "updated_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 655 | +); | |
| 656 | +--> statement-breakpoint | |
| 657 | +CREATE TABLE "knowledge_edges" ( | |
| 658 | + "id" bigserial PRIMARY KEY NOT NULL, | |
| 659 | + "source_entity_type" text NOT NULL, | |
| 660 | + "source_entity_id" text NOT NULL, | |
| 661 | + "target_entity_type" text NOT NULL, | |
| 662 | + "target_entity_id" text NOT NULL, | |
| 663 | + "relationship_type" text NOT NULL, | |
| 664 | + "cancer_context_ids" text[] DEFAULT '{}' NOT NULL, | |
| 665 | + "predictive" boolean, | |
| 666 | + "prognostic" boolean, | |
| 667 | + "diagnostic" boolean, | |
| 668 | + "predisposing" boolean, | |
| 669 | + "direction" text, | |
| 670 | + "evidence_level" text, | |
| 671 | + "evidence_score" real, | |
| 672 | + "evidence_category" text DEFAULT 'curated_evidence' NOT NULL, | |
| 673 | + "status" text DEFAULT 'active' NOT NULL, | |
| 674 | + "source_id" varchar(32) NOT NULL, | |
| 675 | + "source_record_id" text, | |
| 676 | + "provenance_ids" integer[] DEFAULT '{}' NOT NULL, | |
| 677 | + "support_count" integer DEFAULT 1 NOT NULL, | |
| 678 | + "first_seen_at" timestamp with time zone DEFAULT now() NOT NULL, | |
| 679 | + "last_seen_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 680 | +); | |
| 681 | +--> statement-breakpoint | |
| 682 | +CREATE TABLE "risk_factors" ( | |
| 683 | + "id" bigserial PRIMARY KEY NOT NULL, | |
| 684 | + "slug" text NOT NULL, | |
| 685 | + "name" text NOT NULL, | |
| 686 | + "kind" text NOT NULL, | |
| 687 | + "classification_authority" text, | |
| 688 | + "classification" text, | |
| 689 | + "description" text, | |
| 690 | + "created_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 691 | +); | |
| 692 | +--> statement-breakpoint | |
| 693 | +CREATE TABLE "epidemiology_observations" ( | |
| 694 | + "id" bigserial PRIMARY KEY NOT NULL, | |
| 695 | + "cancer_id" varchar(32) NOT NULL, | |
| 696 | + "geography_id" varchar(32) NOT NULL, | |
| 697 | + "year" integer NOT NULL, | |
| 698 | + "year_end" integer, | |
| 699 | + "sex" text DEFAULT 'all' NOT NULL, | |
| 700 | + "age_group" text DEFAULT 'all' NOT NULL, | |
| 701 | + "metric" text NOT NULL, | |
| 702 | + "value" double precision NOT NULL, | |
| 703 | + "unit" text NOT NULL, | |
| 704 | + "lower_ci" double precision, | |
| 705 | + "upper_ci" double precision, | |
| 706 | + "standard_population" text, | |
| 707 | + "estimate_type" text DEFAULT 'observed' NOT NULL, | |
| 708 | + "site_definition" text, | |
| 709 | + "source_id" varchar(32) NOT NULL, | |
| 710 | + "provenance_id" integer NOT NULL, | |
| 711 | + "ingest_run_id" text, | |
| 712 | + "updated_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 713 | +); | |
| 714 | +--> statement-breakpoint | |
| 715 | +CREATE TABLE "survival_observations" ( | |
| 716 | + "id" bigserial PRIMARY KEY NOT NULL, | |
| 717 | + "cancer_id" varchar(32) NOT NULL, | |
| 718 | + "geography_id" varchar(32), | |
| 719 | + "stage" text, | |
| 720 | + "staging_system" text, | |
| 721 | + "sex" text DEFAULT 'all' NOT NULL, | |
| 722 | + "age_group" text DEFAULT 'all' NOT NULL, | |
| 723 | + "diagnosis_period" text, | |
| 724 | + "survival_type" text NOT NULL, | |
| 725 | + "duration_months" integer NOT NULL, | |
| 726 | + "probability" real, | |
| 727 | + "median_months" real, | |
| 728 | + "cohort_size" integer, | |
| 729 | + "lower_ci" real, | |
| 730 | + "upper_ci" real, | |
| 731 | + "method" text, | |
| 732 | + "source_id" varchar(32) NOT NULL, | |
| 733 | + "provenance_id" integer NOT NULL, | |
| 734 | + "ingest_run_id" text, | |
| 735 | + "updated_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 736 | +); | |
| 737 | +--> statement-breakpoint | |
| 738 | +CREATE TABLE "ai_answers" ( | |
| 739 | + "id" bigserial PRIMARY KEY NOT NULL, | |
| 740 | + "kind" text NOT NULL, | |
| 741 | + "subject_id" text, | |
| 742 | + "question_hash" text NOT NULL, | |
| 743 | + "question" text, | |
| 744 | + "answer" jsonb NOT NULL, | |
| 745 | + "model" text NOT NULL, | |
| 746 | + "prompt_version" text NOT NULL, | |
| 747 | + "source_snapshot" jsonb DEFAULT '{}'::jsonb NOT NULL, | |
| 748 | + "data_as_of" timestamp with time zone NOT NULL, | |
| 749 | + "created_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 750 | +); | |
| 751 | +--> statement-breakpoint | |
| 752 | +CREATE TABLE "api_keys" ( | |
| 753 | + "id" bigserial PRIMARY KEY NOT NULL, | |
| 754 | + "key_hash" text NOT NULL, | |
| 755 | + "prefix" text NOT NULL, | |
| 756 | + "label" text, | |
| 757 | + "owner_email" text, | |
| 758 | + "tier" text DEFAULT 'free' NOT NULL, | |
| 759 | + "rate_limit_per_minute" integer DEFAULT 60 NOT NULL, | |
| 760 | + "active" boolean DEFAULT true NOT NULL, | |
| 761 | + "last_used_at" timestamp with time zone, | |
| 762 | + "created_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 763 | +); | |
| 764 | +--> statement-breakpoint | |
| 765 | +CREATE TABLE "entity_counters" ( | |
| 766 | + "id" bigserial PRIMARY KEY NOT NULL, | |
| 767 | + "entity_type" text NOT NULL, | |
| 768 | + "entity_id" text NOT NULL, | |
| 769 | + "trial_count" integer DEFAULT 0 NOT NULL, | |
| 770 | + "active_trial_count" integer DEFAULT 0 NOT NULL, | |
| 771 | + "recruiting_trial_count" integer DEFAULT 0 NOT NULL, | |
| 772 | + "phase3_trial_count" integer DEFAULT 0 NOT NULL, | |
| 773 | + "publication_count" integer DEFAULT 0 NOT NULL, | |
| 774 | + "publication_count_5y" integer DEFAULT 0 NOT NULL, | |
| 775 | + "publication_count_12m" integer DEFAULT 0 NOT NULL, | |
| 776 | + "gene_count" integer DEFAULT 0 NOT NULL, | |
| 777 | + "variant_count" integer DEFAULT 0 NOT NULL, | |
| 778 | + "drug_count" integer DEFAULT 0 NOT NULL, | |
| 779 | + "approved_drug_count" integer DEFAULT 0 NOT NULL, | |
| 780 | + "evidence_count" integer DEFAULT 0 NOT NULL, | |
| 781 | + "cohort_count" integer DEFAULT 0 NOT NULL, | |
| 782 | + "subtype_count" integer DEFAULT 0 NOT NULL, | |
| 783 | + "descendant_count" integer DEFAULT 0 NOT NULL, | |
| 784 | + "epidemiology_obs_count" integer DEFAULT 0 NOT NULL, | |
| 785 | + "survival_obs_count" integer DEFAULT 0 NOT NULL, | |
| 786 | + "completeness" jsonb DEFAULT '{}'::jsonb NOT NULL, | |
| 787 | + "updated_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 788 | +); | |
| 789 | +--> statement-breakpoint | |
| 790 | +CREATE TABLE "metric_definitions" ( | |
| 791 | + "id" varchar(32) PRIMARY KEY NOT NULL, | |
| 792 | + "slug" text NOT NULL, | |
| 793 | + "name" text NOT NULL, | |
| 794 | + "description" text NOT NULL, | |
| 795 | + "formula" text NOT NULL, | |
| 796 | + "formula_version" text NOT NULL, | |
| 797 | + "unit" text NOT NULL, | |
| 798 | + "higher_is_worse" boolean, | |
| 799 | + "aggregation" text, | |
| 800 | + "valid_dimensions" text[] DEFAULT '{}' NOT NULL, | |
| 801 | + "source_slugs" text[] DEFAULT '{}' NOT NULL, | |
| 802 | + "category" text NOT NULL, | |
| 803 | + "eligibility" jsonb DEFAULT '{}'::jsonb NOT NULL, | |
| 804 | + "experimental" boolean DEFAULT false NOT NULL, | |
| 805 | + "created_at" timestamp with time zone DEFAULT now() NOT NULL, | |
| 806 | + "updated_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 807 | +); | |
| 808 | +--> statement-breakpoint | |
| 809 | +CREATE TABLE "ranking_snapshots" ( | |
| 810 | + "id" bigserial PRIMARY KEY NOT NULL, | |
| 811 | + "metric_id" varchar(32) NOT NULL, | |
| 812 | + "metric_slug" text NOT NULL, | |
| 813 | + "scope_key" text NOT NULL, | |
| 814 | + "geography" text DEFAULT 'WORLD' NOT NULL, | |
| 815 | + "sex" text DEFAULT 'all' NOT NULL, | |
| 816 | + "age_group" text DEFAULT 'all' NOT NULL, | |
| 817 | + "year" integer, | |
| 818 | + "entity_level" text DEFAULT 'top' NOT NULL, | |
| 819 | + "formula_version" text NOT NULL, | |
| 820 | + "eligible_entities" integer NOT NULL, | |
| 821 | + "inputs_hash" text NOT NULL, | |
| 822 | + "source_ids" text[] DEFAULT '{}' NOT NULL, | |
| 823 | + "is_current" boolean DEFAULT true NOT NULL, | |
| 824 | + "generated_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 825 | +); | |
| 826 | +--> statement-breakpoint | |
| 827 | +CREATE TABLE "rankings" ( | |
| 828 | + "id" bigserial PRIMARY KEY NOT NULL, | |
| 829 | + "snapshot_id" integer NOT NULL, | |
| 830 | + "metric_slug" text NOT NULL, | |
| 831 | + "scope_key" text NOT NULL, | |
| 832 | + "cancer_id" varchar(32) NOT NULL, | |
| 833 | + "rank" integer NOT NULL, | |
| 834 | + "eligible_entities" integer NOT NULL, | |
| 835 | + "percentile" real NOT NULL, | |
| 836 | + "value" double precision NOT NULL, | |
| 837 | + "unit" text NOT NULL, | |
| 838 | + "confidence" text DEFAULT 'MEDIUM' NOT NULL, | |
| 839 | + "inputs" jsonb DEFAULT '{}'::jsonb NOT NULL, | |
| 840 | + "breakdown" jsonb, | |
| 841 | + "previous_rank" integer, | |
| 842 | + "generated_at" timestamp with time zone DEFAULT now() NOT NULL | |
| 843 | +); | |
| 844 | +--> statement-breakpoint | |
| 845 | +CREATE INDEX "change_events_entity_idx" ON "change_events" USING btree ("entity_type","entity_id","created_at");--> statement-breakpoint | |
| 846 | +CREATE UNIQUE INDEX "connector_field_stats_uq" ON "connector_field_stats" USING btree ("connector_id","entity","field");--> statement-breakpoint | |
| 847 | +CREATE INDEX "ingest_runs_connector_idx" ON "ingest_runs" USING btree ("connector_id","started_at");--> statement-breakpoint | |
| 848 | +CREATE INDEX "provenance_source_idx" ON "provenance" USING btree ("source_id","source_record_id");--> statement-breakpoint | |
| 849 | +CREATE INDEX "provenance_pmid_idx" ON "provenance" USING btree ("pmid");--> statement-breakpoint | |
| 850 | +CREATE UNIQUE INDEX "source_records_uq" ON "source_records" USING btree ("source_id","entity_kind","source_record_id");--> statement-breakpoint | |
| 851 | +CREATE INDEX "source_records_canonical_idx" ON "source_records" USING btree ("canonical_type","canonical_id");--> statement-breakpoint | |
| 852 | +CREATE UNIQUE INDEX "sources_slug_uq" ON "sources" USING btree ("slug");--> statement-breakpoint | |
| 853 | +CREATE UNIQUE INDEX "unresolved_labels_uq" ON "unresolved_labels" USING btree ("source_id","entity_kind","normalized");--> statement-breakpoint | |
| 854 | +CREATE INDEX "unresolved_labels_count_idx" ON "unresolved_labels" USING btree ("status","count");--> statement-breakpoint | |
| 855 | +CREATE UNIQUE INDEX "anatomical_sites_slug_uq" ON "anatomical_sites" USING btree ("slug");--> statement-breakpoint | |
| 856 | +CREATE UNIQUE INDEX "cancer_aliases_uq" ON "cancer_aliases" USING btree ("cancer_id","normalized","alias_type");--> statement-breakpoint | |
| 857 | +CREATE INDEX "cancer_aliases_norm_idx" ON "cancer_aliases" USING btree ("normalized");--> statement-breakpoint | |
| 858 | +CREATE UNIQUE INDEX "cancer_anatomy_uq" ON "cancer_anatomy" USING btree ("cancer_id","site_id","relation");--> statement-breakpoint | |
| 859 | +CREATE UNIQUE INDEX "cancer_codes_uq" ON "cancer_codes" USING btree ("cancer_id","system","code");--> statement-breakpoint | |
| 860 | +CREATE INDEX "cancer_codes_lookup_idx" ON "cancer_codes" USING btree ("system","code");--> statement-breakpoint | |
| 861 | +CREATE UNIQUE INDEX "cancer_hierarchy_uq" ON "cancer_hierarchy" USING btree ("parent_id","child_id","hierarchy_type");--> statement-breakpoint | |
| 862 | +CREATE INDEX "cancer_hierarchy_child_idx" ON "cancer_hierarchy" USING btree ("child_id");--> statement-breakpoint | |
| 863 | +CREATE UNIQUE INDEX "cancers_slug_uq" ON "cancers" USING btree ("slug");--> statement-breakpoint | |
| 864 | +CREATE UNIQUE INDEX "cancers_ncit_uq" ON "cancers" USING btree ("primary_ncit_code");--> statement-breakpoint | |
| 865 | +CREATE INDEX "cancers_name_idx" ON "cancers" USING btree ("canonical_name");--> statement-breakpoint | |
| 866 | +CREATE INDEX "cancers_type_idx" ON "cancers" USING btree ("entity_type","malignant","top_level");--> statement-breakpoint | |
| 867 | +CREATE UNIQUE INDEX "geographies_slug_uq" ON "geographies" USING btree ("slug");--> statement-breakpoint | |
| 868 | +CREATE INDEX "geographies_iso3_idx" ON "geographies" USING btree ("iso3");--> statement-breakpoint | |
| 869 | +CREATE UNIQUE INDEX "biomarkers_slug_uq" ON "biomarkers" USING btree ("slug");--> statement-breakpoint | |
| 870 | +CREATE UNIQUE INDEX "cancer_gene_freq_uq" ON "cancer_gene_frequencies" USING btree ("cohort_id","gene_symbol","alteration_type");--> statement-breakpoint | |
| 871 | +CREATE INDEX "cancer_gene_freq_cancer_idx" ON "cancer_gene_frequencies" USING btree ("cancer_id","frequency");--> statement-breakpoint | |
| 872 | +CREATE INDEX "cancer_gene_freq_gene_idx" ON "cancer_gene_frequencies" USING btree ("gene_id");--> statement-breakpoint | |
| 873 | +CREATE UNIQUE INDEX "entity_embeddings_uq" ON "entity_embeddings" USING btree ("entity_type","entity_id","model");--> statement-breakpoint | |
| 874 | +CREATE UNIQUE INDEX "gene_aliases_uq" ON "gene_aliases" USING btree ("gene_id","alias","alias_type");--> statement-breakpoint | |
| 875 | +CREATE INDEX "gene_aliases_alias_idx" ON "gene_aliases" USING btree ("alias");--> statement-breakpoint | |
| 876 | +CREATE UNIQUE INDEX "genes_symbol_uq" ON "genes" USING btree ("symbol");--> statement-breakpoint | |
| 877 | +CREATE UNIQUE INDEX "genes_hgnc_uq" ON "genes" USING btree ("hgnc_id");--> statement-breakpoint | |
| 878 | +CREATE INDEX "genes_ensembl_idx" ON "genes" USING btree ("ensembl_gene_id");--> statement-breakpoint | |
| 879 | +CREATE INDEX "genes_ncbi_idx" ON "genes" USING btree ("ncbi_gene_id");--> statement-breakpoint | |
| 880 | +CREATE UNIQUE INDEX "genomic_cohorts_uq" ON "genomic_cohorts" USING btree ("source_id","study_id");--> statement-breakpoint | |
| 881 | +CREATE INDEX "genomic_cohorts_cancer_idx" ON "genomic_cohorts" USING btree ("cancer_id");--> statement-breakpoint | |
| 882 | +CREATE UNIQUE INDEX "variant_aliases_uq" ON "variant_aliases" USING btree ("variant_id","alias");--> statement-breakpoint | |
| 883 | +CREATE UNIQUE INDEX "variant_clinsig_uq" ON "variant_clinical_significance" USING btree ("clinvar_variation_id");--> statement-breakpoint | |
| 884 | +CREATE UNIQUE INDEX "variants_slug_uq" ON "variants" USING btree ("slug");--> statement-breakpoint | |
| 885 | +CREATE INDEX "variants_gene_idx" ON "variants" USING btree ("gene_id");--> statement-breakpoint | |
| 886 | +CREATE INDEX "variants_clinvar_idx" ON "variants" USING btree ("clinvar_variation_id");--> statement-breakpoint | |
| 887 | +CREATE INDEX "variants_civic_idx" ON "variants" USING btree ("civic_variant_id");--> statement-breakpoint | |
| 888 | +CREATE UNIQUE INDEX "drug_aliases_uq" ON "drug_aliases" USING btree ("drug_id","normalized","alias_type");--> statement-breakpoint | |
| 889 | +CREATE INDEX "drug_aliases_norm_idx" ON "drug_aliases" USING btree ("normalized");--> statement-breakpoint | |
| 890 | +CREATE INDEX "drug_approvals_drug_idx" ON "drug_approvals" USING btree ("drug_id");--> statement-breakpoint | |
| 891 | +CREATE INDEX "drug_approvals_cancer_idx" ON "drug_approvals" USING btree ("cancer_id");--> statement-breakpoint | |
| 892 | +CREATE UNIQUE INDEX "drugs_slug_uq" ON "drugs" USING btree ("slug");--> statement-breakpoint | |
| 893 | +CREATE INDEX "drugs_ncit_idx" ON "drugs" USING btree ("ncit_code");--> statement-breakpoint | |
| 894 | +CREATE INDEX "drugs_civic_idx" ON "drugs" USING btree ("civic_therapy_id");--> statement-breakpoint | |
| 895 | +CREATE INDEX "drugs_chembl_idx" ON "drugs" USING btree ("chembl_id");--> statement-breakpoint | |
| 896 | +CREATE UNIQUE INDEX "treatment_regimens_slug_uq" ON "treatment_regimens" USING btree ("slug");--> statement-breakpoint | |
| 897 | +CREATE UNIQUE INDEX "clinical_trials_nct_uq" ON "clinical_trials" USING btree ("nct_id");--> statement-breakpoint | |
| 898 | +CREATE INDEX "clinical_trials_status_idx" ON "clinical_trials" USING btree ("overall_status");--> statement-breakpoint | |
| 899 | +CREATE INDEX "clinical_trials_updated_idx" ON "clinical_trials" USING btree ("last_update_posted_date");--> statement-breakpoint | |
| 900 | +CREATE INDEX "clinical_trials_sponsor_idx" ON "clinical_trials" USING btree ("lead_sponsor");--> statement-breakpoint | |
| 901 | +CREATE UNIQUE INDEX "trial_conditions_uq" ON "trial_conditions" USING btree ("trial_id","normalized");--> statement-breakpoint | |
| 902 | +CREATE INDEX "trial_conditions_cancer_idx" ON "trial_conditions" USING btree ("cancer_id");--> statement-breakpoint | |
| 903 | +CREATE INDEX "trial_conditions_norm_idx" ON "trial_conditions" USING btree ("normalized");--> statement-breakpoint | |
| 904 | +CREATE UNIQUE INDEX "trial_interventions_uq" ON "trial_interventions" USING btree ("trial_id","normalized");--> statement-breakpoint | |
| 905 | +CREATE INDEX "trial_interventions_drug_idx" ON "trial_interventions" USING btree ("drug_id");--> statement-breakpoint | |
| 906 | +CREATE INDEX "trial_locations_trial_idx" ON "trial_locations" USING btree ("trial_id");--> statement-breakpoint | |
| 907 | +CREATE INDEX "trial_locations_country_idx" ON "trial_locations" USING btree ("country");--> statement-breakpoint | |
| 908 | +CREATE UNIQUE INDEX "trial_pulse_uq" ON "trial_pulse" USING btree ("day","cancer_id","phase");--> statement-breakpoint | |
| 909 | +CREATE UNIQUE INDEX "literature_counts_uq" ON "literature_counts" USING btree ("cancer_id","window_key");--> statement-breakpoint | |
| 910 | +CREATE INDEX "literature_counts_window_idx" ON "literature_counts" USING btree ("window_key","count");--> statement-breakpoint | |
| 911 | +CREATE UNIQUE INDEX "pub_entity_edges_uq" ON "publication_entity_edges" USING btree ("publication_id","entity_type","entity_id","method");--> statement-breakpoint | |
| 912 | +CREATE INDEX "pub_entity_edges_entity_idx" ON "publication_entity_edges" USING btree ("entity_type","entity_id");--> statement-breakpoint | |
| 913 | +CREATE UNIQUE INDEX "publications_pmid_uq" ON "publications" USING btree ("pmid");--> statement-breakpoint | |
| 914 | +CREATE INDEX "publications_doi_idx" ON "publications" USING btree ("doi");--> statement-breakpoint | |
| 915 | +CREATE INDEX "publications_year_idx" ON "publications" USING btree ("pub_year");--> statement-breakpoint | |
| 916 | +CREATE INDEX "publications_retracted_idx" ON "publications" USING btree ("retracted");--> statement-breakpoint | |
| 917 | +CREATE UNIQUE INDEX "civic_evidence_uq" ON "civic_evidence_items" USING btree ("civic_id");--> statement-breakpoint | |
| 918 | +CREATE INDEX "civic_evidence_cancer_idx" ON "civic_evidence_items" USING btree ("cancer_id");--> statement-breakpoint | |
| 919 | +CREATE INDEX "civic_evidence_gene_idx" ON "civic_evidence_items" USING btree ("gene_symbols");--> statement-breakpoint | |
| 920 | +CREATE UNIQUE INDEX "knowledge_edges_uq" ON "knowledge_edges" USING btree ("source_entity_type","source_entity_id","target_entity_type","target_entity_id","relationship_type","source_id","source_record_id");--> statement-breakpoint | |
| 921 | +CREATE INDEX "knowledge_edges_source_idx" ON "knowledge_edges" USING btree ("source_entity_type","source_entity_id","relationship_type");--> statement-breakpoint | |
| 922 | +CREATE INDEX "knowledge_edges_target_idx" ON "knowledge_edges" USING btree ("target_entity_type","target_entity_id","relationship_type");--> statement-breakpoint | |
| 923 | +CREATE UNIQUE INDEX "risk_factors_slug_uq" ON "risk_factors" USING btree ("slug");--> statement-breakpoint | |
| 924 | +CREATE UNIQUE INDEX "epi_obs_uq" ON "epidemiology_observations" USING btree ("cancer_id","geography_id","year","sex","age_group","metric","source_id","site_definition");--> statement-breakpoint | |
| 925 | +CREATE INDEX "epi_obs_lookup_idx" ON "epidemiology_observations" USING btree ("metric","geography_id","year","sex");--> statement-breakpoint | |
| 926 | +CREATE INDEX "epi_obs_cancer_idx" ON "epidemiology_observations" USING btree ("cancer_id","metric");--> statement-breakpoint | |
| 927 | +CREATE INDEX "survival_obs_cancer_idx" ON "survival_observations" USING btree ("cancer_id","survival_type","duration_months");--> statement-breakpoint | |
| 928 | +CREATE UNIQUE INDEX "ai_answers_uq" ON "ai_answers" USING btree ("kind","question_hash","prompt_version");--> statement-breakpoint | |
| 929 | +CREATE UNIQUE INDEX "api_keys_hash_uq" ON "api_keys" USING btree ("key_hash");--> statement-breakpoint | |
| 930 | +CREATE UNIQUE INDEX "entity_counters_uq" ON "entity_counters" USING btree ("entity_type","entity_id");--> statement-breakpoint | |
| 931 | +CREATE INDEX "entity_counters_trials_idx" ON "entity_counters" USING btree ("entity_type","active_trial_count");--> statement-breakpoint | |
| 932 | +CREATE UNIQUE INDEX "metric_definitions_slug_uq" ON "metric_definitions" USING btree ("slug");--> statement-breakpoint | |
| 933 | +CREATE INDEX "ranking_snapshots_lookup_idx" ON "ranking_snapshots" USING btree ("metric_slug","scope_key","is_current");--> statement-breakpoint | |
| 934 | +CREATE UNIQUE INDEX "rankings_uq" ON "rankings" USING btree ("snapshot_id","cancer_id");--> statement-breakpoint | |
| 935 | +CREATE INDEX "rankings_cancer_idx" ON "rankings" USING btree ("cancer_id","metric_slug");--> statement-breakpoint | |
| 936 | +CREATE INDEX "rankings_lookup_idx" ON "rankings" USING btree ("metric_slug","scope_key","rank"); | |
| \ No newline at end of file | ||
added
packages/database/migrations/meta/0000_snapshot.json
+6918 −0
@@ -0,0 +1,6918 @@ | ||
| 1 | +{ | |
| 2 | + "id": "7cee03f3-3bb2-47c2-b164-ea15161ffedd", | |
| 3 | + "prevId": "00000000-0000-0000-0000-000000000000", | |
| 4 | + "version": "7", | |
| 5 | + "dialect": "postgresql", | |
| 6 | + "tables": { | |
| 7 | + "public.audit_log": { | |
| 8 | + "name": "audit_log", | |
| 9 | + "schema": "", | |
| 10 | + "columns": { | |
| 11 | + "id": { | |
| 12 | + "name": "id", | |
| 13 | + "type": "bigserial", | |
| 14 | + "primaryKey": true, | |
| 15 | + "notNull": true | |
| 16 | + }, | |
| 17 | + "actor": { | |
| 18 | + "name": "actor", | |
| 19 | + "type": "text", | |
| 20 | + "primaryKey": false, | |
| 21 | + "notNull": true | |
| 22 | + }, | |
| 23 | + "action": { | |
| 24 | + "name": "action", | |
| 25 | + "type": "text", | |
| 26 | + "primaryKey": false, | |
| 27 | + "notNull": true | |
| 28 | + }, | |
| 29 | + "entity_type": { | |
| 30 | + "name": "entity_type", | |
| 31 | + "type": "text", | |
| 32 | + "primaryKey": false, | |
| 33 | + "notNull": false | |
| 34 | + }, | |
| 35 | + "entity_id": { | |
| 36 | + "name": "entity_id", | |
| 37 | + "type": "text", | |
| 38 | + "primaryKey": false, | |
| 39 | + "notNull": false | |
| 40 | + }, | |
| 41 | + "before": { | |
| 42 | + "name": "before", | |
| 43 | + "type": "jsonb", | |
| 44 | + "primaryKey": false, | |
| 45 | + "notNull": false | |
| 46 | + }, | |
| 47 | + "after": { | |
| 48 | + "name": "after", | |
| 49 | + "type": "jsonb", | |
| 50 | + "primaryKey": false, | |
| 51 | + "notNull": false | |
| 52 | + }, | |
| 53 | + "reason": { | |
| 54 | + "name": "reason", | |
| 55 | + "type": "text", | |
| 56 | + "primaryKey": false, | |
| 57 | + "notNull": false | |
| 58 | + }, | |
| 59 | + "created_at": { | |
| 60 | + "name": "created_at", | |
| 61 | + "type": "timestamp with time zone", | |
| 62 | + "primaryKey": false, | |
| 63 | + "notNull": true, | |
| 64 | + "default": "now()" | |
| 65 | + } | |
| 66 | + }, | |
| 67 | + "indexes": {}, | |
| 68 | + "foreignKeys": {}, | |
| 69 | + "compositePrimaryKeys": {}, | |
| 70 | + "uniqueConstraints": {}, | |
| 71 | + "policies": {}, | |
| 72 | + "checkConstraints": {}, | |
| 73 | + "isRLSEnabled": false | |
| 74 | + }, | |
| 75 | + "public.change_events": { | |
| 76 | + "name": "change_events", | |
| 77 | + "schema": "", | |
| 78 | + "columns": { | |
| 79 | + "id": { | |
| 80 | + "name": "id", | |
| 81 | + "type": "bigserial", | |
| 82 | + "primaryKey": true, | |
| 83 | + "notNull": true | |
| 84 | + }, | |
| 85 | + "entity_type": { | |
| 86 | + "name": "entity_type", | |
| 87 | + "type": "text", | |
| 88 | + "primaryKey": false, | |
| 89 | + "notNull": true | |
| 90 | + }, | |
| 91 | + "entity_id": { | |
| 92 | + "name": "entity_id", | |
| 93 | + "type": "text", | |
| 94 | + "primaryKey": false, | |
| 95 | + "notNull": true | |
| 96 | + }, | |
| 97 | + "kind": { | |
| 98 | + "name": "kind", | |
| 99 | + "type": "text", | |
| 100 | + "primaryKey": false, | |
| 101 | + "notNull": true | |
| 102 | + }, | |
| 103 | + "summary": { | |
| 104 | + "name": "summary", | |
| 105 | + "type": "text", | |
| 106 | + "primaryKey": false, | |
| 107 | + "notNull": true | |
| 108 | + }, | |
| 109 | + "before": { | |
| 110 | + "name": "before", | |
| 111 | + "type": "jsonb", | |
| 112 | + "primaryKey": false, | |
| 113 | + "notNull": false | |
| 114 | + }, | |
| 115 | + "after": { | |
| 116 | + "name": "after", | |
| 117 | + "type": "jsonb", | |
| 118 | + "primaryKey": false, | |
| 119 | + "notNull": false | |
| 120 | + }, | |
| 121 | + "ingest_run_id": { | |
| 122 | + "name": "ingest_run_id", | |
| 123 | + "type": "text", | |
| 124 | + "primaryKey": false, | |
| 125 | + "notNull": false | |
| 126 | + }, | |
| 127 | + "created_at": { | |
| 128 | + "name": "created_at", | |
| 129 | + "type": "timestamp with time zone", | |
| 130 | + "primaryKey": false, | |
| 131 | + "notNull": true, | |
| 132 | + "default": "now()" | |
| 133 | + } | |
| 134 | + }, | |
| 135 | + "indexes": { | |
| 136 | + "change_events_entity_idx": { | |
| 137 | + "name": "change_events_entity_idx", | |
| 138 | + "columns": [ | |
| 139 | + { | |
| 140 | + "expression": "entity_type", | |
| 141 | + "isExpression": false, | |
| 142 | + "asc": true, | |
| 143 | + "nulls": "last" | |
| 144 | + }, | |
| 145 | + { | |
| 146 | + "expression": "entity_id", | |
| 147 | + "isExpression": false, | |
| 148 | + "asc": true, | |
| 149 | + "nulls": "last" | |
| 150 | + }, | |
| 151 | + { | |
| 152 | + "expression": "created_at", | |
| 153 | + "isExpression": false, | |
| 154 | + "asc": true, | |
| 155 | + "nulls": "last" | |
| 156 | + } | |
| 157 | + ], | |
| 158 | + "isUnique": false, | |
| 159 | + "concurrently": false, | |
| 160 | + "method": "btree", | |
| 161 | + "with": {} | |
| 162 | + } | |
| 163 | + }, | |
| 164 | + "foreignKeys": {}, | |
| 165 | + "compositePrimaryKeys": {}, | |
| 166 | + "uniqueConstraints": {}, | |
| 167 | + "policies": {}, | |
| 168 | + "checkConstraints": {}, | |
| 169 | + "isRLSEnabled": false | |
| 170 | + }, | |
| 171 | + "public.connector_cursors": { | |
| 172 | + "name": "connector_cursors", | |
| 173 | + "schema": "", | |
| 174 | + "columns": { | |
| 175 | + "connector_id": { | |
| 176 | + "name": "connector_id", | |
| 177 | + "type": "text", | |
| 178 | + "primaryKey": true, | |
| 179 | + "notNull": true | |
| 180 | + }, | |
| 181 | + "cursor": { | |
| 182 | + "name": "cursor", | |
| 183 | + "type": "jsonb", | |
| 184 | + "primaryKey": false, | |
| 185 | + "notNull": true, | |
| 186 | + "default": "'{}'::jsonb" | |
| 187 | + }, | |
| 188 | + "last_success_at": { | |
| 189 | + "name": "last_success_at", | |
| 190 | + "type": "timestamp with time zone", | |
| 191 | + "primaryKey": false, | |
| 192 | + "notNull": false | |
| 193 | + }, | |
| 194 | + "last_attempt_at": { | |
| 195 | + "name": "last_attempt_at", | |
| 196 | + "type": "timestamp with time zone", | |
| 197 | + "primaryKey": false, | |
| 198 | + "notNull": false | |
| 199 | + }, | |
| 200 | + "paused": { | |
| 201 | + "name": "paused", | |
| 202 | + "type": "boolean", | |
| 203 | + "primaryKey": false, | |
| 204 | + "notNull": true, | |
| 205 | + "default": false | |
| 206 | + }, | |
| 207 | + "health": { | |
| 208 | + "name": "health", | |
| 209 | + "type": "text", | |
| 210 | + "primaryKey": false, | |
| 211 | + "notNull": true, | |
| 212 | + "default": "'unknown'" | |
| 213 | + }, | |
| 214 | + "health_detail": { | |
| 215 | + "name": "health_detail", | |
| 216 | + "type": "text", | |
| 217 | + "primaryKey": false, | |
| 218 | + "notNull": false | |
| 219 | + }, | |
| 220 | + "updated_at": { | |
| 221 | + "name": "updated_at", | |
| 222 | + "type": "timestamp with time zone", | |
| 223 | + "primaryKey": false, | |
| 224 | + "notNull": true, | |
| 225 | + "default": "now()" | |
| 226 | + } | |
| 227 | + }, | |
| 228 | + "indexes": {}, | |
| 229 | + "foreignKeys": {}, | |
| 230 | + "compositePrimaryKeys": {}, | |
| 231 | + "uniqueConstraints": {}, | |
| 232 | + "policies": {}, | |
| 233 | + "checkConstraints": {}, | |
| 234 | + "isRLSEnabled": false | |
| 235 | + }, | |
| 236 | + "public.connector_field_stats": { | |
| 237 | + "name": "connector_field_stats", | |
| 238 | + "schema": "", | |
| 239 | + "columns": { | |
| 240 | + "id": { | |
| 241 | + "name": "id", | |
| 242 | + "type": "bigserial", | |
| 243 | + "primaryKey": true, | |
| 244 | + "notNull": true | |
| 245 | + }, | |
| 246 | + "connector_id": { | |
| 247 | + "name": "connector_id", | |
| 248 | + "type": "text", | |
| 249 | + "primaryKey": false, | |
| 250 | + "notNull": true | |
| 251 | + }, | |
| 252 | + "entity": { | |
| 253 | + "name": "entity", | |
| 254 | + "type": "text", | |
| 255 | + "primaryKey": false, | |
| 256 | + "notNull": true | |
| 257 | + }, | |
| 258 | + "field": { | |
| 259 | + "name": "field", | |
| 260 | + "type": "text", | |
| 261 | + "primaryKey": false, | |
| 262 | + "notNull": true | |
| 263 | + }, | |
| 264 | + "types": { | |
| 265 | + "name": "types", | |
| 266 | + "type": "text[]", | |
| 267 | + "primaryKey": false, | |
| 268 | + "notNull": true, | |
| 269 | + "default": "'{}'" | |
| 270 | + }, | |
| 271 | + "seen_count": { | |
| 272 | + "name": "seen_count", | |
| 273 | + "type": "integer", | |
| 274 | + "primaryKey": false, | |
| 275 | + "notNull": true, | |
| 276 | + "default": 0 | |
| 277 | + }, | |
| 278 | + "null_count": { | |
| 279 | + "name": "null_count", | |
| 280 | + "type": "integer", | |
| 281 | + "primaryKey": false, | |
| 282 | + "notNull": true, | |
| 283 | + "default": 0 | |
| 284 | + }, | |
| 285 | + "first_seen_run": { | |
| 286 | + "name": "first_seen_run", | |
| 287 | + "type": "text", | |
| 288 | + "primaryKey": false, | |
| 289 | + "notNull": false | |
| 290 | + }, | |
| 291 | + "last_seen_run": { | |
| 292 | + "name": "last_seen_run", | |
| 293 | + "type": "text", | |
| 294 | + "primaryKey": false, | |
| 295 | + "notNull": false | |
| 296 | + }, | |
| 297 | + "updated_at": { | |
| 298 | + "name": "updated_at", | |
| 299 | + "type": "timestamp with time zone", | |
| 300 | + "primaryKey": false, | |
| 301 | + "notNull": true, | |
| 302 | + "default": "now()" | |
| 303 | + } | |
| 304 | + }, | |
| 305 | + "indexes": { | |
| 306 | + "connector_field_stats_uq": { | |
| 307 | + "name": "connector_field_stats_uq", | |
| 308 | + "columns": [ | |
| 309 | + { | |
| 310 | + "expression": "connector_id", | |
| 311 | + "isExpression": false, | |
| 312 | + "asc": true, | |
| 313 | + "nulls": "last" | |
| 314 | + }, | |
| 315 | + { | |
| 316 | + "expression": "entity", | |
| 317 | + "isExpression": false, | |
| 318 | + "asc": true, | |
| 319 | + "nulls": "last" | |
| 320 | + }, | |
| 321 | + { | |
| 322 | + "expression": "field", | |
| 323 | + "isExpression": false, | |
| 324 | + "asc": true, | |
| 325 | + "nulls": "last" | |
| 326 | + } | |
| 327 | + ], | |
| 328 | + "isUnique": true, | |
| 329 | + "concurrently": false, | |
| 330 | + "method": "btree", | |
| 331 | + "with": {} | |
| 332 | + } | |
| 333 | + }, | |
| 334 | + "foreignKeys": {}, | |
| 335 | + "compositePrimaryKeys": {}, | |
| 336 | + "uniqueConstraints": {}, | |
| 337 | + "policies": {}, | |
| 338 | + "checkConstraints": {}, | |
| 339 | + "isRLSEnabled": false | |
| 340 | + }, | |
| 341 | + "public.entity_merges": { | |
| 342 | + "name": "entity_merges", | |
| 343 | + "schema": "", | |
| 344 | + "columns": { | |
| 345 | + "id": { | |
| 346 | + "name": "id", | |
| 347 | + "type": "bigserial", | |
| 348 | + "primaryKey": true, | |
| 349 | + "notNull": true | |
| 350 | + }, | |
| 351 | + "entity_type": { | |
| 352 | + "name": "entity_type", | |
| 353 | + "type": "text", | |
| 354 | + "primaryKey": false, | |
| 355 | + "notNull": true | |
| 356 | + }, | |
| 357 | + "keep_id": { | |
| 358 | + "name": "keep_id", | |
| 359 | + "type": "text", | |
| 360 | + "primaryKey": false, | |
| 361 | + "notNull": true | |
| 362 | + }, | |
| 363 | + "merge_id": { | |
| 364 | + "name": "merge_id", | |
| 365 | + "type": "text", | |
| 366 | + "primaryKey": false, | |
| 367 | + "notNull": true | |
| 368 | + }, | |
| 369 | + "evidence": { | |
| 370 | + "name": "evidence", | |
| 371 | + "type": "jsonb", | |
| 372 | + "primaryKey": false, | |
| 373 | + "notNull": true, | |
| 374 | + "default": "'{}'::jsonb" | |
| 375 | + }, | |
| 376 | + "status": { | |
| 377 | + "name": "status", | |
| 378 | + "type": "text", | |
| 379 | + "primaryKey": false, | |
| 380 | + "notNull": true, | |
| 381 | + "default": "'proposed'" | |
| 382 | + }, | |
| 383 | + "decided_by": { | |
| 384 | + "name": "decided_by", | |
| 385 | + "type": "text", | |
| 386 | + "primaryKey": false, | |
| 387 | + "notNull": false | |
| 388 | + }, | |
| 389 | + "decided_at": { | |
| 390 | + "name": "decided_at", | |
| 391 | + "type": "timestamp with time zone", | |
| 392 | + "primaryKey": false, | |
| 393 | + "notNull": false | |
| 394 | + }, | |
| 395 | + "created_at": { | |
| 396 | + "name": "created_at", | |
| 397 | + "type": "timestamp with time zone", | |
| 398 | + "primaryKey": false, | |
| 399 | + "notNull": true, | |
| 400 | + "default": "now()" | |
| 401 | + } | |
| 402 | + }, | |
| 403 | + "indexes": {}, | |
| 404 | + "foreignKeys": {}, | |
| 405 | + "compositePrimaryKeys": {}, | |
| 406 | + "uniqueConstraints": {}, | |
| 407 | + "policies": {}, | |
| 408 | + "checkConstraints": {}, | |
| 409 | + "isRLSEnabled": false | |
| 410 | + }, | |
| 411 | + "public.id_sequences": { | |
| 412 | + "name": "id_sequences", | |
| 413 | + "schema": "", | |
| 414 | + "columns": { | |
| 415 | + "namespace": { | |
| 416 | + "name": "namespace", | |
| 417 | + "type": "varchar(16)", | |
| 418 | + "primaryKey": true, | |
| 419 | + "notNull": true | |
| 420 | + }, | |
| 421 | + "next": { | |
| 422 | + "name": "next", | |
| 423 | + "type": "bigint", | |
| 424 | + "primaryKey": false, | |
| 425 | + "notNull": true, | |
| 426 | + "default": 1 | |
| 427 | + } | |
| 428 | + }, | |
| 429 | + "indexes": {}, | |
| 430 | + "foreignKeys": {}, | |
| 431 | + "compositePrimaryKeys": {}, | |
| 432 | + "uniqueConstraints": {}, | |
| 433 | + "policies": {}, | |
| 434 | + "checkConstraints": {}, | |
| 435 | + "isRLSEnabled": false | |
| 436 | + }, | |
| 437 | + "public.ingest_runs": { | |
| 438 | + "name": "ingest_runs", | |
| 439 | + "schema": "", | |
| 440 | + "columns": { | |
| 441 | + "id": { | |
| 442 | + "name": "id", | |
| 443 | + "type": "text", | |
| 444 | + "primaryKey": true, | |
| 445 | + "notNull": true | |
| 446 | + }, | |
| 447 | + "connector_id": { | |
| 448 | + "name": "connector_id", | |
| 449 | + "type": "text", | |
| 450 | + "primaryKey": false, | |
| 451 | + "notNull": true | |
| 452 | + }, | |
| 453 | + "source_id": { | |
| 454 | + "name": "source_id", | |
| 455 | + "type": "varchar(32)", | |
| 456 | + "primaryKey": false, | |
| 457 | + "notNull": true | |
| 458 | + }, | |
| 459 | + "mode": { | |
| 460 | + "name": "mode", | |
| 461 | + "type": "text", | |
| 462 | + "primaryKey": false, | |
| 463 | + "notNull": true, | |
| 464 | + "default": "'incremental'" | |
| 465 | + }, | |
| 466 | + "status": { | |
| 467 | + "name": "status", | |
| 468 | + "type": "text", | |
| 469 | + "primaryKey": false, | |
| 470 | + "notNull": true, | |
| 471 | + "default": "'running'" | |
| 472 | + }, | |
| 473 | + "started_at": { | |
| 474 | + "name": "started_at", | |
| 475 | + "type": "timestamp with time zone", | |
| 476 | + "primaryKey": false, | |
| 477 | + "notNull": true, | |
| 478 | + "default": "now()" | |
| 479 | + }, | |
| 480 | + "finished_at": { | |
| 481 | + "name": "finished_at", | |
| 482 | + "type": "timestamp with time zone", | |
| 483 | + "primaryKey": false, | |
| 484 | + "notNull": false | |
| 485 | + }, | |
| 486 | + "duration_ms": { | |
| 487 | + "name": "duration_ms", | |
| 488 | + "type": "integer", | |
| 489 | + "primaryKey": false, | |
| 490 | + "notNull": false | |
| 491 | + }, | |
| 492 | + "records_fetched": { | |
| 493 | + "name": "records_fetched", | |
| 494 | + "type": "integer", | |
| 495 | + "primaryKey": false, | |
| 496 | + "notNull": true, | |
| 497 | + "default": 0 | |
| 498 | + }, | |
| 499 | + "records_created": { | |
| 500 | + "name": "records_created", | |
| 501 | + "type": "integer", | |
| 502 | + "primaryKey": false, | |
| 503 | + "notNull": true, | |
| 504 | + "default": 0 | |
| 505 | + }, | |
| 506 | + "records_updated": { | |
| 507 | + "name": "records_updated", | |
| 508 | + "type": "integer", | |
| 509 | + "primaryKey": false, | |
| 510 | + "notNull": true, | |
| 511 | + "default": 0 | |
| 512 | + }, | |
| 513 | + "records_unchanged": { | |
| 514 | + "name": "records_unchanged", | |
| 515 | + "type": "integer", | |
| 516 | + "primaryKey": false, | |
| 517 | + "notNull": true, | |
| 518 | + "default": 0 | |
| 519 | + }, | |
| 520 | + "records_rejected": { | |
| 521 | + "name": "records_rejected", | |
| 522 | + "type": "integer", | |
| 523 | + "primaryKey": false, | |
| 524 | + "notNull": true, | |
| 525 | + "default": 0 | |
| 526 | + }, | |
| 527 | + "http_requests": { | |
| 528 | + "name": "http_requests", | |
| 529 | + "type": "integer", | |
| 530 | + "primaryKey": false, | |
| 531 | + "notNull": true, | |
| 532 | + "default": 0 | |
| 533 | + }, | |
| 534 | + "http_failures": { | |
| 535 | + "name": "http_failures", | |
| 536 | + "type": "integer", | |
| 537 | + "primaryKey": false, | |
| 538 | + "notNull": true, | |
| 539 | + "default": 0 | |
| 540 | + }, | |
| 541 | + "rate_limit_events": { | |
| 542 | + "name": "rate_limit_events", | |
| 543 | + "type": "integer", | |
| 544 | + "primaryKey": false, | |
| 545 | + "notNull": true, | |
| 546 | + "default": 0 | |
| 547 | + }, | |
| 548 | + "validation_failures": { | |
| 549 | + "name": "validation_failures", | |
| 550 | + "type": "integer", | |
| 551 | + "primaryKey": false, | |
| 552 | + "notNull": true, | |
| 553 | + "default": 0 | |
| 554 | + }, | |
| 555 | + "schema_drift": { | |
| 556 | + "name": "schema_drift", | |
| 557 | + "type": "jsonb", | |
| 558 | + "primaryKey": false, | |
| 559 | + "notNull": true, | |
| 560 | + "default": "'[]'::jsonb" | |
| 561 | + }, | |
| 562 | + "cursor_before": { | |
| 563 | + "name": "cursor_before", | |
| 564 | + "type": "jsonb", | |
| 565 | + "primaryKey": false, | |
| 566 | + "notNull": false | |
| 567 | + }, | |
| 568 | + "cursor_after": { | |
| 569 | + "name": "cursor_after", | |
| 570 | + "type": "jsonb", | |
| 571 | + "primaryKey": false, | |
| 572 | + "notNull": false | |
| 573 | + }, | |
| 574 | + "error": { | |
| 575 | + "name": "error", | |
| 576 | + "type": "text", | |
| 577 | + "primaryKey": false, | |
| 578 | + "notNull": false | |
| 579 | + }, | |
| 580 | + "log": { | |
| 581 | + "name": "log", | |
| 582 | + "type": "jsonb", | |
| 583 | + "primaryKey": false, | |
| 584 | + "notNull": true, | |
| 585 | + "default": "'[]'::jsonb" | |
| 586 | + }, | |
| 587 | + "dataset_version": { | |
| 588 | + "name": "dataset_version", | |
| 589 | + "type": "text", | |
| 590 | + "primaryKey": false, | |
| 591 | + "notNull": false | |
| 592 | + }, | |
| 593 | + "anomaly": { | |
| 594 | + "name": "anomaly", | |
| 595 | + "type": "text", | |
| 596 | + "primaryKey": false, | |
| 597 | + "notNull": false | |
| 598 | + } | |
| 599 | + }, | |
| 600 | + "indexes": { | |
| 601 | + "ingest_runs_connector_idx": { | |
| 602 | + "name": "ingest_runs_connector_idx", | |
| 603 | + "columns": [ | |
| 604 | + { | |
| 605 | + "expression": "connector_id", | |
| 606 | + "isExpression": false, | |
| 607 | + "asc": true, | |
| 608 | + "nulls": "last" | |
| 609 | + }, | |
| 610 | + { | |
| 611 | + "expression": "started_at", | |
| 612 | + "isExpression": false, | |
| 613 | + "asc": true, | |
| 614 | + "nulls": "last" | |
| 615 | + } | |
| 616 | + ], | |
| 617 | + "isUnique": false, | |
| 618 | + "concurrently": false, | |
| 619 | + "method": "btree", | |
| 620 | + "with": {} | |
| 621 | + } | |
| 622 | + }, | |
| 623 | + "foreignKeys": {}, | |
| 624 | + "compositePrimaryKeys": {}, | |
| 625 | + "uniqueConstraints": {}, | |
| 626 | + "policies": {}, | |
| 627 | + "checkConstraints": {}, | |
| 628 | + "isRLSEnabled": false | |
| 629 | + }, | |
| 630 | + "public.provenance": { | |
| 631 | + "name": "provenance", | |
| 632 | + "schema": "", | |
| 633 | + "columns": { | |
| 634 | + "id": { | |
| 635 | + "name": "id", | |
| 636 | + "type": "bigserial", | |
| 637 | + "primaryKey": true, | |
| 638 | + "notNull": true | |
| 639 | + }, | |
| 640 | + "public_id": { | |
| 641 | + "name": "public_id", | |
| 642 | + "type": "varchar(32)", | |
| 643 | + "primaryKey": false, | |
| 644 | + "notNull": false | |
| 645 | + }, | |
| 646 | + "source_id": { | |
| 647 | + "name": "source_id", | |
| 648 | + "type": "varchar(32)", | |
| 649 | + "primaryKey": false, | |
| 650 | + "notNull": true | |
| 651 | + }, | |
| 652 | + "source_record_id": { | |
| 653 | + "name": "source_record_id", | |
| 654 | + "type": "text", | |
| 655 | + "primaryKey": false, | |
| 656 | + "notNull": false | |
| 657 | + }, | |
| 658 | + "source_url": { | |
| 659 | + "name": "source_url", | |
| 660 | + "type": "text", | |
| 661 | + "primaryKey": false, | |
| 662 | + "notNull": false | |
| 663 | + }, | |
| 664 | + "dataset": { | |
| 665 | + "name": "dataset", | |
| 666 | + "type": "text", | |
| 667 | + "primaryKey": false, | |
| 668 | + "notNull": false | |
| 669 | + }, | |
| 670 | + "dataset_version": { | |
| 671 | + "name": "dataset_version", | |
| 672 | + "type": "text", | |
| 673 | + "primaryKey": false, | |
| 674 | + "notNull": false | |
| 675 | + }, | |
| 676 | + "publication_id": { | |
| 677 | + "name": "publication_id", | |
| 678 | + "type": "varchar(32)", | |
| 679 | + "primaryKey": false, | |
| 680 | + "notNull": false | |
| 681 | + }, | |
| 682 | + "pmid": { | |
| 683 | + "name": "pmid", | |
| 684 | + "type": "text", | |
| 685 | + "primaryKey": false, | |
| 686 | + "notNull": false | |
| 687 | + }, | |
| 688 | + "doi": { | |
| 689 | + "name": "doi", | |
| 690 | + "type": "text", | |
| 691 | + "primaryKey": false, | |
| 692 | + "notNull": false | |
| 693 | + }, | |
| 694 | + "retrieved_at": { | |
| 695 | + "name": "retrieved_at", | |
| 696 | + "type": "timestamp with time zone", | |
| 697 | + "primaryKey": false, | |
| 698 | + "notNull": true | |
| 699 | + }, | |
| 700 | + "published_at": { | |
| 701 | + "name": "published_at", | |
| 702 | + "type": "text", | |
| 703 | + "primaryKey": false, | |
| 704 | + "notNull": false | |
| 705 | + }, | |
| 706 | + "updated_at_source": { | |
| 707 | + "name": "updated_at_source", | |
| 708 | + "type": "text", | |
| 709 | + "primaryKey": false, | |
| 710 | + "notNull": false | |
| 711 | + }, | |
| 712 | + "geography": { | |
| 713 | + "name": "geography", | |
| 714 | + "type": "text", | |
| 715 | + "primaryKey": false, | |
| 716 | + "notNull": false | |
| 717 | + }, | |
| 718 | + "population": { | |
| 719 | + "name": "population", | |
| 720 | + "type": "text", | |
| 721 | + "primaryKey": false, | |
| 722 | + "notNull": false | |
| 723 | + }, | |
| 724 | + "cohort_size": { | |
| 725 | + "name": "cohort_size", | |
| 726 | + "type": "integer", | |
| 727 | + "primaryKey": false, | |
| 728 | + "notNull": false | |
| 729 | + }, | |
| 730 | + "methodology": { | |
| 731 | + "name": "methodology", | |
| 732 | + "type": "text", | |
| 733 | + "primaryKey": false, | |
| 734 | + "notNull": false | |
| 735 | + }, | |
| 736 | + "evidence_type": { | |
| 737 | + "name": "evidence_type", | |
| 738 | + "type": "text", | |
| 739 | + "primaryKey": false, | |
| 740 | + "notNull": true | |
| 741 | + }, | |
| 742 | + "access_level": { | |
| 743 | + "name": "access_level", | |
| 744 | + "type": "text", | |
| 745 | + "primaryKey": false, | |
| 746 | + "notNull": true, | |
| 747 | + "default": "'open'" | |
| 748 | + }, | |
| 749 | + "confidence": { | |
| 750 | + "name": "confidence", | |
| 751 | + "type": "real", | |
| 752 | + "primaryKey": false, | |
| 753 | + "notNull": false | |
| 754 | + }, | |
| 755 | + "license": { | |
| 756 | + "name": "license", | |
| 757 | + "type": "text", | |
| 758 | + "primaryKey": false, | |
| 759 | + "notNull": false | |
| 760 | + }, | |
| 761 | + "ingest_run_id": { | |
| 762 | + "name": "ingest_run_id", | |
| 763 | + "type": "text", | |
| 764 | + "primaryKey": false, | |
| 765 | + "notNull": false | |
| 766 | + }, | |
| 767 | + "created_at": { | |
| 768 | + "name": "created_at", | |
| 769 | + "type": "timestamp with time zone", | |
| 770 | + "primaryKey": false, | |
| 771 | + "notNull": true, | |
| 772 | + "default": "now()" | |
| 773 | + } | |
| 774 | + }, | |
| 775 | + "indexes": { | |
| 776 | + "provenance_source_idx": { | |
| 777 | + "name": "provenance_source_idx", | |
| 778 | + "columns": [ | |
| 779 | + { | |
| 780 | + "expression": "source_id", | |
| 781 | + "isExpression": false, | |
| 782 | + "asc": true, | |
| 783 | + "nulls": "last" | |
| 784 | + }, | |
| 785 | + { | |
| 786 | + "expression": "source_record_id", | |
| 787 | + "isExpression": false, | |
| 788 | + "asc": true, | |
| 789 | + "nulls": "last" | |
| 790 | + } | |
| 791 | + ], | |
| 792 | + "isUnique": false, | |
| 793 | + "concurrently": false, | |
| 794 | + "method": "btree", | |
| 795 | + "with": {} | |
| 796 | + }, | |
| 797 | + "provenance_pmid_idx": { | |
| 798 | + "name": "provenance_pmid_idx", | |
| 799 | + "columns": [ | |
| 800 | + { | |
| 801 | + "expression": "pmid", | |
| 802 | + "isExpression": false, | |
| 803 | + "asc": true, | |
| 804 | + "nulls": "last" | |
| 805 | + } | |
| 806 | + ], | |
| 807 | + "isUnique": false, | |
| 808 | + "concurrently": false, | |
| 809 | + "method": "btree", | |
| 810 | + "with": {} | |
| 811 | + } | |
| 812 | + }, | |
| 813 | + "foreignKeys": {}, | |
| 814 | + "compositePrimaryKeys": {}, | |
| 815 | + "uniqueConstraints": {}, | |
| 816 | + "policies": {}, | |
| 817 | + "checkConstraints": {}, | |
| 818 | + "isRLSEnabled": false | |
| 819 | + }, | |
| 820 | + "public.source_records": { | |
| 821 | + "name": "source_records", | |
| 822 | + "schema": "", | |
| 823 | + "columns": { | |
| 824 | + "id": { | |
| 825 | + "name": "id", | |
| 826 | + "type": "bigserial", | |
| 827 | + "primaryKey": true, | |
| 828 | + "notNull": true | |
| 829 | + }, | |
| 830 | + "source_id": { | |
| 831 | + "name": "source_id", | |
| 832 | + "type": "varchar(32)", | |
| 833 | + "primaryKey": false, | |
| 834 | + "notNull": true | |
| 835 | + }, | |
| 836 | + "entity_kind": { | |
| 837 | + "name": "entity_kind", | |
| 838 | + "type": "text", | |
| 839 | + "primaryKey": false, | |
| 840 | + "notNull": true | |
| 841 | + }, | |
| 842 | + "source_record_id": { | |
| 843 | + "name": "source_record_id", | |
| 844 | + "type": "text", | |
| 845 | + "primaryKey": false, | |
| 846 | + "notNull": true | |
| 847 | + }, | |
| 848 | + "payload_hash": { | |
| 849 | + "name": "payload_hash", | |
| 850 | + "type": "text", | |
| 851 | + "primaryKey": false, | |
| 852 | + "notNull": true | |
| 853 | + }, | |
| 854 | + "raw_path": { | |
| 855 | + "name": "raw_path", | |
| 856 | + "type": "text", | |
| 857 | + "primaryKey": false, | |
| 858 | + "notNull": false | |
| 859 | + }, | |
| 860 | + "status": { | |
| 861 | + "name": "status", | |
| 862 | + "type": "text", | |
| 863 | + "primaryKey": false, | |
| 864 | + "notNull": true, | |
| 865 | + "default": "'active'" | |
| 866 | + }, | |
| 867 | + "first_seen_run": { | |
| 868 | + "name": "first_seen_run", | |
| 869 | + "type": "text", | |
| 870 | + "primaryKey": false, | |
| 871 | + "notNull": false | |
| 872 | + }, | |
| 873 | + "last_seen_run": { | |
| 874 | + "name": "last_seen_run", | |
| 875 | + "type": "text", | |
| 876 | + "primaryKey": false, | |
| 877 | + "notNull": false | |
| 878 | + }, | |
| 879 | + "retrieved_at": { | |
| 880 | + "name": "retrieved_at", | |
| 881 | + "type": "timestamp with time zone", | |
| 882 | + "primaryKey": false, | |
| 883 | + "notNull": true, | |
| 884 | + "default": "now()" | |
| 885 | + }, | |
| 886 | + "source_updated_at": { | |
| 887 | + "name": "source_updated_at", | |
| 888 | + "type": "timestamp with time zone", | |
| 889 | + "primaryKey": false, | |
| 890 | + "notNull": false | |
| 891 | + }, | |
| 892 | + "canonical_type": { | |
| 893 | + "name": "canonical_type", | |
| 894 | + "type": "text", | |
| 895 | + "primaryKey": false, | |
| 896 | + "notNull": false | |
| 897 | + }, | |
| 898 | + "canonical_id": { | |
| 899 | + "name": "canonical_id", | |
| 900 | + "type": "text", | |
| 901 | + "primaryKey": false, | |
| 902 | + "notNull": false | |
| 903 | + }, | |
| 904 | + "created_at": { | |
| 905 | + "name": "created_at", | |
| 906 | + "type": "timestamp with time zone", | |
| 907 | + "primaryKey": false, | |
| 908 | + "notNull": true, | |
| 909 | + "default": "now()" | |
| 910 | + }, | |
| 911 | + "updated_at": { | |
| 912 | + "name": "updated_at", | |
| 913 | + "type": "timestamp with time zone", | |
| 914 | + "primaryKey": false, | |
| 915 | + "notNull": true, | |
| 916 | + "default": "now()" | |
| 917 | + } | |
| 918 | + }, | |
| 919 | + "indexes": { | |
| 920 | + "source_records_uq": { | |
| 921 | + "name": "source_records_uq", | |
| 922 | + "columns": [ | |
| 923 | + { | |
| 924 | + "expression": "source_id", | |
| 925 | + "isExpression": false, | |
| 926 | + "asc": true, | |
| 927 | + "nulls": "last" | |
| 928 | + }, | |
| 929 | + { | |
| 930 | + "expression": "entity_kind", | |
| 931 | + "isExpression": false, | |
| 932 | + "asc": true, | |
| 933 | + "nulls": "last" | |
| 934 | + }, | |
| 935 | + { | |
| 936 | + "expression": "source_record_id", | |
| 937 | + "isExpression": false, | |
| 938 | + "asc": true, | |
| 939 | + "nulls": "last" | |
| 940 | + } | |
| 941 | + ], | |
| 942 | + "isUnique": true, | |
| 943 | + "concurrently": false, | |
| 944 | + "method": "btree", | |
| 945 | + "with": {} | |
| 946 | + }, | |
| 947 | + "source_records_canonical_idx": { | |
| 948 | + "name": "source_records_canonical_idx", | |
| 949 | + "columns": [ | |
| 950 | + { | |
| 951 | + "expression": "canonical_type", | |
| 952 | + "isExpression": false, | |
| 953 | + "asc": true, | |
| 954 | + "nulls": "last" | |
| 955 | + }, | |
| 956 | + { | |
| 957 | + "expression": "canonical_id", | |
| 958 | + "isExpression": false, | |
| 959 | + "asc": true, | |
| 960 | + "nulls": "last" | |
| 961 | + } | |
| 962 | + ], | |
| 963 | + "isUnique": false, | |
| 964 | + "concurrently": false, | |
| 965 | + "method": "btree", | |
| 966 | + "with": {} | |
| 967 | + } | |
| 968 | + }, | |
| 969 | + "foreignKeys": {}, | |
| 970 | + "compositePrimaryKeys": {}, | |
| 971 | + "uniqueConstraints": {}, | |
| 972 | + "policies": {}, | |
| 973 | + "checkConstraints": {}, | |
| 974 | + "isRLSEnabled": false | |
| 975 | + }, | |
| 976 | + "public.sources": { | |
| 977 | + "name": "sources", | |
| 978 | + "schema": "", | |
| 979 | + "columns": { | |
| 980 | + "id": { | |
| 981 | + "name": "id", | |
| 982 | + "type": "varchar(32)", | |
| 983 | + "primaryKey": true, | |
| 984 | + "notNull": true | |
| 985 | + }, | |
| 986 | + "slug": { | |
| 987 | + "name": "slug", | |
| 988 | + "type": "text", | |
| 989 | + "primaryKey": false, | |
| 990 | + "notNull": true | |
| 991 | + }, | |
| 992 | + "name": { | |
| 993 | + "name": "name", | |
| 994 | + "type": "text", | |
| 995 | + "primaryKey": false, | |
| 996 | + "notNull": true | |
| 997 | + }, | |
| 998 | + "organization": { | |
| 999 | + "name": "organization", | |
| 1000 | + "type": "text", | |
| 1001 | + "primaryKey": false, | |
| 1002 | + "notNull": false | |
| 1003 | + }, | |
| 1004 | + "category": { | |
| 1005 | + "name": "category", | |
| 1006 | + "type": "text", | |
| 1007 | + "primaryKey": false, | |
| 1008 | + "notNull": true | |
| 1009 | + }, | |
| 1010 | + "description": { | |
| 1011 | + "name": "description", | |
| 1012 | + "type": "text", | |
| 1013 | + "primaryKey": false, | |
| 1014 | + "notNull": false | |
| 1015 | + }, | |
| 1016 | + "homepage": { | |
| 1017 | + "name": "homepage", | |
| 1018 | + "type": "text", | |
| 1019 | + "primaryKey": false, | |
| 1020 | + "notNull": false | |
| 1021 | + }, | |
| 1022 | + "docs_url": { | |
| 1023 | + "name": "docs_url", | |
| 1024 | + "type": "text", | |
| 1025 | + "primaryKey": false, | |
| 1026 | + "notNull": false | |
| 1027 | + }, | |
| 1028 | + "terms_url": { | |
| 1029 | + "name": "terms_url", | |
| 1030 | + "type": "text", | |
| 1031 | + "primaryKey": false, | |
| 1032 | + "notNull": false | |
| 1033 | + }, | |
| 1034 | + "access_type": { | |
| 1035 | + "name": "access_type", | |
| 1036 | + "type": "text", | |
| 1037 | + "primaryKey": false, | |
| 1038 | + "notNull": true | |
| 1039 | + }, | |
| 1040 | + "access_auth": { | |
| 1041 | + "name": "access_auth", | |
| 1042 | + "type": "text", | |
| 1043 | + "primaryKey": false, | |
| 1044 | + "notNull": true | |
| 1045 | + }, | |
| 1046 | + "license": { | |
| 1047 | + "name": "license", | |
| 1048 | + "type": "text", | |
| 1049 | + "primaryKey": false, | |
| 1050 | + "notNull": false | |
| 1051 | + }, | |
| 1052 | + "license_status": { | |
| 1053 | + "name": "license_status", | |
| 1054 | + "type": "text", | |
| 1055 | + "primaryKey": false, | |
| 1056 | + "notNull": true, | |
| 1057 | + "default": "'review'" | |
| 1058 | + }, | |
| 1059 | + "commercial_use": { | |
| 1060 | + "name": "commercial_use", | |
| 1061 | + "type": "text", | |
| 1062 | + "primaryKey": false, | |
| 1063 | + "notNull": true, | |
| 1064 | + "default": "'unknown'" | |
| 1065 | + }, | |
| 1066 | + "redistribution": { | |
| 1067 | + "name": "redistribution", | |
| 1068 | + "type": "text", | |
| 1069 | + "primaryKey": false, | |
| 1070 | + "notNull": true, | |
| 1071 | + "default": "'unknown'" | |
| 1072 | + }, | |
| 1073 | + "attribution": { | |
| 1074 | + "name": "attribution", | |
| 1075 | + "type": "text", | |
| 1076 | + "primaryKey": false, | |
| 1077 | + "notNull": false | |
| 1078 | + }, | |
| 1079 | + "license_reviewed_at": { | |
| 1080 | + "name": "license_reviewed_at", | |
| 1081 | + "type": "timestamp with time zone", | |
| 1082 | + "primaryKey": false, | |
| 1083 | + "notNull": false | |
| 1084 | + }, | |
| 1085 | + "approved_for_production": { | |
| 1086 | + "name": "approved_for_production", | |
| 1087 | + "type": "boolean", | |
| 1088 | + "primaryKey": false, | |
| 1089 | + "notNull": true, | |
| 1090 | + "default": false | |
| 1091 | + }, | |
| 1092 | + "update_frequency": { | |
| 1093 | + "name": "update_frequency", | |
| 1094 | + "type": "text", | |
| 1095 | + "primaryKey": false, | |
| 1096 | + "notNull": false | |
| 1097 | + }, | |
| 1098 | + "supports_incremental": { | |
| 1099 | + "name": "supports_incremental", | |
| 1100 | + "type": "boolean", | |
| 1101 | + "primaryKey": false, | |
| 1102 | + "notNull": true, | |
| 1103 | + "default": false | |
| 1104 | + }, | |
| 1105 | + "entities": { | |
| 1106 | + "name": "entities", | |
| 1107 | + "type": "text[]", | |
| 1108 | + "primaryKey": false, | |
| 1109 | + "notNull": true, | |
| 1110 | + "default": "'{}'" | |
| 1111 | + }, | |
| 1112 | + "metrics": { | |
| 1113 | + "name": "metrics", | |
| 1114 | + "type": "text[]", | |
| 1115 | + "primaryKey": false, | |
| 1116 | + "notNull": true, | |
| 1117 | + "default": "'{}'" | |
| 1118 | + }, | |
| 1119 | + "rate_limit": { | |
| 1120 | + "name": "rate_limit", | |
| 1121 | + "type": "text", | |
| 1122 | + "primaryKey": false, | |
| 1123 | + "notNull": false | |
| 1124 | + }, | |
| 1125 | + "status": { | |
| 1126 | + "name": "status", | |
| 1127 | + "type": "text", | |
| 1128 | + "primaryKey": false, | |
| 1129 | + "notNull": true, | |
| 1130 | + "default": "'planned'" | |
| 1131 | + }, | |
| 1132 | + "tier": { | |
| 1133 | + "name": "tier", | |
| 1134 | + "type": "integer", | |
| 1135 | + "primaryKey": false, | |
| 1136 | + "notNull": true, | |
| 1137 | + "default": 0 | |
| 1138 | + }, | |
| 1139 | + "manifest": { | |
| 1140 | + "name": "manifest", | |
| 1141 | + "type": "jsonb", | |
| 1142 | + "primaryKey": false, | |
| 1143 | + "notNull": true, | |
| 1144 | + "default": "'{}'::jsonb" | |
| 1145 | + }, | |
| 1146 | + "created_at": { | |
| 1147 | + "name": "created_at", | |
| 1148 | + "type": "timestamp with time zone", | |
| 1149 | + "primaryKey": false, | |
| 1150 | + "notNull": true, | |
| 1151 | + "default": "now()" | |
| 1152 | + }, | |
| 1153 | + "updated_at": { | |
| 1154 | + "name": "updated_at", | |
| 1155 | + "type": "timestamp with time zone", | |
| 1156 | + "primaryKey": false, | |
| 1157 | + "notNull": true, | |
| 1158 | + "default": "now()" | |
| 1159 | + } | |
| 1160 | + }, | |
| 1161 | + "indexes": { | |
| 1162 | + "sources_slug_uq": { | |
| 1163 | + "name": "sources_slug_uq", | |
| 1164 | + "columns": [ | |
| 1165 | + { | |
| 1166 | + "expression": "slug", | |
| 1167 | + "isExpression": false, | |
| 1168 | + "asc": true, | |
| 1169 | + "nulls": "last" | |
| 1170 | + } | |
| 1171 | + ], | |
| 1172 | + "isUnique": true, | |
| 1173 | + "concurrently": false, | |
| 1174 | + "method": "btree", | |
| 1175 | + "with": {} | |
| 1176 | + } | |
| 1177 | + }, | |
| 1178 | + "foreignKeys": {}, | |
| 1179 | + "compositePrimaryKeys": {}, | |
| 1180 | + "uniqueConstraints": {}, | |
| 1181 | + "policies": {}, | |
| 1182 | + "checkConstraints": {}, | |
| 1183 | + "isRLSEnabled": false | |
| 1184 | + }, | |
| 1185 | + "public.unresolved_labels": { | |
| 1186 | + "name": "unresolved_labels", | |
| 1187 | + "schema": "", | |
| 1188 | + "columns": { | |
| 1189 | + "id": { | |
| 1190 | + "name": "id", | |
| 1191 | + "type": "bigserial", | |
| 1192 | + "primaryKey": true, | |
| 1193 | + "notNull": true | |
| 1194 | + }, | |
| 1195 | + "source_id": { | |
| 1196 | + "name": "source_id", | |
| 1197 | + "type": "varchar(32)", | |
| 1198 | + "primaryKey": false, | |
| 1199 | + "notNull": true | |
| 1200 | + }, | |
| 1201 | + "entity_kind": { | |
| 1202 | + "name": "entity_kind", | |
| 1203 | + "type": "text", | |
| 1204 | + "primaryKey": false, | |
| 1205 | + "notNull": true | |
| 1206 | + }, | |
| 1207 | + "source_text": { | |
| 1208 | + "name": "source_text", | |
| 1209 | + "type": "text", | |
| 1210 | + "primaryKey": false, | |
| 1211 | + "notNull": true | |
| 1212 | + }, | |
| 1213 | + "normalized": { | |
| 1214 | + "name": "normalized", | |
| 1215 | + "type": "text", | |
| 1216 | + "primaryKey": false, | |
| 1217 | + "notNull": true | |
| 1218 | + }, | |
| 1219 | + "context": { | |
| 1220 | + "name": "context", | |
| 1221 | + "type": "jsonb", | |
| 1222 | + "primaryKey": false, | |
| 1223 | + "notNull": true, | |
| 1224 | + "default": "'{}'::jsonb" | |
| 1225 | + }, | |
| 1226 | + "count": { | |
| 1227 | + "name": "count", | |
| 1228 | + "type": "integer", | |
| 1229 | + "primaryKey": false, | |
| 1230 | + "notNull": true, | |
| 1231 | + "default": 1 | |
| 1232 | + }, | |
| 1233 | + "status": { | |
| 1234 | + "name": "status", | |
| 1235 | + "type": "text", | |
| 1236 | + "primaryKey": false, | |
| 1237 | + "notNull": true, | |
| 1238 | + "default": "'open'" | |
| 1239 | + }, | |
| 1240 | + "suggested_id": { | |
| 1241 | + "name": "suggested_id", | |
| 1242 | + "type": "text", | |
| 1243 | + "primaryKey": false, | |
| 1244 | + "notNull": false | |
| 1245 | + }, | |
| 1246 | + "suggested_match_type": { | |
| 1247 | + "name": "suggested_match_type", | |
| 1248 | + "type": "text", | |
| 1249 | + "primaryKey": false, | |
| 1250 | + "notNull": false | |
| 1251 | + }, | |
| 1252 | + "suggested_score": { | |
| 1253 | + "name": "suggested_score", | |
| 1254 | + "type": "real", | |
| 1255 | + "primaryKey": false, | |
| 1256 | + "notNull": false | |
| 1257 | + }, | |
| 1258 | + "resolved_id": { | |
| 1259 | + "name": "resolved_id", | |
| 1260 | + "type": "text", | |
| 1261 | + "primaryKey": false, | |
| 1262 | + "notNull": false | |
| 1263 | + }, | |
| 1264 | + "resolved_by": { | |
| 1265 | + "name": "resolved_by", | |
| 1266 | + "type": "text", | |
| 1267 | + "primaryKey": false, | |
| 1268 | + "notNull": false | |
| 1269 | + }, | |
| 1270 | + "created_at": { | |
| 1271 | + "name": "created_at", | |
| 1272 | + "type": "timestamp with time zone", | |
| 1273 | + "primaryKey": false, | |
| 1274 | + "notNull": true, | |
| 1275 | + "default": "now()" | |
| 1276 | + }, | |
| 1277 | + "updated_at": { | |
| 1278 | + "name": "updated_at", | |
| 1279 | + "type": "timestamp with time zone", | |
| 1280 | + "primaryKey": false, | |
| 1281 | + "notNull": true, | |
| 1282 | + "default": "now()" | |
| 1283 | + } | |
| 1284 | + }, | |
| 1285 | + "indexes": { | |
| 1286 | + "unresolved_labels_uq": { | |
| 1287 | + "name": "unresolved_labels_uq", | |
| 1288 | + "columns": [ | |
| 1289 | + { | |
| 1290 | + "expression": "source_id", | |
| 1291 | + "isExpression": false, | |
| 1292 | + "asc": true, | |
| 1293 | + "nulls": "last" | |
| 1294 | + }, | |
| 1295 | + { | |
| 1296 | + "expression": "entity_kind", | |
| 1297 | + "isExpression": false, | |
| 1298 | + "asc": true, | |
| 1299 | + "nulls": "last" | |
| 1300 | + }, | |
| 1301 | + { | |
| 1302 | + "expression": "normalized", | |
| 1303 | + "isExpression": false, | |
| 1304 | + "asc": true, | |
| 1305 | + "nulls": "last" | |
| 1306 | + } | |
| 1307 | + ], | |
| 1308 | + "isUnique": true, | |
| 1309 | + "concurrently": false, | |
| 1310 | + "method": "btree", | |
| 1311 | + "with": {} | |
| 1312 | + }, | |
| 1313 | + "unresolved_labels_count_idx": { | |
| 1314 | + "name": "unresolved_labels_count_idx", | |
| 1315 | + "columns": [ | |
| 1316 | + { | |
| 1317 | + "expression": "status", | |
| 1318 | + "isExpression": false, | |
| 1319 | + "asc": true, | |
| 1320 | + "nulls": "last" | |
| 1321 | + }, | |
| 1322 | + { | |
| 1323 | + "expression": "count", | |
| 1324 | + "isExpression": false, | |
| 1325 | + "asc": true, | |
| 1326 | + "nulls": "last" | |
| 1327 | + } | |
| 1328 | + ], | |
| 1329 | + "isUnique": false, | |
| 1330 | + "concurrently": false, | |
| 1331 | + "method": "btree", | |
| 1332 | + "with": {} | |
| 1333 | + } | |
| 1334 | + }, | |
| 1335 | + "foreignKeys": {}, | |
| 1336 | + "compositePrimaryKeys": {}, | |
| 1337 | + "uniqueConstraints": {}, | |
| 1338 | + "policies": {}, | |
| 1339 | + "checkConstraints": {}, | |
| 1340 | + "isRLSEnabled": false | |
| 1341 | + }, | |
| 1342 | + "public.anatomical_sites": { | |
| 1343 | + "name": "anatomical_sites", | |
| 1344 | + "schema": "", | |
| 1345 | + "columns": { | |
| 1346 | + "id": { | |
| 1347 | + "name": "id", | |
| 1348 | + "type": "varchar(32)", | |
| 1349 | + "primaryKey": true, | |
| 1350 | + "notNull": true | |
| 1351 | + }, | |
| 1352 | + "name": { | |
| 1353 | + "name": "name", | |
| 1354 | + "type": "text", | |
| 1355 | + "primaryKey": false, | |
| 1356 | + "notNull": true | |
| 1357 | + }, | |
| 1358 | + "slug": { | |
| 1359 | + "name": "slug", | |
| 1360 | + "type": "text", | |
| 1361 | + "primaryKey": false, | |
| 1362 | + "notNull": true | |
| 1363 | + }, | |
| 1364 | + "ncit_code": { | |
| 1365 | + "name": "ncit_code", | |
| 1366 | + "type": "text", | |
| 1367 | + "primaryKey": false, | |
| 1368 | + "notNull": false | |
| 1369 | + }, | |
| 1370 | + "uberon_id": { | |
| 1371 | + "name": "uberon_id", | |
| 1372 | + "type": "text", | |
| 1373 | + "primaryKey": false, | |
| 1374 | + "notNull": false | |
| 1375 | + }, | |
| 1376 | + "parent_id": { | |
| 1377 | + "name": "parent_id", | |
| 1378 | + "type": "varchar(32)", | |
| 1379 | + "primaryKey": false, | |
| 1380 | + "notNull": false | |
| 1381 | + }, | |
| 1382 | + "system": { | |
| 1383 | + "name": "system", | |
| 1384 | + "type": "text", | |
| 1385 | + "primaryKey": false, | |
| 1386 | + "notNull": false | |
| 1387 | + } | |
| 1388 | + }, | |
| 1389 | + "indexes": { | |
| 1390 | + "anatomical_sites_slug_uq": { | |
| 1391 | + "name": "anatomical_sites_slug_uq", | |
| 1392 | + "columns": [ | |
| 1393 | + { | |
| 1394 | + "expression": "slug", | |
| 1395 | + "isExpression": false, | |
| 1396 | + "asc": true, | |
| 1397 | + "nulls": "last" | |
| 1398 | + } | |
| 1399 | + ], | |
| 1400 | + "isUnique": true, | |
| 1401 | + "concurrently": false, | |
| 1402 | + "method": "btree", | |
| 1403 | + "with": {} | |
| 1404 | + } | |
| 1405 | + }, | |
| 1406 | + "foreignKeys": {}, | |
| 1407 | + "compositePrimaryKeys": {}, | |
| 1408 | + "uniqueConstraints": {}, | |
| 1409 | + "policies": {}, | |
| 1410 | + "checkConstraints": {}, | |
| 1411 | + "isRLSEnabled": false | |
| 1412 | + }, | |
| 1413 | + "public.cancer_aliases": { | |
| 1414 | + "name": "cancer_aliases", | |
| 1415 | + "schema": "", | |
| 1416 | + "columns": { | |
| 1417 | + "id": { | |
| 1418 | + "name": "id", | |
| 1419 | + "type": "bigserial", | |
| 1420 | + "primaryKey": true, | |
| 1421 | + "notNull": true | |
| 1422 | + }, | |
| 1423 | + "cancer_id": { | |
| 1424 | + "name": "cancer_id", | |
| 1425 | + "type": "varchar(32)", | |
| 1426 | + "primaryKey": false, | |
| 1427 | + "notNull": true | |
| 1428 | + }, | |
| 1429 | + "alias": { | |
| 1430 | + "name": "alias", | |
| 1431 | + "type": "text", | |
| 1432 | + "primaryKey": false, | |
| 1433 | + "notNull": true | |
| 1434 | + }, | |
| 1435 | + "normalized": { | |
| 1436 | + "name": "normalized", | |
| 1437 | + "type": "text", | |
| 1438 | + "primaryKey": false, | |
| 1439 | + "notNull": true | |
| 1440 | + }, | |
| 1441 | + "alias_type": { | |
| 1442 | + "name": "alias_type", | |
| 1443 | + "type": "text", | |
| 1444 | + "primaryKey": false, | |
| 1445 | + "notNull": true, | |
| 1446 | + "default": "'synonym'" | |
| 1447 | + }, | |
| 1448 | + "source_id": { | |
| 1449 | + "name": "source_id", | |
| 1450 | + "type": "varchar(32)", | |
| 1451 | + "primaryKey": false, | |
| 1452 | + "notNull": false | |
| 1453 | + }, | |
| 1454 | + "source_terminology": { | |
| 1455 | + "name": "source_terminology", | |
| 1456 | + "type": "text", | |
| 1457 | + "primaryKey": false, | |
| 1458 | + "notNull": false | |
| 1459 | + }, | |
| 1460 | + "language": { | |
| 1461 | + "name": "language", | |
| 1462 | + "type": "text", | |
| 1463 | + "primaryKey": false, | |
| 1464 | + "notNull": true, | |
| 1465 | + "default": "'en'" | |
| 1466 | + } | |
| 1467 | + }, | |
| 1468 | + "indexes": { | |
| 1469 | + "cancer_aliases_uq": { | |
| 1470 | + "name": "cancer_aliases_uq", | |
| 1471 | + "columns": [ | |
| 1472 | + { | |
| 1473 | + "expression": "cancer_id", | |
| 1474 | + "isExpression": false, | |
| 1475 | + "asc": true, | |
| 1476 | + "nulls": "last" | |
| 1477 | + }, | |
| 1478 | + { | |
| 1479 | + "expression": "normalized", | |
| 1480 | + "isExpression": false, | |
| 1481 | + "asc": true, | |
| 1482 | + "nulls": "last" | |
| 1483 | + }, | |
| 1484 | + { | |
| 1485 | + "expression": "alias_type", | |
| 1486 | + "isExpression": false, | |
| 1487 | + "asc": true, | |
| 1488 | + "nulls": "last" | |
| 1489 | + } | |
| 1490 | + ], | |
| 1491 | + "isUnique": true, | |
| 1492 | + "concurrently": false, | |
| 1493 | + "method": "btree", | |
| 1494 | + "with": {} | |
| 1495 | + }, | |
| 1496 | + "cancer_aliases_norm_idx": { | |
| 1497 | + "name": "cancer_aliases_norm_idx", | |
| 1498 | + "columns": [ | |
| 1499 | + { | |
| 1500 | + "expression": "normalized", | |
| 1501 | + "isExpression": false, | |
| 1502 | + "asc": true, | |
| 1503 | + "nulls": "last" | |
| 1504 | + } | |
| 1505 | + ], | |
| 1506 | + "isUnique": false, | |
| 1507 | + "concurrently": false, | |
| 1508 | + "method": "btree", | |
| 1509 | + "with": {} | |
| 1510 | + } | |
| 1511 | + }, | |
| 1512 | + "foreignKeys": {}, | |
| 1513 | + "compositePrimaryKeys": {}, | |
| 1514 | + "uniqueConstraints": {}, | |
| 1515 | + "policies": {}, | |
| 1516 | + "checkConstraints": {}, | |
| 1517 | + "isRLSEnabled": false | |
| 1518 | + }, | |
| 1519 | + "public.cancer_anatomy": { | |
| 1520 | + "name": "cancer_anatomy", | |
| 1521 | + "schema": "", | |
| 1522 | + "columns": { | |
| 1523 | + "id": { | |
| 1524 | + "name": "id", | |
| 1525 | + "type": "bigserial", | |
| 1526 | + "primaryKey": true, | |
| 1527 | + "notNull": true | |
| 1528 | + }, | |
| 1529 | + "cancer_id": { | |
| 1530 | + "name": "cancer_id", | |
| 1531 | + "type": "varchar(32)", | |
| 1532 | + "primaryKey": false, | |
| 1533 | + "notNull": true | |
| 1534 | + }, | |
| 1535 | + "site_id": { | |
| 1536 | + "name": "site_id", | |
| 1537 | + "type": "varchar(32)", | |
| 1538 | + "primaryKey": false, | |
| 1539 | + "notNull": true | |
| 1540 | + }, | |
| 1541 | + "relation": { | |
| 1542 | + "name": "relation", | |
| 1543 | + "type": "text", | |
| 1544 | + "primaryKey": false, | |
| 1545 | + "notNull": true, | |
| 1546 | + "default": "'primary'" | |
| 1547 | + }, | |
| 1548 | + "source_id": { | |
| 1549 | + "name": "source_id", | |
| 1550 | + "type": "varchar(32)", | |
| 1551 | + "primaryKey": false, | |
| 1552 | + "notNull": false | |
| 1553 | + } | |
| 1554 | + }, | |
| 1555 | + "indexes": { | |
| 1556 | + "cancer_anatomy_uq": { | |
| 1557 | + "name": "cancer_anatomy_uq", | |
| 1558 | + "columns": [ | |
| 1559 | + { | |
| 1560 | + "expression": "cancer_id", | |
| 1561 | + "isExpression": false, | |
| 1562 | + "asc": true, | |
| 1563 | + "nulls": "last" | |
| 1564 | + }, | |
| 1565 | + { | |
| 1566 | + "expression": "site_id", | |
| 1567 | + "isExpression": false, | |
| 1568 | + "asc": true, | |
| 1569 | + "nulls": "last" | |
| 1570 | + }, | |
| 1571 | + { | |
| 1572 | + "expression": "relation", | |
| 1573 | + "isExpression": false, | |
| 1574 | + "asc": true, | |
| 1575 | + "nulls": "last" | |
| 1576 | + } | |
| 1577 | + ], | |
| 1578 | + "isUnique": true, | |
| 1579 | + "concurrently": false, | |
| 1580 | + "method": "btree", | |
| 1581 | + "with": {} | |
| 1582 | + } | |
| 1583 | + }, | |
| 1584 | + "foreignKeys": {}, | |
| 1585 | + "compositePrimaryKeys": {}, | |
| 1586 | + "uniqueConstraints": {}, | |
| 1587 | + "policies": {}, | |
| 1588 | + "checkConstraints": {}, | |
| 1589 | + "isRLSEnabled": false | |
| 1590 | + }, | |
| 1591 | + "public.cancer_codes": { | |
| 1592 | + "name": "cancer_codes", | |
| 1593 | + "schema": "", | |
| 1594 | + "columns": { | |
| 1595 | + "id": { | |
| 1596 | + "name": "id", | |
| 1597 | + "type": "bigserial", | |
| 1598 | + "primaryKey": true, | |
| 1599 | + "notNull": true | |
| 1600 | + }, | |
| 1601 | + "cancer_id": { | |
| 1602 | + "name": "cancer_id", | |
| 1603 | + "type": "varchar(32)", | |
| 1604 | + "primaryKey": false, | |
| 1605 | + "notNull": true | |
| 1606 | + }, | |
| 1607 | + "system": { | |
| 1608 | + "name": "system", | |
| 1609 | + "type": "text", | |
| 1610 | + "primaryKey": false, | |
| 1611 | + "notNull": true | |
| 1612 | + }, | |
| 1613 | + "code": { | |
| 1614 | + "name": "code", | |
| 1615 | + "type": "text", | |
| 1616 | + "primaryKey": false, | |
| 1617 | + "notNull": true | |
| 1618 | + }, | |
| 1619 | + "match_type": { | |
| 1620 | + "name": "match_type", | |
| 1621 | + "type": "text", | |
| 1622 | + "primaryKey": false, | |
| 1623 | + "notNull": true, | |
| 1624 | + "default": "'EXACT_IDENTIFIER'" | |
| 1625 | + }, | |
| 1626 | + "source_id": { | |
| 1627 | + "name": "source_id", | |
| 1628 | + "type": "varchar(32)", | |
| 1629 | + "primaryKey": false, | |
| 1630 | + "notNull": false | |
| 1631 | + }, | |
| 1632 | + "valid_from": { | |
| 1633 | + "name": "valid_from", | |
| 1634 | + "type": "text", | |
| 1635 | + "primaryKey": false, | |
| 1636 | + "notNull": false | |
| 1637 | + }, | |
| 1638 | + "valid_to": { | |
| 1639 | + "name": "valid_to", | |
| 1640 | + "type": "text", | |
| 1641 | + "primaryKey": false, | |
| 1642 | + "notNull": false | |
| 1643 | + } | |
| 1644 | + }, | |
| 1645 | + "indexes": { | |
| 1646 | + "cancer_codes_uq": { | |
| 1647 | + "name": "cancer_codes_uq", | |
| 1648 | + "columns": [ | |
| 1649 | + { | |
| 1650 | + "expression": "cancer_id", | |
| 1651 | + "isExpression": false, | |
| 1652 | + "asc": true, | |
| 1653 | + "nulls": "last" | |
| 1654 | + }, | |
| 1655 | + { | |
| 1656 | + "expression": "system", | |
| 1657 | + "isExpression": false, | |
| 1658 | + "asc": true, | |
| 1659 | + "nulls": "last" | |
| 1660 | + }, | |
| 1661 | + { | |
| 1662 | + "expression": "code", | |
| 1663 | + "isExpression": false, | |
| 1664 | + "asc": true, | |
| 1665 | + "nulls": "last" | |
| 1666 | + } | |
| 1667 | + ], | |
| 1668 | + "isUnique": true, | |
| 1669 | + "concurrently": false, | |
| 1670 | + "method": "btree", | |
| 1671 | + "with": {} | |
| 1672 | + }, | |
| 1673 | + "cancer_codes_lookup_idx": { | |
| 1674 | + "name": "cancer_codes_lookup_idx", | |
| 1675 | + "columns": [ | |
| 1676 | + { | |
| 1677 | + "expression": "system", | |
| 1678 | + "isExpression": false, | |
| 1679 | + "asc": true, | |
| 1680 | + "nulls": "last" | |
| 1681 | + }, | |
| 1682 | + { | |
| 1683 | + "expression": "code", | |
| 1684 | + "isExpression": false, | |
| 1685 | + "asc": true, | |
| 1686 | + "nulls": "last" | |
| 1687 | + } | |
| 1688 | + ], | |
| 1689 | + "isUnique": false, | |
| 1690 | + "concurrently": false, | |
| 1691 | + "method": "btree", | |
| 1692 | + "with": {} | |
| 1693 | + } | |
| 1694 | + }, | |
| 1695 | + "foreignKeys": {}, | |
| 1696 | + "compositePrimaryKeys": {}, | |
| 1697 | + "uniqueConstraints": {}, | |
| 1698 | + "policies": {}, | |
| 1699 | + "checkConstraints": {}, | |
| 1700 | + "isRLSEnabled": false | |
| 1701 | + }, | |
| 1702 | + "public.cancer_hierarchy": { | |
| 1703 | + "name": "cancer_hierarchy", | |
| 1704 | + "schema": "", | |
| 1705 | + "columns": { | |
| 1706 | + "id": { | |
| 1707 | + "name": "id", | |
| 1708 | + "type": "bigserial", | |
| 1709 | + "primaryKey": true, | |
| 1710 | + "notNull": true | |
| 1711 | + }, | |
| 1712 | + "parent_id": { | |
| 1713 | + "name": "parent_id", | |
| 1714 | + "type": "varchar(32)", | |
| 1715 | + "primaryKey": false, | |
| 1716 | + "notNull": true | |
| 1717 | + }, | |
| 1718 | + "child_id": { | |
| 1719 | + "name": "child_id", | |
| 1720 | + "type": "varchar(32)", | |
| 1721 | + "primaryKey": false, | |
| 1722 | + "notNull": true | |
| 1723 | + }, | |
| 1724 | + "hierarchy_type": { | |
| 1725 | + "name": "hierarchy_type", | |
| 1726 | + "type": "text", | |
| 1727 | + "primaryKey": false, | |
| 1728 | + "notNull": true | |
| 1729 | + }, | |
| 1730 | + "source_id": { | |
| 1731 | + "name": "source_id", | |
| 1732 | + "type": "varchar(32)", | |
| 1733 | + "primaryKey": false, | |
| 1734 | + "notNull": false | |
| 1735 | + } | |
| 1736 | + }, | |
| 1737 | + "indexes": { | |
| 1738 | + "cancer_hierarchy_uq": { | |
| 1739 | + "name": "cancer_hierarchy_uq", | |
| 1740 | + "columns": [ | |
| 1741 | + { | |
| 1742 | + "expression": "parent_id", | |
| 1743 | + "isExpression": false, | |
| 1744 | + "asc": true, | |
| 1745 | + "nulls": "last" | |
| 1746 | + }, | |
| 1747 | + { | |
| 1748 | + "expression": "child_id", | |
| 1749 | + "isExpression": false, | |
| 1750 | + "asc": true, | |
| 1751 | + "nulls": "last" | |
| 1752 | + }, | |
| 1753 | + { | |
| 1754 | + "expression": "hierarchy_type", | |
| 1755 | + "isExpression": false, | |
| 1756 | + "asc": true, | |
| 1757 | + "nulls": "last" | |
| 1758 | + } | |
| 1759 | + ], | |
| 1760 | + "isUnique": true, | |
| 1761 | + "concurrently": false, | |
| 1762 | + "method": "btree", | |
| 1763 | + "with": {} | |
| 1764 | + }, | |
| 1765 | + "cancer_hierarchy_child_idx": { | |
| 1766 | + "name": "cancer_hierarchy_child_idx", | |
| 1767 | + "columns": [ | |
| 1768 | + { | |
| 1769 | + "expression": "child_id", | |
| 1770 | + "isExpression": false, | |
| 1771 | + "asc": true, | |
| 1772 | + "nulls": "last" | |
| 1773 | + } | |
| 1774 | + ], | |
| 1775 | + "isUnique": false, | |
| 1776 | + "concurrently": false, | |
| 1777 | + "method": "btree", | |
| 1778 | + "with": {} | |
| 1779 | + } | |
| 1780 | + }, | |
| 1781 | + "foreignKeys": {}, | |
| 1782 | + "compositePrimaryKeys": {}, | |
| 1783 | + "uniqueConstraints": {}, | |
| 1784 | + "policies": {}, | |
| 1785 | + "checkConstraints": {}, | |
| 1786 | + "isRLSEnabled": false | |
| 1787 | + }, | |
| 1788 | + "public.cancers": { | |
| 1789 | + "name": "cancers", | |
| 1790 | + "schema": "", | |
| 1791 | + "columns": { | |
| 1792 | + "id": { | |
| 1793 | + "name": "id", | |
| 1794 | + "type": "varchar(32)", | |
| 1795 | + "primaryKey": true, | |
| 1796 | + "notNull": true | |
| 1797 | + }, | |
| 1798 | + "slug": { | |
| 1799 | + "name": "slug", | |
| 1800 | + "type": "text", | |
| 1801 | + "primaryKey": false, | |
| 1802 | + "notNull": true | |
| 1803 | + }, | |
| 1804 | + "canonical_name": { | |
| 1805 | + "name": "canonical_name", | |
| 1806 | + "type": "text", | |
| 1807 | + "primaryKey": false, | |
| 1808 | + "notNull": true | |
| 1809 | + }, | |
| 1810 | + "short_name": { | |
| 1811 | + "name": "short_name", | |
| 1812 | + "type": "text", | |
| 1813 | + "primaryKey": false, | |
| 1814 | + "notNull": false | |
| 1815 | + }, | |
| 1816 | + "entity_type": { | |
| 1817 | + "name": "entity_type", | |
| 1818 | + "type": "text", | |
| 1819 | + "primaryKey": false, | |
| 1820 | + "notNull": true, | |
| 1821 | + "default": "'cancer'" | |
| 1822 | + }, | |
| 1823 | + "malignant": { | |
| 1824 | + "name": "malignant", | |
| 1825 | + "type": "boolean", | |
| 1826 | + "primaryKey": false, | |
| 1827 | + "notNull": true, | |
| 1828 | + "default": true | |
| 1829 | + }, | |
| 1830 | + "solid_tumor": { | |
| 1831 | + "name": "solid_tumor", | |
| 1832 | + "type": "boolean", | |
| 1833 | + "primaryKey": false, | |
| 1834 | + "notNull": true, | |
| 1835 | + "default": true | |
| 1836 | + }, | |
| 1837 | + "hematologic": { | |
| 1838 | + "name": "hematologic", | |
| 1839 | + "type": "boolean", | |
| 1840 | + "primaryKey": false, | |
| 1841 | + "notNull": true, | |
| 1842 | + "default": false | |
| 1843 | + }, | |
| 1844 | + "pediatric_relevant": { | |
| 1845 | + "name": "pediatric_relevant", | |
| 1846 | + "type": "boolean", | |
| 1847 | + "primaryKey": false, | |
| 1848 | + "notNull": true, | |
| 1849 | + "default": false | |
| 1850 | + }, | |
| 1851 | + "rare_cancer": { | |
| 1852 | + "name": "rare_cancer", | |
| 1853 | + "type": "boolean", | |
| 1854 | + "primaryKey": false, | |
| 1855 | + "notNull": false | |
| 1856 | + }, | |
| 1857 | + "top_level": { | |
| 1858 | + "name": "top_level", | |
| 1859 | + "type": "boolean", | |
| 1860 | + "primaryKey": false, | |
| 1861 | + "notNull": true, | |
| 1862 | + "default": false | |
| 1863 | + }, | |
| 1864 | + "description": { | |
| 1865 | + "name": "description", | |
| 1866 | + "type": "text", | |
| 1867 | + "primaryKey": false, | |
| 1868 | + "notNull": false | |
| 1869 | + }, | |
| 1870 | + "description_provenance_id": { | |
| 1871 | + "name": "description_provenance_id", | |
| 1872 | + "type": "integer", | |
| 1873 | + "primaryKey": false, | |
| 1874 | + "notNull": false | |
| 1875 | + }, | |
| 1876 | + "primary_ncit_code": { | |
| 1877 | + "name": "primary_ncit_code", | |
| 1878 | + "type": "text", | |
| 1879 | + "primaryKey": false, | |
| 1880 | + "notNull": false | |
| 1881 | + }, | |
| 1882 | + "primary_oncotree_code": { | |
| 1883 | + "name": "primary_oncotree_code", | |
| 1884 | + "type": "text", | |
| 1885 | + "primaryKey": false, | |
| 1886 | + "notNull": false | |
| 1887 | + }, | |
| 1888 | + "depth": { | |
| 1889 | + "name": "depth", | |
| 1890 | + "type": "integer", | |
| 1891 | + "primaryKey": false, | |
| 1892 | + "notNull": true, | |
| 1893 | + "default": 0 | |
| 1894 | + }, | |
| 1895 | + "status": { | |
| 1896 | + "name": "status", | |
| 1897 | + "type": "text", | |
| 1898 | + "primaryKey": false, | |
| 1899 | + "notNull": true, | |
| 1900 | + "default": "'active'" | |
| 1901 | + }, | |
| 1902 | + "merged_into": { | |
| 1903 | + "name": "merged_into", | |
| 1904 | + "type": "varchar(32)", | |
| 1905 | + "primaryKey": false, | |
| 1906 | + "notNull": false | |
| 1907 | + }, | |
| 1908 | + "deprecated_reason": { | |
| 1909 | + "name": "deprecated_reason", | |
| 1910 | + "type": "text", | |
| 1911 | + "primaryKey": false, | |
| 1912 | + "notNull": false | |
| 1913 | + }, | |
| 1914 | + "classification_version": { | |
| 1915 | + "name": "classification_version", | |
| 1916 | + "type": "text", | |
| 1917 | + "primaryKey": false, | |
| 1918 | + "notNull": false | |
| 1919 | + }, | |
| 1920 | + "semantic_types": { | |
| 1921 | + "name": "semantic_types", | |
| 1922 | + "type": "text[]", | |
| 1923 | + "primaryKey": false, | |
| 1924 | + "notNull": true, | |
| 1925 | + "default": "'{}'" | |
| 1926 | + }, | |
| 1927 | + "created_at": { | |
| 1928 | + "name": "created_at", | |
| 1929 | + "type": "timestamp with time zone", | |
| 1930 | + "primaryKey": false, | |
| 1931 | + "notNull": true, | |
| 1932 | + "default": "now()" | |
| 1933 | + }, | |
| 1934 | + "updated_at": { | |
| 1935 | + "name": "updated_at", | |
| 1936 | + "type": "timestamp with time zone", | |
| 1937 | + "primaryKey": false, | |
| 1938 | + "notNull": true, | |
| 1939 | + "default": "now()" | |
| 1940 | + } | |
| 1941 | + }, | |
| 1942 | + "indexes": { | |
| 1943 | + "cancers_slug_uq": { | |
| 1944 | + "name": "cancers_slug_uq", | |
| 1945 | + "columns": [ | |
| 1946 | + { | |
| 1947 | + "expression": "slug", | |
| 1948 | + "isExpression": false, | |
| 1949 | + "asc": true, | |
| 1950 | + "nulls": "last" | |
| 1951 | + } | |
| 1952 | + ], | |
| 1953 | + "isUnique": true, | |
| 1954 | + "concurrently": false, | |
| 1955 | + "method": "btree", | |
| 1956 | + "with": {} | |
| 1957 | + }, | |
| 1958 | + "cancers_ncit_uq": { | |
| 1959 | + "name": "cancers_ncit_uq", | |
| 1960 | + "columns": [ | |
| 1961 | + { | |
| 1962 | + "expression": "primary_ncit_code", | |
| 1963 | + "isExpression": false, | |
| 1964 | + "asc": true, | |
| 1965 | + "nulls": "last" | |
| 1966 | + } | |
| 1967 | + ], | |
| 1968 | + "isUnique": true, | |
| 1969 | + "concurrently": false, | |
| 1970 | + "method": "btree", | |
| 1971 | + "with": {} | |
| 1972 | + }, | |
| 1973 | + "cancers_name_idx": { | |
| 1974 | + "name": "cancers_name_idx", | |
| 1975 | + "columns": [ | |
| 1976 | + { | |
| 1977 | + "expression": "canonical_name", | |
| 1978 | + "isExpression": false, | |
| 1979 | + "asc": true, | |
| 1980 | + "nulls": "last" | |
| 1981 | + } | |
| 1982 | + ], | |
| 1983 | + "isUnique": false, | |
| 1984 | + "concurrently": false, | |
| 1985 | + "method": "btree", | |
| 1986 | + "with": {} | |
| 1987 | + }, | |
| 1988 | + "cancers_type_idx": { | |
| 1989 | + "name": "cancers_type_idx", | |
| 1990 | + "columns": [ | |
| 1991 | + { | |
| 1992 | + "expression": "entity_type", | |
| 1993 | + "isExpression": false, | |
| 1994 | + "asc": true, | |
| 1995 | + "nulls": "last" | |
| 1996 | + }, | |
| 1997 | + { | |
| 1998 | + "expression": "malignant", | |
| 1999 | + "isExpression": false, | |
| 2000 | + "asc": true, | |
| 2001 | + "nulls": "last" | |
| 2002 | + }, | |
| 2003 | + { | |
| 2004 | + "expression": "top_level", | |
| 2005 | + "isExpression": false, | |
| 2006 | + "asc": true, | |
| 2007 | + "nulls": "last" | |
| 2008 | + } | |
| 2009 | + ], | |
| 2010 | + "isUnique": false, | |
| 2011 | + "concurrently": false, | |
| 2012 | + "method": "btree", | |
| 2013 | + "with": {} | |
| 2014 | + } | |
| 2015 | + }, | |
| 2016 | + "foreignKeys": {}, | |
| 2017 | + "compositePrimaryKeys": {}, | |
| 2018 | + "uniqueConstraints": {}, | |
| 2019 | + "policies": {}, | |
| 2020 | + "checkConstraints": {}, | |
| 2021 | + "isRLSEnabled": false | |
| 2022 | + }, | |
| 2023 | + "public.cohort_definitions": { | |
| 2024 | + "name": "cohort_definitions", | |
| 2025 | + "schema": "", | |
| 2026 | + "columns": { | |
| 2027 | + "id": { | |
| 2028 | + "name": "id", | |
| 2029 | + "type": "bigserial", | |
| 2030 | + "primaryKey": true, | |
| 2031 | + "notNull": true | |
| 2032 | + }, | |
| 2033 | + "name": { | |
| 2034 | + "name": "name", | |
| 2035 | + "type": "text", | |
| 2036 | + "primaryKey": false, | |
| 2037 | + "notNull": true | |
| 2038 | + }, | |
| 2039 | + "cancer_id": { | |
| 2040 | + "name": "cancer_id", | |
| 2041 | + "type": "varchar(32)", | |
| 2042 | + "primaryKey": false, | |
| 2043 | + "notNull": true | |
| 2044 | + }, | |
| 2045 | + "biomarker_ids": { | |
| 2046 | + "name": "biomarker_ids", | |
| 2047 | + "type": "text[]", | |
| 2048 | + "primaryKey": false, | |
| 2049 | + "notNull": true, | |
| 2050 | + "default": "'{}'" | |
| 2051 | + }, | |
| 2052 | + "variant_ids": { | |
| 2053 | + "name": "variant_ids", | |
| 2054 | + "type": "text[]", | |
| 2055 | + "primaryKey": false, | |
| 2056 | + "notNull": true, | |
| 2057 | + "default": "'{}'" | |
| 2058 | + }, | |
| 2059 | + "stage": { | |
| 2060 | + "name": "stage", | |
| 2061 | + "type": "text", | |
| 2062 | + "primaryKey": false, | |
| 2063 | + "notNull": false | |
| 2064 | + }, | |
| 2065 | + "attributes": { | |
| 2066 | + "name": "attributes", | |
| 2067 | + "type": "jsonb", | |
| 2068 | + "primaryKey": false, | |
| 2069 | + "notNull": true, | |
| 2070 | + "default": "'{}'::jsonb" | |
| 2071 | + }, | |
| 2072 | + "confidence": { | |
| 2073 | + "name": "confidence", | |
| 2074 | + "type": "real", | |
| 2075 | + "primaryKey": false, | |
| 2076 | + "notNull": false | |
| 2077 | + }, | |
| 2078 | + "created_at": { | |
| 2079 | + "name": "created_at", | |
| 2080 | + "type": "timestamp with time zone", | |
| 2081 | + "primaryKey": false, | |
| 2082 | + "notNull": true, | |
| 2083 | + "default": "now()" | |
| 2084 | + } | |
| 2085 | + }, | |
| 2086 | + "indexes": {}, | |
| 2087 | + "foreignKeys": {}, | |
| 2088 | + "compositePrimaryKeys": {}, | |
| 2089 | + "uniqueConstraints": {}, | |
| 2090 | + "policies": {}, | |
| 2091 | + "checkConstraints": {}, | |
| 2092 | + "isRLSEnabled": false | |
| 2093 | + }, | |
| 2094 | + "public.geographies": { | |
| 2095 | + "name": "geographies", | |
| 2096 | + "schema": "", | |
| 2097 | + "columns": { | |
| 2098 | + "id": { | |
| 2099 | + "name": "id", | |
| 2100 | + "type": "varchar(32)", | |
| 2101 | + "primaryKey": true, | |
| 2102 | + "notNull": true | |
| 2103 | + }, | |
| 2104 | + "slug": { | |
| 2105 | + "name": "slug", | |
| 2106 | + "type": "text", | |
| 2107 | + "primaryKey": false, | |
| 2108 | + "notNull": true | |
| 2109 | + }, | |
| 2110 | + "name": { | |
| 2111 | + "name": "name", | |
| 2112 | + "type": "text", | |
| 2113 | + "primaryKey": false, | |
| 2114 | + "notNull": true | |
| 2115 | + }, | |
| 2116 | + "kind": { | |
| 2117 | + "name": "kind", | |
| 2118 | + "type": "text", | |
| 2119 | + "primaryKey": false, | |
| 2120 | + "notNull": true | |
| 2121 | + }, | |
| 2122 | + "iso2": { | |
| 2123 | + "name": "iso2", | |
| 2124 | + "type": "text", | |
| 2125 | + "primaryKey": false, | |
| 2126 | + "notNull": false | |
| 2127 | + }, | |
| 2128 | + "iso3": { | |
| 2129 | + "name": "iso3", | |
| 2130 | + "type": "text", | |
| 2131 | + "primaryKey": false, | |
| 2132 | + "notNull": false | |
| 2133 | + }, | |
| 2134 | + "parent_id": { | |
| 2135 | + "name": "parent_id", | |
| 2136 | + "type": "varchar(32)", | |
| 2137 | + "primaryKey": false, | |
| 2138 | + "notNull": false | |
| 2139 | + }, | |
| 2140 | + "who_region": { | |
| 2141 | + "name": "who_region", | |
| 2142 | + "type": "text", | |
| 2143 | + "primaryKey": false, | |
| 2144 | + "notNull": false | |
| 2145 | + }, | |
| 2146 | + "population": { | |
| 2147 | + "name": "population", | |
| 2148 | + "type": "integer", | |
| 2149 | + "primaryKey": false, | |
| 2150 | + "notNull": false | |
| 2151 | + }, | |
| 2152 | + "population_year": { | |
| 2153 | + "name": "population_year", | |
| 2154 | + "type": "integer", | |
| 2155 | + "primaryKey": false, | |
| 2156 | + "notNull": false | |
| 2157 | + } | |
| 2158 | + }, | |
| 2159 | + "indexes": { | |
| 2160 | + "geographies_slug_uq": { | |
| 2161 | + "name": "geographies_slug_uq", | |
| 2162 | + "columns": [ | |
| 2163 | + { | |
| 2164 | + "expression": "slug", | |
| 2165 | + "isExpression": false, | |
| 2166 | + "asc": true, | |
| 2167 | + "nulls": "last" | |
| 2168 | + } | |
| 2169 | + ], | |
| 2170 | + "isUnique": true, | |
| 2171 | + "concurrently": false, | |
| 2172 | + "method": "btree", | |
| 2173 | + "with": {} | |
| 2174 | + }, | |
| 2175 | + "geographies_iso3_idx": { | |
| 2176 | + "name": "geographies_iso3_idx", | |
| 2177 | + "columns": [ | |
| 2178 | + { | |
| 2179 | + "expression": "iso3", | |
| 2180 | + "isExpression": false, | |
| 2181 | + "asc": true, | |
| 2182 | + "nulls": "last" | |
| 2183 | + } | |
| 2184 | + ], | |
| 2185 | + "isUnique": false, | |
| 2186 | + "concurrently": false, | |
| 2187 | + "method": "btree", | |
| 2188 | + "with": {} | |
| 2189 | + } | |
| 2190 | + }, | |
| 2191 | + "foreignKeys": {}, | |
| 2192 | + "compositePrimaryKeys": {}, | |
| 2193 | + "uniqueConstraints": {}, | |
| 2194 | + "policies": {}, | |
| 2195 | + "checkConstraints": {}, | |
| 2196 | + "isRLSEnabled": false | |
| 2197 | + }, | |
| 2198 | + "public.biomarkers": { | |
| 2199 | + "name": "biomarkers", | |
| 2200 | + "schema": "", | |
| 2201 | + "columns": { | |
| 2202 | + "id": { | |
| 2203 | + "name": "id", | |
| 2204 | + "type": "varchar(32)", | |
| 2205 | + "primaryKey": true, | |
| 2206 | + "notNull": true | |
| 2207 | + }, | |
| 2208 | + "slug": { | |
| 2209 | + "name": "slug", | |
| 2210 | + "type": "text", | |
| 2211 | + "primaryKey": false, | |
| 2212 | + "notNull": true | |
| 2213 | + }, | |
| 2214 | + "name": { | |
| 2215 | + "name": "name", | |
| 2216 | + "type": "text", | |
| 2217 | + "primaryKey": false, | |
| 2218 | + "notNull": true | |
| 2219 | + }, | |
| 2220 | + "kind": { | |
| 2221 | + "name": "kind", | |
| 2222 | + "type": "text", | |
| 2223 | + "primaryKey": false, | |
| 2224 | + "notNull": true | |
| 2225 | + }, | |
| 2226 | + "gene_id": { | |
| 2227 | + "name": "gene_id", | |
| 2228 | + "type": "varchar(32)", | |
| 2229 | + "primaryKey": false, | |
| 2230 | + "notNull": false | |
| 2231 | + }, | |
| 2232 | + "ncit_code": { | |
| 2233 | + "name": "ncit_code", | |
| 2234 | + "type": "text", | |
| 2235 | + "primaryKey": false, | |
| 2236 | + "notNull": false | |
| 2237 | + }, | |
| 2238 | + "description": { | |
| 2239 | + "name": "description", | |
| 2240 | + "type": "text", | |
| 2241 | + "primaryKey": false, | |
| 2242 | + "notNull": false | |
| 2243 | + }, | |
| 2244 | + "measurement": { | |
| 2245 | + "name": "measurement", | |
| 2246 | + "type": "jsonb", | |
| 2247 | + "primaryKey": false, | |
| 2248 | + "notNull": true, | |
| 2249 | + "default": "'{}'::jsonb" | |
| 2250 | + }, | |
| 2251 | + "created_at": { | |
| 2252 | + "name": "created_at", | |
| 2253 | + "type": "timestamp with time zone", | |
| 2254 | + "primaryKey": false, | |
| 2255 | + "notNull": true, | |
| 2256 | + "default": "now()" | |
| 2257 | + }, | |
| 2258 | + "updated_at": { | |
| 2259 | + "name": "updated_at", | |
| 2260 | + "type": "timestamp with time zone", | |
| 2261 | + "primaryKey": false, | |
| 2262 | + "notNull": true, | |
| 2263 | + "default": "now()" | |
| 2264 | + } | |
| 2265 | + }, | |
| 2266 | + "indexes": { | |
| 2267 | + "biomarkers_slug_uq": { | |
| 2268 | + "name": "biomarkers_slug_uq", | |
| 2269 | + "columns": [ | |
| 2270 | + { | |
| 2271 | + "expression": "slug", | |
| 2272 | + "isExpression": false, | |
| 2273 | + "asc": true, | |
| 2274 | + "nulls": "last" | |
| 2275 | + } | |
| 2276 | + ], | |
| 2277 | + "isUnique": true, | |
| 2278 | + "concurrently": false, | |
| 2279 | + "method": "btree", | |
| 2280 | + "with": {} | |
| 2281 | + } | |
| 2282 | + }, | |
| 2283 | + "foreignKeys": {}, | |
| 2284 | + "compositePrimaryKeys": {}, | |
| 2285 | + "uniqueConstraints": {}, | |
| 2286 | + "policies": {}, | |
| 2287 | + "checkConstraints": {}, | |
| 2288 | + "isRLSEnabled": false | |
| 2289 | + }, | |
| 2290 | + "public.cancer_gene_frequencies": { | |
| 2291 | + "name": "cancer_gene_frequencies", | |
| 2292 | + "schema": "", | |
| 2293 | + "columns": { | |
| 2294 | + "id": { | |
| 2295 | + "name": "id", | |
| 2296 | + "type": "bigserial", | |
| 2297 | + "primaryKey": true, | |
| 2298 | + "notNull": true | |
| 2299 | + }, | |
| 2300 | + "cohort_id": { | |
| 2301 | + "name": "cohort_id", | |
| 2302 | + "type": "varchar(32)", | |
| 2303 | + "primaryKey": false, | |
| 2304 | + "notNull": true | |
| 2305 | + }, | |
| 2306 | + "cancer_id": { | |
| 2307 | + "name": "cancer_id", | |
| 2308 | + "type": "varchar(32)", | |
| 2309 | + "primaryKey": false, | |
| 2310 | + "notNull": false | |
| 2311 | + }, | |
| 2312 | + "gene_id": { | |
| 2313 | + "name": "gene_id", | |
| 2314 | + "type": "varchar(32)", | |
| 2315 | + "primaryKey": false, | |
| 2316 | + "notNull": false | |
| 2317 | + }, | |
| 2318 | + "gene_symbol": { | |
| 2319 | + "name": "gene_symbol", | |
| 2320 | + "type": "text", | |
| 2321 | + "primaryKey": false, | |
| 2322 | + "notNull": true | |
| 2323 | + }, | |
| 2324 | + "alteration_type": { | |
| 2325 | + "name": "alteration_type", | |
| 2326 | + "type": "text", | |
| 2327 | + "primaryKey": false, | |
| 2328 | + "notNull": true, | |
| 2329 | + "default": "'ssm'" | |
| 2330 | + }, | |
| 2331 | + "cases_affected": { | |
| 2332 | + "name": "cases_affected", | |
| 2333 | + "type": "integer", | |
| 2334 | + "primaryKey": false, | |
| 2335 | + "notNull": true | |
| 2336 | + }, | |
| 2337 | + "cases_profiled": { | |
| 2338 | + "name": "cases_profiled", | |
| 2339 | + "type": "integer", | |
| 2340 | + "primaryKey": false, | |
| 2341 | + "notNull": true | |
| 2342 | + }, | |
| 2343 | + "frequency": { | |
| 2344 | + "name": "frequency", | |
| 2345 | + "type": "real", | |
| 2346 | + "primaryKey": false, | |
| 2347 | + "notNull": true | |
| 2348 | + }, | |
| 2349 | + "rank": { | |
| 2350 | + "name": "rank", | |
| 2351 | + "type": "integer", | |
| 2352 | + "primaryKey": false, | |
| 2353 | + "notNull": false | |
| 2354 | + }, | |
| 2355 | + "data_release": { | |
| 2356 | + "name": "data_release", | |
| 2357 | + "type": "text", | |
| 2358 | + "primaryKey": false, | |
| 2359 | + "notNull": false | |
| 2360 | + }, | |
| 2361 | + "provenance_id": { | |
| 2362 | + "name": "provenance_id", | |
| 2363 | + "type": "integer", | |
| 2364 | + "primaryKey": false, | |
| 2365 | + "notNull": true | |
| 2366 | + }, | |
| 2367 | + "updated_at": { | |
| 2368 | + "name": "updated_at", | |
| 2369 | + "type": "timestamp with time zone", | |
| 2370 | + "primaryKey": false, | |
| 2371 | + "notNull": true, | |
| 2372 | + "default": "now()" | |
| 2373 | + } | |
| 2374 | + }, | |
| 2375 | + "indexes": { | |
| 2376 | + "cancer_gene_freq_uq": { | |
| 2377 | + "name": "cancer_gene_freq_uq", | |
| 2378 | + "columns": [ | |
| 2379 | + { | |
| 2380 | + "expression": "cohort_id", | |
| 2381 | + "isExpression": false, | |
| 2382 | + "asc": true, | |
| 2383 | + "nulls": "last" | |
| 2384 | + }, | |
| 2385 | + { | |
| 2386 | + "expression": "gene_symbol", | |
| 2387 | + "isExpression": false, | |
| 2388 | + "asc": true, | |
| 2389 | + "nulls": "last" | |
| 2390 | + }, | |
| 2391 | + { | |
| 2392 | + "expression": "alteration_type", | |
| 2393 | + "isExpression": false, | |
| 2394 | + "asc": true, | |
| 2395 | + "nulls": "last" | |
| 2396 | + } | |
| 2397 | + ], | |
| 2398 | + "isUnique": true, | |
| 2399 | + "concurrently": false, | |
| 2400 | + "method": "btree", | |
| 2401 | + "with": {} | |
| 2402 | + }, | |
| 2403 | + "cancer_gene_freq_cancer_idx": { | |
| 2404 | + "name": "cancer_gene_freq_cancer_idx", | |
| 2405 | + "columns": [ | |
| 2406 | + { | |
| 2407 | + "expression": "cancer_id", | |
| 2408 | + "isExpression": false, | |
| 2409 | + "asc": true, | |
| 2410 | + "nulls": "last" | |
| 2411 | + }, | |
| 2412 | + { | |
| 2413 | + "expression": "frequency", | |
| 2414 | + "isExpression": false, | |
| 2415 | + "asc": true, | |
| 2416 | + "nulls": "last" | |
| 2417 | + } | |
| 2418 | + ], | |
| 2419 | + "isUnique": false, | |
| 2420 | + "concurrently": false, | |
| 2421 | + "method": "btree", | |
| 2422 | + "with": {} | |
| 2423 | + }, | |
| 2424 | + "cancer_gene_freq_gene_idx": { | |
| 2425 | + "name": "cancer_gene_freq_gene_idx", | |
| 2426 | + "columns": [ | |
| 2427 | + { | |
| 2428 | + "expression": "gene_id", | |
| 2429 | + "isExpression": false, | |
| 2430 | + "asc": true, | |
| 2431 | + "nulls": "last" | |
| 2432 | + } | |
| 2433 | + ], | |
| 2434 | + "isUnique": false, | |
| 2435 | + "concurrently": false, | |
| 2436 | + "method": "btree", | |
| 2437 | + "with": {} | |
| 2438 | + } | |
| 2439 | + }, | |
| 2440 | + "foreignKeys": {}, | |
| 2441 | + "compositePrimaryKeys": {}, | |
| 2442 | + "uniqueConstraints": {}, | |
| 2443 | + "policies": {}, | |
| 2444 | + "checkConstraints": {}, | |
| 2445 | + "isRLSEnabled": false | |
| 2446 | + }, | |
| 2447 | + "public.entity_embeddings": { | |
| 2448 | + "name": "entity_embeddings", | |
| 2449 | + "schema": "", | |
| 2450 | + "columns": { | |
| 2451 | + "id": { | |
| 2452 | + "name": "id", | |
| 2453 | + "type": "bigserial", | |
| 2454 | + "primaryKey": true, | |
| 2455 | + "notNull": true | |
| 2456 | + }, | |
| 2457 | + "entity_type": { | |
| 2458 | + "name": "entity_type", | |
| 2459 | + "type": "text", | |
| 2460 | + "primaryKey": false, | |
| 2461 | + "notNull": true | |
| 2462 | + }, | |
| 2463 | + "entity_id": { | |
| 2464 | + "name": "entity_id", | |
| 2465 | + "type": "text", | |
| 2466 | + "primaryKey": false, | |
| 2467 | + "notNull": true | |
| 2468 | + }, | |
| 2469 | + "model": { | |
| 2470 | + "name": "model", | |
| 2471 | + "type": "text", | |
| 2472 | + "primaryKey": false, | |
| 2473 | + "notNull": true | |
| 2474 | + }, | |
| 2475 | + "dimensions": { | |
| 2476 | + "name": "dimensions", | |
| 2477 | + "type": "integer", | |
| 2478 | + "primaryKey": false, | |
| 2479 | + "notNull": true | |
| 2480 | + }, | |
| 2481 | + "text_hash": { | |
| 2482 | + "name": "text_hash", | |
| 2483 | + "type": "text", | |
| 2484 | + "primaryKey": false, | |
| 2485 | + "notNull": true | |
| 2486 | + }, | |
| 2487 | + "embedding": { | |
| 2488 | + "name": "embedding", | |
| 2489 | + "type": "text", | |
| 2490 | + "primaryKey": false, | |
| 2491 | + "notNull": false | |
| 2492 | + }, | |
| 2493 | + "created_at": { | |
| 2494 | + "name": "created_at", | |
| 2495 | + "type": "timestamp with time zone", | |
| 2496 | + "primaryKey": false, | |
| 2497 | + "notNull": true, | |
| 2498 | + "default": "now()" | |
| 2499 | + } | |
| 2500 | + }, | |
| 2501 | + "indexes": { | |
| 2502 | + "entity_embeddings_uq": { | |
| 2503 | + "name": "entity_embeddings_uq", | |
| 2504 | + "columns": [ | |
| 2505 | + { | |
| 2506 | + "expression": "entity_type", | |
| 2507 | + "isExpression": false, | |
| 2508 | + "asc": true, | |
| 2509 | + "nulls": "last" | |
| 2510 | + }, | |
| 2511 | + { | |
| 2512 | + "expression": "entity_id", | |
| 2513 | + "isExpression": false, | |
| 2514 | + "asc": true, | |
| 2515 | + "nulls": "last" | |
| 2516 | + }, | |
| 2517 | + { | |
| 2518 | + "expression": "model", | |
| 2519 | + "isExpression": false, | |
| 2520 | + "asc": true, | |
| 2521 | + "nulls": "last" | |
| 2522 | + } | |
| 2523 | + ], | |
| 2524 | + "isUnique": true, | |
| 2525 | + "concurrently": false, | |
| 2526 | + "method": "btree", | |
| 2527 | + "with": {} | |
| 2528 | + } | |
| 2529 | + }, | |
| 2530 | + "foreignKeys": {}, | |
| 2531 | + "compositePrimaryKeys": {}, | |
| 2532 | + "uniqueConstraints": {}, | |
| 2533 | + "policies": {}, | |
| 2534 | + "checkConstraints": {}, | |
| 2535 | + "isRLSEnabled": false | |
| 2536 | + }, | |
| 2537 | + "public.gene_aliases": { | |
| 2538 | + "name": "gene_aliases", | |
| 2539 | + "schema": "", | |
| 2540 | + "columns": { | |
| 2541 | + "id": { | |
| 2542 | + "name": "id", | |
| 2543 | + "type": "bigserial", | |
| 2544 | + "primaryKey": true, | |
| 2545 | + "notNull": true | |
| 2546 | + }, | |
| 2547 | + "gene_id": { | |
| 2548 | + "name": "gene_id", | |
| 2549 | + "type": "varchar(32)", | |
| 2550 | + "primaryKey": false, | |
| 2551 | + "notNull": true | |
| 2552 | + }, | |
| 2553 | + "alias": { | |
| 2554 | + "name": "alias", | |
| 2555 | + "type": "text", | |
| 2556 | + "primaryKey": false, | |
| 2557 | + "notNull": true | |
| 2558 | + }, | |
| 2559 | + "alias_type": { | |
| 2560 | + "name": "alias_type", | |
| 2561 | + "type": "text", | |
| 2562 | + "primaryKey": false, | |
| 2563 | + "notNull": true | |
| 2564 | + }, | |
| 2565 | + "source_id": { | |
| 2566 | + "name": "source_id", | |
| 2567 | + "type": "varchar(32)", | |
| 2568 | + "primaryKey": false, | |
| 2569 | + "notNull": false | |
| 2570 | + } | |
| 2571 | + }, | |
| 2572 | + "indexes": { | |
| 2573 | + "gene_aliases_uq": { | |
| 2574 | + "name": "gene_aliases_uq", | |
| 2575 | + "columns": [ | |
| 2576 | + { | |
| 2577 | + "expression": "gene_id", | |
| 2578 | + "isExpression": false, | |
| 2579 | + "asc": true, | |
| 2580 | + "nulls": "last" | |
| 2581 | + }, | |
| 2582 | + { | |
| 2583 | + "expression": "alias", | |
| 2584 | + "isExpression": false, | |
| 2585 | + "asc": true, | |
| 2586 | + "nulls": "last" | |
| 2587 | + }, | |
| 2588 | + { | |
| 2589 | + "expression": "alias_type", | |
| 2590 | + "isExpression": false, | |
| 2591 | + "asc": true, | |
| 2592 | + "nulls": "last" | |
| 2593 | + } | |
| 2594 | + ], | |
| 2595 | + "isUnique": true, | |
| 2596 | + "concurrently": false, | |
| 2597 | + "method": "btree", | |
| 2598 | + "with": {} | |
| 2599 | + }, | |
| 2600 | + "gene_aliases_alias_idx": { | |
| 2601 | + "name": "gene_aliases_alias_idx", | |
| 2602 | + "columns": [ | |
| 2603 | + { | |
| 2604 | + "expression": "alias", | |
| 2605 | + "isExpression": false, | |
| 2606 | + "asc": true, | |
| 2607 | + "nulls": "last" | |
| 2608 | + } | |
| 2609 | + ], | |
| 2610 | + "isUnique": false, | |
| 2611 | + "concurrently": false, | |
| 2612 | + "method": "btree", | |
| 2613 | + "with": {} | |
| 2614 | + } | |
| 2615 | + }, | |
| 2616 | + "foreignKeys": {}, | |
| 2617 | + "compositePrimaryKeys": {}, | |
| 2618 | + "uniqueConstraints": {}, | |
| 2619 | + "policies": {}, | |
| 2620 | + "checkConstraints": {}, | |
| 2621 | + "isRLSEnabled": false | |
| 2622 | + }, | |
| 2623 | + "public.genes": { | |
| 2624 | + "name": "genes", | |
| 2625 | + "schema": "", | |
| 2626 | + "columns": { | |
| 2627 | + "id": { | |
| 2628 | + "name": "id", | |
| 2629 | + "type": "varchar(32)", | |
| 2630 | + "primaryKey": true, | |
| 2631 | + "notNull": true | |
| 2632 | + }, | |
| 2633 | + "hgnc_id": { | |
| 2634 | + "name": "hgnc_id", | |
| 2635 | + "type": "text", | |
| 2636 | + "primaryKey": false, | |
| 2637 | + "notNull": false | |
| 2638 | + }, | |
| 2639 | + "symbol": { | |
| 2640 | + "name": "symbol", | |
| 2641 | + "type": "text", | |
| 2642 | + "primaryKey": false, | |
| 2643 | + "notNull": true | |
| 2644 | + }, | |
| 2645 | + "name": { | |
| 2646 | + "name": "name", | |
| 2647 | + "type": "text", | |
| 2648 | + "primaryKey": false, | |
| 2649 | + "notNull": false | |
| 2650 | + }, | |
| 2651 | + "locus_type": { | |
| 2652 | + "name": "locus_type", | |
| 2653 | + "type": "text", | |
| 2654 | + "primaryKey": false, | |
| 2655 | + "notNull": false | |
| 2656 | + }, | |
| 2657 | + "locus_group": { | |
| 2658 | + "name": "locus_group", | |
| 2659 | + "type": "text", | |
| 2660 | + "primaryKey": false, | |
| 2661 | + "notNull": false | |
| 2662 | + }, | |
| 2663 | + "location": { | |
| 2664 | + "name": "location", | |
| 2665 | + "type": "text", | |
| 2666 | + "primaryKey": false, | |
| 2667 | + "notNull": false | |
| 2668 | + }, | |
| 2669 | + "chromosome": { | |
| 2670 | + "name": "chromosome", | |
| 2671 | + "type": "text", | |
| 2672 | + "primaryKey": false, | |
| 2673 | + "notNull": false | |
| 2674 | + }, | |
| 2675 | + "ensembl_gene_id": { | |
| 2676 | + "name": "ensembl_gene_id", | |
| 2677 | + "type": "text", | |
| 2678 | + "primaryKey": false, | |
| 2679 | + "notNull": false | |
| 2680 | + }, | |
| 2681 | + "ncbi_gene_id": { | |
| 2682 | + "name": "ncbi_gene_id", | |
| 2683 | + "type": "text", | |
| 2684 | + "primaryKey": false, | |
| 2685 | + "notNull": false | |
| 2686 | + }, | |
| 2687 | + "omim_ids": { | |
| 2688 | + "name": "omim_ids", | |
| 2689 | + "type": "text[]", | |
| 2690 | + "primaryKey": false, | |
| 2691 | + "notNull": true, | |
| 2692 | + "default": "'{}'" | |
| 2693 | + }, | |
| 2694 | + "uniprot_ids": { | |
| 2695 | + "name": "uniprot_ids", | |
| 2696 | + "type": "text[]", | |
| 2697 | + "primaryKey": false, | |
| 2698 | + "notNull": true, | |
| 2699 | + "default": "'{}'" | |
| 2700 | + }, | |
| 2701 | + "refseq_accession": { | |
| 2702 | + "name": "refseq_accession", | |
| 2703 | + "type": "text", | |
| 2704 | + "primaryKey": false, | |
| 2705 | + "notNull": false | |
| 2706 | + }, | |
| 2707 | + "prev_symbols": { | |
| 2708 | + "name": "prev_symbols", | |
| 2709 | + "type": "text[]", | |
| 2710 | + "primaryKey": false, | |
| 2711 | + "notNull": true, | |
| 2712 | + "default": "'{}'" | |
| 2713 | + }, | |
| 2714 | + "alias_symbols": { | |
| 2715 | + "name": "alias_symbols", | |
| 2716 | + "type": "text[]", | |
| 2717 | + "primaryKey": false, | |
| 2718 | + "notNull": true, | |
| 2719 | + "default": "'{}'" | |
| 2720 | + }, | |
| 2721 | + "gene_families": { | |
| 2722 | + "name": "gene_families", | |
| 2723 | + "type": "text[]", | |
| 2724 | + "primaryKey": false, | |
| 2725 | + "notNull": true, | |
| 2726 | + "default": "'{}'" | |
| 2727 | + }, | |
| 2728 | + "status": { | |
| 2729 | + "name": "status", | |
| 2730 | + "type": "text", | |
| 2731 | + "primaryKey": false, | |
| 2732 | + "notNull": true, | |
| 2733 | + "default": "'Approved'" | |
| 2734 | + }, | |
| 2735 | + "is_cancer_gene": { | |
| 2736 | + "name": "is_cancer_gene", | |
| 2737 | + "type": "boolean", | |
| 2738 | + "primaryKey": false, | |
| 2739 | + "notNull": true, | |
| 2740 | + "default": false | |
| 2741 | + }, | |
| 2742 | + "civic_gene_id": { | |
| 2743 | + "name": "civic_gene_id", | |
| 2744 | + "type": "integer", | |
| 2745 | + "primaryKey": false, | |
| 2746 | + "notNull": false | |
| 2747 | + }, | |
| 2748 | + "description": { | |
| 2749 | + "name": "description", | |
| 2750 | + "type": "text", | |
| 2751 | + "primaryKey": false, | |
| 2752 | + "notNull": false | |
| 2753 | + }, | |
| 2754 | + "created_at": { | |
| 2755 | + "name": "created_at", | |
| 2756 | + "type": "timestamp with time zone", | |
| 2757 | + "primaryKey": false, | |
| 2758 | + "notNull": true, | |
| 2759 | + "default": "now()" | |
| 2760 | + }, | |
| 2761 | + "updated_at": { | |
| 2762 | + "name": "updated_at", | |
| 2763 | + "type": "timestamp with time zone", | |
| 2764 | + "primaryKey": false, | |
| 2765 | + "notNull": true, | |
| 2766 | + "default": "now()" | |
| 2767 | + } | |
| 2768 | + }, | |
| 2769 | + "indexes": { | |
| 2770 | + "genes_symbol_uq": { | |
| 2771 | + "name": "genes_symbol_uq", | |
| 2772 | + "columns": [ | |
| 2773 | + { | |
| 2774 | + "expression": "symbol", | |
| 2775 | + "isExpression": false, | |
| 2776 | + "asc": true, | |
| 2777 | + "nulls": "last" | |
| 2778 | + } | |
| 2779 | + ], | |
| 2780 | + "isUnique": true, | |
| 2781 | + "concurrently": false, | |
| 2782 | + "method": "btree", | |
| 2783 | + "with": {} | |
| 2784 | + }, | |
| 2785 | + "genes_hgnc_uq": { | |
| 2786 | + "name": "genes_hgnc_uq", | |
| 2787 | + "columns": [ | |
| 2788 | + { | |
| 2789 | + "expression": "hgnc_id", | |
| 2790 | + "isExpression": false, | |
| 2791 | + "asc": true, | |
| 2792 | + "nulls": "last" | |
| 2793 | + } | |
| 2794 | + ], | |
| 2795 | + "isUnique": true, | |
| 2796 | + "concurrently": false, | |
| 2797 | + "method": "btree", | |
| 2798 | + "with": {} | |
| 2799 | + }, | |
| 2800 | + "genes_ensembl_idx": { | |
| 2801 | + "name": "genes_ensembl_idx", | |
| 2802 | + "columns": [ | |
| 2803 | + { | |
| 2804 | + "expression": "ensembl_gene_id", | |
| 2805 | + "isExpression": false, | |
| 2806 | + "asc": true, | |
| 2807 | + "nulls": "last" | |
| 2808 | + } | |
| 2809 | + ], | |
| 2810 | + "isUnique": false, | |
| 2811 | + "concurrently": false, | |
| 2812 | + "method": "btree", | |
| 2813 | + "with": {} | |
| 2814 | + }, | |
| 2815 | + "genes_ncbi_idx": { | |
| 2816 | + "name": "genes_ncbi_idx", | |
| 2817 | + "columns": [ | |
| 2818 | + { | |
| 2819 | + "expression": "ncbi_gene_id", | |
| 2820 | + "isExpression": false, | |
| 2821 | + "asc": true, | |
| 2822 | + "nulls": "last" | |
| 2823 | + } | |
| 2824 | + ], | |
| 2825 | + "isUnique": false, | |
| 2826 | + "concurrently": false, | |
| 2827 | + "method": "btree", | |
| 2828 | + "with": {} | |
| 2829 | + } | |
| 2830 | + }, | |
| 2831 | + "foreignKeys": {}, | |
| 2832 | + "compositePrimaryKeys": {}, | |
| 2833 | + "uniqueConstraints": {}, | |
| 2834 | + "policies": {}, | |
| 2835 | + "checkConstraints": {}, | |
| 2836 | + "isRLSEnabled": false | |
| 2837 | + }, | |
| 2838 | + "public.genomic_cohorts": { | |
| 2839 | + "name": "genomic_cohorts", | |
| 2840 | + "schema": "", | |
| 2841 | + "columns": { | |
| 2842 | + "id": { | |
| 2843 | + "name": "id", | |
| 2844 | + "type": "varchar(32)", | |
| 2845 | + "primaryKey": true, | |
| 2846 | + "notNull": true | |
| 2847 | + }, | |
| 2848 | + "source_id": { | |
| 2849 | + "name": "source_id", | |
| 2850 | + "type": "varchar(32)", | |
| 2851 | + "primaryKey": false, | |
| 2852 | + "notNull": true | |
| 2853 | + }, | |
| 2854 | + "study_id": { | |
| 2855 | + "name": "study_id", | |
| 2856 | + "type": "text", | |
| 2857 | + "primaryKey": false, | |
| 2858 | + "notNull": true | |
| 2859 | + }, | |
| 2860 | + "name": { | |
| 2861 | + "name": "name", | |
| 2862 | + "type": "text", | |
| 2863 | + "primaryKey": false, | |
| 2864 | + "notNull": true | |
| 2865 | + }, | |
| 2866 | + "program": { | |
| 2867 | + "name": "program", | |
| 2868 | + "type": "text", | |
| 2869 | + "primaryKey": false, | |
| 2870 | + "notNull": false | |
| 2871 | + }, | |
| 2872 | + "primary_sites": { | |
| 2873 | + "name": "primary_sites", | |
| 2874 | + "type": "text[]", | |
| 2875 | + "primaryKey": false, | |
| 2876 | + "notNull": true, | |
| 2877 | + "default": "'{}'" | |
| 2878 | + }, | |
| 2879 | + "disease_types": { | |
| 2880 | + "name": "disease_types", | |
| 2881 | + "type": "text[]", | |
| 2882 | + "primaryKey": false, | |
| 2883 | + "notNull": true, | |
| 2884 | + "default": "'{}'" | |
| 2885 | + }, | |
| 2886 | + "cancer_id": { | |
| 2887 | + "name": "cancer_id", | |
| 2888 | + "type": "varchar(32)", | |
| 2889 | + "primaryKey": false, | |
| 2890 | + "notNull": false | |
| 2891 | + }, | |
| 2892 | + "cancer_match_type": { | |
| 2893 | + "name": "cancer_match_type", | |
| 2894 | + "type": "text", | |
| 2895 | + "primaryKey": false, | |
| 2896 | + "notNull": false | |
| 2897 | + }, | |
| 2898 | + "case_count": { | |
| 2899 | + "name": "case_count", | |
| 2900 | + "type": "integer", | |
| 2901 | + "primaryKey": false, | |
| 2902 | + "notNull": false | |
| 2903 | + }, | |
| 2904 | + "cases_with_ssm": { | |
| 2905 | + "name": "cases_with_ssm", | |
| 2906 | + "type": "integer", | |
| 2907 | + "primaryKey": false, | |
| 2908 | + "notNull": false | |
| 2909 | + }, | |
| 2910 | + "data_release": { | |
| 2911 | + "name": "data_release", | |
| 2912 | + "type": "text", | |
| 2913 | + "primaryKey": false, | |
| 2914 | + "notNull": false | |
| 2915 | + }, | |
| 2916 | + "access_level": { | |
| 2917 | + "name": "access_level", | |
| 2918 | + "type": "text", | |
| 2919 | + "primaryKey": false, | |
| 2920 | + "notNull": true, | |
| 2921 | + "default": "'open'" | |
| 2922 | + }, | |
| 2923 | + "url": { | |
| 2924 | + "name": "url", | |
| 2925 | + "type": "text", | |
| 2926 | + "primaryKey": false, | |
| 2927 | + "notNull": false | |
| 2928 | + }, | |
| 2929 | + "provenance_id": { | |
| 2930 | + "name": "provenance_id", | |
| 2931 | + "type": "integer", | |
| 2932 | + "primaryKey": false, | |
| 2933 | + "notNull": false | |
| 2934 | + }, | |
| 2935 | + "updated_at": { | |
| 2936 | + "name": "updated_at", | |
| 2937 | + "type": "timestamp with time zone", | |
| 2938 | + "primaryKey": false, | |
| 2939 | + "notNull": true, | |
| 2940 | + "default": "now()" | |
| 2941 | + } | |
| 2942 | + }, | |
| 2943 | + "indexes": { | |
| 2944 | + "genomic_cohorts_uq": { | |
| 2945 | + "name": "genomic_cohorts_uq", | |
| 2946 | + "columns": [ | |
| 2947 | + { | |
| 2948 | + "expression": "source_id", | |
| 2949 | + "isExpression": false, | |
| 2950 | + "asc": true, | |
| 2951 | + "nulls": "last" | |
| 2952 | + }, | |
| 2953 | + { | |
| 2954 | + "expression": "study_id", | |
| 2955 | + "isExpression": false, | |
| 2956 | + "asc": true, | |
| 2957 | + "nulls": "last" | |
| 2958 | + } | |
| 2959 | + ], | |
| 2960 | + "isUnique": true, | |
| 2961 | + "concurrently": false, | |
| 2962 | + "method": "btree", | |
| 2963 | + "with": {} | |
| 2964 | + }, | |
| 2965 | + "genomic_cohorts_cancer_idx": { | |
| 2966 | + "name": "genomic_cohorts_cancer_idx", | |
| 2967 | + "columns": [ | |
| 2968 | + { | |
| 2969 | + "expression": "cancer_id", | |
| 2970 | + "isExpression": false, | |
| 2971 | + "asc": true, | |
| 2972 | + "nulls": "last" | |
| 2973 | + } | |
| 2974 | + ], | |
| 2975 | + "isUnique": false, | |
| 2976 | + "concurrently": false, | |
| 2977 | + "method": "btree", | |
| 2978 | + "with": {} | |
| 2979 | + } | |
| 2980 | + }, | |
| 2981 | + "foreignKeys": {}, | |
| 2982 | + "compositePrimaryKeys": {}, | |
| 2983 | + "uniqueConstraints": {}, | |
| 2984 | + "policies": {}, | |
| 2985 | + "checkConstraints": {}, | |
| 2986 | + "isRLSEnabled": false | |
| 2987 | + }, | |
| 2988 | + "public.variant_aliases": { | |
| 2989 | + "name": "variant_aliases", | |
| 2990 | + "schema": "", | |
| 2991 | + "columns": { | |
| 2992 | + "id": { | |
| 2993 | + "name": "id", | |
| 2994 | + "type": "bigserial", | |
| 2995 | + "primaryKey": true, | |
| 2996 | + "notNull": true | |
| 2997 | + }, | |
| 2998 | + "variant_id": { | |
| 2999 | + "name": "variant_id", | |
| 3000 | + "type": "varchar(32)", | |
| 3001 | + "primaryKey": false, | |
| 3002 | + "notNull": true | |
| 3003 | + }, | |
| 3004 | + "alias": { | |
| 3005 | + "name": "alias", | |
| 3006 | + "type": "text", | |
| 3007 | + "primaryKey": false, | |
| 3008 | + "notNull": true | |
| 3009 | + }, | |
| 3010 | + "source_id": { | |
| 3011 | + "name": "source_id", | |
| 3012 | + "type": "varchar(32)", | |
| 3013 | + "primaryKey": false, | |
| 3014 | + "notNull": false | |
| 3015 | + } | |
| 3016 | + }, | |
| 3017 | + "indexes": { | |
| 3018 | + "variant_aliases_uq": { | |
| 3019 | + "name": "variant_aliases_uq", | |
| 3020 | + "columns": [ | |
| 3021 | + { | |
| 3022 | + "expression": "variant_id", | |
| 3023 | + "isExpression": false, | |
| 3024 | + "asc": true, | |
| 3025 | + "nulls": "last" | |
| 3026 | + }, | |
| 3027 | + { | |
| 3028 | + "expression": "alias", | |
| 3029 | + "isExpression": false, | |
| 3030 | + "asc": true, | |
| 3031 | + "nulls": "last" | |
| 3032 | + } | |
| 3033 | + ], | |
| 3034 | + "isUnique": true, | |
| 3035 | + "concurrently": false, | |
| 3036 | + "method": "btree", | |
| 3037 | + "with": {} | |
| 3038 | + } | |
| 3039 | + }, | |
| 3040 | + "foreignKeys": {}, | |
| 3041 | + "compositePrimaryKeys": {}, | |
| 3042 | + "uniqueConstraints": {}, | |
| 3043 | + "policies": {}, | |
| 3044 | + "checkConstraints": {}, | |
| 3045 | + "isRLSEnabled": false | |
| 3046 | + }, | |
| 3047 | + "public.variant_clinical_significance": { | |
| 3048 | + "name": "variant_clinical_significance", | |
| 3049 | + "schema": "", | |
| 3050 | + "columns": { | |
| 3051 | + "id": { | |
| 3052 | + "name": "id", | |
| 3053 | + "type": "bigserial", | |
| 3054 | + "primaryKey": true, | |
| 3055 | + "notNull": true | |
| 3056 | + }, | |
| 3057 | + "variant_id": { | |
| 3058 | + "name": "variant_id", | |
| 3059 | + "type": "varchar(32)", | |
| 3060 | + "primaryKey": false, | |
| 3061 | + "notNull": true | |
| 3062 | + }, | |
| 3063 | + "clinvar_variation_id": { | |
| 3064 | + "name": "clinvar_variation_id", | |
| 3065 | + "type": "text", | |
| 3066 | + "primaryKey": false, | |
| 3067 | + "notNull": true | |
| 3068 | + }, | |
| 3069 | + "clinical_significance": { | |
| 3070 | + "name": "clinical_significance", | |
| 3071 | + "type": "text", | |
| 3072 | + "primaryKey": false, | |
| 3073 | + "notNull": true | |
| 3074 | + }, | |
| 3075 | + "review_status": { | |
| 3076 | + "name": "review_status", | |
| 3077 | + "type": "text", | |
| 3078 | + "primaryKey": false, | |
| 3079 | + "notNull": false | |
| 3080 | + }, | |
| 3081 | + "star_rating": { | |
| 3082 | + "name": "star_rating", | |
| 3083 | + "type": "integer", | |
| 3084 | + "primaryKey": false, | |
| 3085 | + "notNull": false | |
| 3086 | + }, | |
| 3087 | + "last_evaluated": { | |
| 3088 | + "name": "last_evaluated", | |
| 3089 | + "type": "text", | |
| 3090 | + "primaryKey": false, | |
| 3091 | + "notNull": false | |
| 3092 | + }, | |
| 3093 | + "conditions": { | |
| 3094 | + "name": "conditions", | |
| 3095 | + "type": "text[]", | |
| 3096 | + "primaryKey": false, | |
| 3097 | + "notNull": true, | |
| 3098 | + "default": "'{}'" | |
| 3099 | + }, | |
| 3100 | + "condition_cancer_ids": { | |
| 3101 | + "name": "condition_cancer_ids", | |
| 3102 | + "type": "text[]", | |
| 3103 | + "primaryKey": false, | |
| 3104 | + "notNull": true, | |
| 3105 | + "default": "'{}'" | |
| 3106 | + }, | |
| 3107 | + "origin_simple": { | |
| 3108 | + "name": "origin_simple", | |
| 3109 | + "type": "text", | |
| 3110 | + "primaryKey": false, | |
| 3111 | + "notNull": false | |
| 3112 | + }, | |
| 3113 | + "number_submitters": { | |
| 3114 | + "name": "number_submitters", | |
| 3115 | + "type": "integer", | |
| 3116 | + "primaryKey": false, | |
| 3117 | + "notNull": false | |
| 3118 | + }, | |
| 3119 | + "provenance_id": { | |
| 3120 | + "name": "provenance_id", | |
| 3121 | + "type": "integer", | |
| 3122 | + "primaryKey": false, | |
| 3123 | + "notNull": true | |
| 3124 | + }, | |
| 3125 | + "ingest_run_id": { | |
| 3126 | + "name": "ingest_run_id", | |
| 3127 | + "type": "text", | |
| 3128 | + "primaryKey": false, | |
| 3129 | + "notNull": false | |
| 3130 | + }, | |
| 3131 | + "updated_at": { | |
| 3132 | + "name": "updated_at", | |
| 3133 | + "type": "timestamp with time zone", | |
| 3134 | + "primaryKey": false, | |
| 3135 | + "notNull": true, | |
| 3136 | + "default": "now()" | |
| 3137 | + } | |
| 3138 | + }, | |
| 3139 | + "indexes": { | |
| 3140 | + "variant_clinsig_uq": { | |
| 3141 | + "name": "variant_clinsig_uq", | |
| 3142 | + "columns": [ | |
| 3143 | + { | |
| 3144 | + "expression": "clinvar_variation_id", | |
| 3145 | + "isExpression": false, | |
| 3146 | + "asc": true, | |
| 3147 | + "nulls": "last" | |
| 3148 | + } | |
| 3149 | + ], | |
| 3150 | + "isUnique": true, | |
| 3151 | + "concurrently": false, | |
| 3152 | + "method": "btree", | |
| 3153 | + "with": {} | |
| 3154 | + } | |
| 3155 | + }, | |
| 3156 | + "foreignKeys": {}, | |
| 3157 | + "compositePrimaryKeys": {}, | |
| 3158 | + "uniqueConstraints": {}, | |
| 3159 | + "policies": {}, | |
| 3160 | + "checkConstraints": {}, | |
| 3161 | + "isRLSEnabled": false | |
| 3162 | + }, | |
| 3163 | + "public.variants": { | |
| 3164 | + "name": "variants", | |
| 3165 | + "schema": "", | |
| 3166 | + "columns": { | |
| 3167 | + "id": { | |
| 3168 | + "name": "id", | |
| 3169 | + "type": "varchar(32)", | |
| 3170 | + "primaryKey": true, | |
| 3171 | + "notNull": true | |
| 3172 | + }, | |
| 3173 | + "slug": { | |
| 3174 | + "name": "slug", | |
| 3175 | + "type": "text", | |
| 3176 | + "primaryKey": false, | |
| 3177 | + "notNull": true | |
| 3178 | + }, | |
| 3179 | + "gene_id": { | |
| 3180 | + "name": "gene_id", | |
| 3181 | + "type": "varchar(32)", | |
| 3182 | + "primaryKey": false, | |
| 3183 | + "notNull": false | |
| 3184 | + }, | |
| 3185 | + "gene_symbol": { | |
| 3186 | + "name": "gene_symbol", | |
| 3187 | + "type": "text", | |
| 3188 | + "primaryKey": false, | |
| 3189 | + "notNull": false | |
| 3190 | + }, | |
| 3191 | + "name": { | |
| 3192 | + "name": "name", | |
| 3193 | + "type": "text", | |
| 3194 | + "primaryKey": false, | |
| 3195 | + "notNull": true | |
| 3196 | + }, | |
| 3197 | + "variant_type": { | |
| 3198 | + "name": "variant_type", | |
| 3199 | + "type": "text", | |
| 3200 | + "primaryKey": false, | |
| 3201 | + "notNull": false | |
| 3202 | + }, | |
| 3203 | + "hgvs_g": { | |
| 3204 | + "name": "hgvs_g", | |
| 3205 | + "type": "text", | |
| 3206 | + "primaryKey": false, | |
| 3207 | + "notNull": false | |
| 3208 | + }, | |
| 3209 | + "hgvs_c": { | |
| 3210 | + "name": "hgvs_c", | |
| 3211 | + "type": "text", | |
| 3212 | + "primaryKey": false, | |
| 3213 | + "notNull": false | |
| 3214 | + }, | |
| 3215 | + "hgvs_p": { | |
| 3216 | + "name": "hgvs_p", | |
| 3217 | + "type": "text", | |
| 3218 | + "primaryKey": false, | |
| 3219 | + "notNull": false | |
| 3220 | + }, | |
| 3221 | + "assembly": { | |
| 3222 | + "name": "assembly", | |
| 3223 | + "type": "text", | |
| 3224 | + "primaryKey": false, | |
| 3225 | + "notNull": false | |
| 3226 | + }, | |
| 3227 | + "chromosome": { | |
| 3228 | + "name": "chromosome", | |
| 3229 | + "type": "text", | |
| 3230 | + "primaryKey": false, | |
| 3231 | + "notNull": false | |
| 3232 | + }, | |
| 3233 | + "start": { | |
| 3234 | + "name": "start", | |
| 3235 | + "type": "integer", | |
| 3236 | + "primaryKey": false, | |
| 3237 | + "notNull": false | |
| 3238 | + }, | |
| 3239 | + "end": { | |
| 3240 | + "name": "end", | |
| 3241 | + "type": "integer", | |
| 3242 | + "primaryKey": false, | |
| 3243 | + "notNull": false | |
| 3244 | + }, | |
| 3245 | + "reference_bases": { | |
| 3246 | + "name": "reference_bases", | |
| 3247 | + "type": "text", | |
| 3248 | + "primaryKey": false, | |
| 3249 | + "notNull": false | |
| 3250 | + }, | |
| 3251 | + "alternate_bases": { | |
| 3252 | + "name": "alternate_bases", | |
| 3253 | + "type": "text", | |
| 3254 | + "primaryKey": false, | |
| 3255 | + "notNull": false | |
| 3256 | + }, | |
| 3257 | + "coordinates": { | |
| 3258 | + "name": "coordinates", | |
| 3259 | + "type": "jsonb", | |
| 3260 | + "primaryKey": false, | |
| 3261 | + "notNull": true, | |
| 3262 | + "default": "'[]'::jsonb" | |
| 3263 | + }, | |
| 3264 | + "clinvar_variation_id": { | |
| 3265 | + "name": "clinvar_variation_id", | |
| 3266 | + "type": "text", | |
| 3267 | + "primaryKey": false, | |
| 3268 | + "notNull": false | |
| 3269 | + }, | |
| 3270 | + "civic_variant_id": { | |
| 3271 | + "name": "civic_variant_id", | |
| 3272 | + "type": "integer", | |
| 3273 | + "primaryKey": false, | |
| 3274 | + "notNull": false | |
| 3275 | + }, | |
| 3276 | + "dbsnp_ids": { | |
| 3277 | + "name": "dbsnp_ids", | |
| 3278 | + "type": "text[]", | |
| 3279 | + "primaryKey": false, | |
| 3280 | + "notNull": true, | |
| 3281 | + "default": "'{}'" | |
| 3282 | + }, | |
| 3283 | + "fusion_partners": { | |
| 3284 | + "name": "fusion_partners", | |
| 3285 | + "type": "text[]", | |
| 3286 | + "primaryKey": false, | |
| 3287 | + "notNull": true, | |
| 3288 | + "default": "'{}'" | |
| 3289 | + }, | |
| 3290 | + "created_at": { | |
| 3291 | + "name": "created_at", | |
| 3292 | + "type": "timestamp with time zone", | |
| 3293 | + "primaryKey": false, | |
| 3294 | + "notNull": true, | |
| 3295 | + "default": "now()" | |
| 3296 | + }, | |
| 3297 | + "updated_at": { | |
| 3298 | + "name": "updated_at", | |
| 3299 | + "type": "timestamp with time zone", | |
| 3300 | + "primaryKey": false, | |
| 3301 | + "notNull": true, | |
| 3302 | + "default": "now()" | |
| 3303 | + } | |
| 3304 | + }, | |
| 3305 | + "indexes": { | |
| 3306 | + "variants_slug_uq": { | |
| 3307 | + "name": "variants_slug_uq", | |
| 3308 | + "columns": [ | |
| 3309 | + { | |
| 3310 | + "expression": "slug", | |
| 3311 | + "isExpression": false, | |
| 3312 | + "asc": true, | |
| 3313 | + "nulls": "last" | |
| 3314 | + } | |
| 3315 | + ], | |
| 3316 | + "isUnique": true, | |
| 3317 | + "concurrently": false, | |
| 3318 | + "method": "btree", | |
| 3319 | + "with": {} | |
| 3320 | + }, | |
| 3321 | + "variants_gene_idx": { | |
| 3322 | + "name": "variants_gene_idx", | |
| 3323 | + "columns": [ | |
| 3324 | + { | |
| 3325 | + "expression": "gene_id", | |
| 3326 | + "isExpression": false, | |
| 3327 | + "asc": true, | |
| 3328 | + "nulls": "last" | |
| 3329 | + } | |
| 3330 | + ], | |
| 3331 | + "isUnique": false, | |
| 3332 | + "concurrently": false, | |
| 3333 | + "method": "btree", | |
| 3334 | + "with": {} | |
| 3335 | + }, | |
| 3336 | + "variants_clinvar_idx": { | |
| 3337 | + "name": "variants_clinvar_idx", | |
| 3338 | + "columns": [ | |
| 3339 | + { | |
| 3340 | + "expression": "clinvar_variation_id", | |
| 3341 | + "isExpression": false, | |
| 3342 | + "asc": true, | |
| 3343 | + "nulls": "last" | |
| 3344 | + } | |
| 3345 | + ], | |
| 3346 | + "isUnique": false, | |
| 3347 | + "concurrently": false, | |
| 3348 | + "method": "btree", | |
| 3349 | + "with": {} | |
| 3350 | + }, | |
| 3351 | + "variants_civic_idx": { | |
| 3352 | + "name": "variants_civic_idx", | |
| 3353 | + "columns": [ | |
| 3354 | + { | |
| 3355 | + "expression": "civic_variant_id", | |
| 3356 | + "isExpression": false, | |
| 3357 | + "asc": true, | |
| 3358 | + "nulls": "last" | |
| 3359 | + } | |
| 3360 | + ], | |
| 3361 | + "isUnique": false, | |
| 3362 | + "concurrently": false, | |
| 3363 | + "method": "btree", | |
| 3364 | + "with": {} | |
| 3365 | + } | |
| 3366 | + }, | |
| 3367 | + "foreignKeys": {}, | |
| 3368 | + "compositePrimaryKeys": {}, | |
| 3369 | + "uniqueConstraints": {}, | |
| 3370 | + "policies": {}, | |
| 3371 | + "checkConstraints": {}, | |
| 3372 | + "isRLSEnabled": false | |
| 3373 | + }, | |
| 3374 | + "public.drug_aliases": { | |
| 3375 | + "name": "drug_aliases", | |
| 3376 | + "schema": "", | |
| 3377 | + "columns": { | |
| 3378 | + "id": { | |
| 3379 | + "name": "id", | |
| 3380 | + "type": "bigserial", | |
| 3381 | + "primaryKey": true, | |
| 3382 | + "notNull": true | |
| 3383 | + }, | |
| 3384 | + "drug_id": { | |
| 3385 | + "name": "drug_id", | |
| 3386 | + "type": "varchar(32)", | |
| 3387 | + "primaryKey": false, | |
| 3388 | + "notNull": true | |
| 3389 | + }, | |
| 3390 | + "alias": { | |
| 3391 | + "name": "alias", | |
| 3392 | + "type": "text", | |
| 3393 | + "primaryKey": false, | |
| 3394 | + "notNull": true | |
| 3395 | + }, | |
| 3396 | + "normalized": { | |
| 3397 | + "name": "normalized", | |
| 3398 | + "type": "text", | |
| 3399 | + "primaryKey": false, | |
| 3400 | + "notNull": true | |
| 3401 | + }, | |
| 3402 | + "alias_type": { | |
| 3403 | + "name": "alias_type", | |
| 3404 | + "type": "text", | |
| 3405 | + "primaryKey": false, | |
| 3406 | + "notNull": true, | |
| 3407 | + "default": "'synonym'" | |
| 3408 | + }, | |
| 3409 | + "source_id": { | |
| 3410 | + "name": "source_id", | |
| 3411 | + "type": "varchar(32)", | |
| 3412 | + "primaryKey": false, | |
| 3413 | + "notNull": false | |
| 3414 | + } | |
| 3415 | + }, | |
| 3416 | + "indexes": { | |
| 3417 | + "drug_aliases_uq": { | |
| 3418 | + "name": "drug_aliases_uq", | |
| 3419 | + "columns": [ | |
| 3420 | + { | |
| 3421 | + "expression": "drug_id", | |
| 3422 | + "isExpression": false, | |
| 3423 | + "asc": true, | |
| 3424 | + "nulls": "last" | |
| 3425 | + }, | |
| 3426 | + { | |
| 3427 | + "expression": "normalized", | |
| 3428 | + "isExpression": false, | |
| 3429 | + "asc": true, | |
| 3430 | + "nulls": "last" | |
| 3431 | + }, | |
| 3432 | + { | |
| 3433 | + "expression": "alias_type", | |
| 3434 | + "isExpression": false, | |
| 3435 | + "asc": true, | |
| 3436 | + "nulls": "last" | |
| 3437 | + } | |
| 3438 | + ], | |
| 3439 | + "isUnique": true, | |
| 3440 | + "concurrently": false, | |
| 3441 | + "method": "btree", | |
| 3442 | + "with": {} | |
| 3443 | + }, | |
| 3444 | + "drug_aliases_norm_idx": { | |
| 3445 | + "name": "drug_aliases_norm_idx", | |
| 3446 | + "columns": [ | |
| 3447 | + { | |
| 3448 | + "expression": "normalized", | |
| 3449 | + "isExpression": false, | |
| 3450 | + "asc": true, | |
| 3451 | + "nulls": "last" | |
| 3452 | + } | |
| 3453 | + ], | |
| 3454 | + "isUnique": false, | |
| 3455 | + "concurrently": false, | |
| 3456 | + "method": "btree", | |
| 3457 | + "with": {} | |
| 3458 | + } | |
| 3459 | + }, | |
| 3460 | + "foreignKeys": {}, | |
| 3461 | + "compositePrimaryKeys": {}, | |
| 3462 | + "uniqueConstraints": {}, | |
| 3463 | + "policies": {}, | |
| 3464 | + "checkConstraints": {}, | |
| 3465 | + "isRLSEnabled": false | |
| 3466 | + }, | |
| 3467 | + "public.drug_approvals": { | |
| 3468 | + "name": "drug_approvals", | |
| 3469 | + "schema": "", | |
| 3470 | + "columns": { | |
| 3471 | + "id": { | |
| 3472 | + "name": "id", | |
| 3473 | + "type": "bigserial", | |
| 3474 | + "primaryKey": true, | |
| 3475 | + "notNull": true | |
| 3476 | + }, | |
| 3477 | + "drug_id": { | |
| 3478 | + "name": "drug_id", | |
| 3479 | + "type": "varchar(32)", | |
| 3480 | + "primaryKey": false, | |
| 3481 | + "notNull": true | |
| 3482 | + }, | |
| 3483 | + "cancer_id": { | |
| 3484 | + "name": "cancer_id", | |
| 3485 | + "type": "varchar(32)", | |
| 3486 | + "primaryKey": false, | |
| 3487 | + "notNull": false | |
| 3488 | + }, | |
| 3489 | + "biomarker_ids": { | |
| 3490 | + "name": "biomarker_ids", | |
| 3491 | + "type": "text[]", | |
| 3492 | + "primaryKey": false, | |
| 3493 | + "notNull": true, | |
| 3494 | + "default": "'{}'" | |
| 3495 | + }, | |
| 3496 | + "tumor_agnostic": { | |
| 3497 | + "name": "tumor_agnostic", | |
| 3498 | + "type": "boolean", | |
| 3499 | + "primaryKey": false, | |
| 3500 | + "notNull": true, | |
| 3501 | + "default": false | |
| 3502 | + }, | |
| 3503 | + "jurisdiction": { | |
| 3504 | + "name": "jurisdiction", | |
| 3505 | + "type": "text", | |
| 3506 | + "primaryKey": false, | |
| 3507 | + "notNull": true | |
| 3508 | + }, | |
| 3509 | + "authority": { | |
| 3510 | + "name": "authority", | |
| 3511 | + "type": "text", | |
| 3512 | + "primaryKey": false, | |
| 3513 | + "notNull": true | |
| 3514 | + }, | |
| 3515 | + "indication": { | |
| 3516 | + "name": "indication", | |
| 3517 | + "type": "text", | |
| 3518 | + "primaryKey": false, | |
| 3519 | + "notNull": true | |
| 3520 | + }, | |
| 3521 | + "line_of_therapy": { | |
| 3522 | + "name": "line_of_therapy", | |
| 3523 | + "type": "text", | |
| 3524 | + "primaryKey": false, | |
| 3525 | + "notNull": false | |
| 3526 | + }, | |
| 3527 | + "disease_stage": { | |
| 3528 | + "name": "disease_stage", | |
| 3529 | + "type": "text", | |
| 3530 | + "primaryKey": false, | |
| 3531 | + "notNull": false | |
| 3532 | + }, | |
| 3533 | + "approval_type": { | |
| 3534 | + "name": "approval_type", | |
| 3535 | + "type": "text", | |
| 3536 | + "primaryKey": false, | |
| 3537 | + "notNull": false | |
| 3538 | + }, | |
| 3539 | + "accelerated": { | |
| 3540 | + "name": "accelerated", | |
| 3541 | + "type": "boolean", | |
| 3542 | + "primaryKey": false, | |
| 3543 | + "notNull": false | |
| 3544 | + }, | |
| 3545 | + "conditional": { | |
| 3546 | + "name": "conditional", | |
| 3547 | + "type": "boolean", | |
| 3548 | + "primaryKey": false, | |
| 3549 | + "notNull": false | |
| 3550 | + }, | |
| 3551 | + "approval_date": { | |
| 3552 | + "name": "approval_date", | |
| 3553 | + "type": "text", | |
| 3554 | + "primaryKey": false, | |
| 3555 | + "notNull": false | |
| 3556 | + }, | |
| 3557 | + "withdrawal_date": { | |
| 3558 | + "name": "withdrawal_date", | |
| 3559 | + "type": "text", | |
| 3560 | + "primaryKey": false, | |
| 3561 | + "notNull": false | |
| 3562 | + }, | |
| 3563 | + "status": { | |
| 3564 | + "name": "status", | |
| 3565 | + "type": "text", | |
| 3566 | + "primaryKey": false, | |
| 3567 | + "notNull": true | |
| 3568 | + }, | |
| 3569 | + "application_number": { | |
| 3570 | + "name": "application_number", | |
| 3571 | + "type": "text", | |
| 3572 | + "primaryKey": false, | |
| 3573 | + "notNull": false | |
| 3574 | + }, | |
| 3575 | + "source_id": { | |
| 3576 | + "name": "source_id", | |
| 3577 | + "type": "varchar(32)", | |
| 3578 | + "primaryKey": false, | |
| 3579 | + "notNull": true | |
| 3580 | + }, | |
| 3581 | + "provenance_id": { | |
| 3582 | + "name": "provenance_id", | |
| 3583 | + "type": "integer", | |
| 3584 | + "primaryKey": false, | |
| 3585 | + "notNull": true | |
| 3586 | + }, | |
| 3587 | + "raw": { | |
| 3588 | + "name": "raw", | |
| 3589 | + "type": "jsonb", | |
| 3590 | + "primaryKey": false, | |
| 3591 | + "notNull": false | |
| 3592 | + }, | |
| 3593 | + "created_at": { | |
| 3594 | + "name": "created_at", | |
| 3595 | + "type": "timestamp with time zone", | |
| 3596 | + "primaryKey": false, | |
| 3597 | + "notNull": true, | |
| 3598 | + "default": "now()" | |
| 3599 | + }, | |
| 3600 | + "updated_at": { | |
| 3601 | + "name": "updated_at", | |
| 3602 | + "type": "timestamp with time zone", | |
| 3603 | + "primaryKey": false, | |
| 3604 | + "notNull": true, | |
| 3605 | + "default": "now()" | |
| 3606 | + } | |
| 3607 | + }, | |
| 3608 | + "indexes": { | |
| 3609 | + "drug_approvals_drug_idx": { | |
| 3610 | + "name": "drug_approvals_drug_idx", | |
| 3611 | + "columns": [ | |
| 3612 | + { | |
| 3613 | + "expression": "drug_id", | |
| 3614 | + "isExpression": false, | |
| 3615 | + "asc": true, | |
| 3616 | + "nulls": "last" | |
| 3617 | + } | |
| 3618 | + ], | |
| 3619 | + "isUnique": false, | |
| 3620 | + "concurrently": false, | |
| 3621 | + "method": "btree", | |
| 3622 | + "with": {} | |
| 3623 | + }, | |
| 3624 | + "drug_approvals_cancer_idx": { | |
| 3625 | + "name": "drug_approvals_cancer_idx", | |
| 3626 | + "columns": [ | |
| 3627 | + { | |
| 3628 | + "expression": "cancer_id", | |
| 3629 | + "isExpression": false, | |
| 3630 | + "asc": true, | |
| 3631 | + "nulls": "last" | |
| 3632 | + } | |
| 3633 | + ], | |
| 3634 | + "isUnique": false, | |
| 3635 | + "concurrently": false, | |
| 3636 | + "method": "btree", | |
| 3637 | + "with": {} | |
| 3638 | + } | |
| 3639 | + }, | |
| 3640 | + "foreignKeys": {}, | |
| 3641 | + "compositePrimaryKeys": {}, | |
| 3642 | + "uniqueConstraints": {}, | |
| 3643 | + "policies": {}, | |
| 3644 | + "checkConstraints": {}, | |
| 3645 | + "isRLSEnabled": false | |
| 3646 | + }, | |
| 3647 | + "public.drugs": { | |
| 3648 | + "name": "drugs", | |
| 3649 | + "schema": "", | |
| 3650 | + "columns": { | |
| 3651 | + "id": { | |
| 3652 | + "name": "id", | |
| 3653 | + "type": "varchar(32)", | |
| 3654 | + "primaryKey": true, | |
| 3655 | + "notNull": true | |
| 3656 | + }, | |
| 3657 | + "slug": { | |
| 3658 | + "name": "slug", | |
| 3659 | + "type": "text", | |
| 3660 | + "primaryKey": false, | |
| 3661 | + "notNull": true | |
| 3662 | + }, | |
| 3663 | + "name": { | |
| 3664 | + "name": "name", | |
| 3665 | + "type": "text", | |
| 3666 | + "primaryKey": false, | |
| 3667 | + "notNull": true | |
| 3668 | + }, | |
| 3669 | + "kind": { | |
| 3670 | + "name": "kind", | |
| 3671 | + "type": "text", | |
| 3672 | + "primaryKey": false, | |
| 3673 | + "notNull": false | |
| 3674 | + }, | |
| 3675 | + "ncit_code": { | |
| 3676 | + "name": "ncit_code", | |
| 3677 | + "type": "text", | |
| 3678 | + "primaryKey": false, | |
| 3679 | + "notNull": false | |
| 3680 | + }, | |
| 3681 | + "chembl_id": { | |
| 3682 | + "name": "chembl_id", | |
| 3683 | + "type": "text", | |
| 3684 | + "primaryKey": false, | |
| 3685 | + "notNull": false | |
| 3686 | + }, | |
| 3687 | + "civic_therapy_id": { | |
| 3688 | + "name": "civic_therapy_id", | |
| 3689 | + "type": "integer", | |
| 3690 | + "primaryKey": false, | |
| 3691 | + "notNull": false | |
| 3692 | + }, | |
| 3693 | + "drugbank_id": { | |
| 3694 | + "name": "drugbank_id", | |
| 3695 | + "type": "text", | |
| 3696 | + "primaryKey": false, | |
| 3697 | + "notNull": false | |
| 3698 | + }, | |
| 3699 | + "pubchem_cid": { | |
| 3700 | + "name": "pubchem_cid", | |
| 3701 | + "type": "text", | |
| 3702 | + "primaryKey": false, | |
| 3703 | + "notNull": false | |
| 3704 | + }, | |
| 3705 | + "unii": { | |
| 3706 | + "name": "unii", | |
| 3707 | + "type": "text", | |
| 3708 | + "primaryKey": false, | |
| 3709 | + "notNull": false | |
| 3710 | + }, | |
| 3711 | + "mechanism": { | |
| 3712 | + "name": "mechanism", | |
| 3713 | + "type": "text", | |
| 3714 | + "primaryKey": false, | |
| 3715 | + "notNull": false | |
| 3716 | + }, | |
| 3717 | + "target_gene_ids": { | |
| 3718 | + "name": "target_gene_ids", | |
| 3719 | + "type": "text[]", | |
| 3720 | + "primaryKey": false, | |
| 3721 | + "notNull": true, | |
| 3722 | + "default": "'{}'" | |
| 3723 | + }, | |
| 3724 | + "development_status": { | |
| 3725 | + "name": "development_status", | |
| 3726 | + "type": "text", | |
| 3727 | + "primaryKey": false, | |
| 3728 | + "notNull": false | |
| 3729 | + }, | |
| 3730 | + "description": { | |
| 3731 | + "name": "description", | |
| 3732 | + "type": "text", | |
| 3733 | + "primaryKey": false, | |
| 3734 | + "notNull": false | |
| 3735 | + }, | |
| 3736 | + "created_at": { | |
| 3737 | + "name": "created_at", | |
| 3738 | + "type": "timestamp with time zone", | |
| 3739 | + "primaryKey": false, | |
| 3740 | + "notNull": true, | |
| 3741 | + "default": "now()" | |
| 3742 | + }, | |
| 3743 | + "updated_at": { | |
| 3744 | + "name": "updated_at", | |
| 3745 | + "type": "timestamp with time zone", | |
| 3746 | + "primaryKey": false, | |
| 3747 | + "notNull": true, | |
| 3748 | + "default": "now()" | |
| 3749 | + } | |
| 3750 | + }, | |
| 3751 | + "indexes": { | |
| 3752 | + "drugs_slug_uq": { | |
| 3753 | + "name": "drugs_slug_uq", | |
| 3754 | + "columns": [ | |
| 3755 | + { | |
| 3756 | + "expression": "slug", | |
| 3757 | + "isExpression": false, | |
| 3758 | + "asc": true, | |
| 3759 | + "nulls": "last" | |
| 3760 | + } | |
| 3761 | + ], | |
| 3762 | + "isUnique": true, | |
| 3763 | + "concurrently": false, | |
| 3764 | + "method": "btree", | |
| 3765 | + "with": {} | |
| 3766 | + }, | |
| 3767 | + "drugs_ncit_idx": { | |
| 3768 | + "name": "drugs_ncit_idx", | |
| 3769 | + "columns": [ | |
| 3770 | + { | |
| 3771 | + "expression": "ncit_code", | |
| 3772 | + "isExpression": false, | |
| 3773 | + "asc": true, | |
| 3774 | + "nulls": "last" | |
| 3775 | + } | |
| 3776 | + ], | |
| 3777 | + "isUnique": false, | |
| 3778 | + "concurrently": false, | |
| 3779 | + "method": "btree", | |
| 3780 | + "with": {} | |
| 3781 | + }, | |
| 3782 | + "drugs_civic_idx": { | |
| 3783 | + "name": "drugs_civic_idx", | |
| 3784 | + "columns": [ | |
| 3785 | + { | |
| 3786 | + "expression": "civic_therapy_id", | |
| 3787 | + "isExpression": false, | |
| 3788 | + "asc": true, | |
| 3789 | + "nulls": "last" | |
| 3790 | + } | |
| 3791 | + ], | |
| 3792 | + "isUnique": false, | |
| 3793 | + "concurrently": false, | |
| 3794 | + "method": "btree", | |
| 3795 | + "with": {} | |
| 3796 | + }, | |
| 3797 | + "drugs_chembl_idx": { | |
| 3798 | + "name": "drugs_chembl_idx", | |
| 3799 | + "columns": [ | |
| 3800 | + { | |
| 3801 | + "expression": "chembl_id", | |
| 3802 | + "isExpression": false, | |
| 3803 | + "asc": true, | |
| 3804 | + "nulls": "last" | |
| 3805 | + } | |
| 3806 | + ], | |
| 3807 | + "isUnique": false, | |
| 3808 | + "concurrently": false, | |
| 3809 | + "method": "btree", | |
| 3810 | + "with": {} | |
| 3811 | + } | |
| 3812 | + }, | |
| 3813 | + "foreignKeys": {}, | |
| 3814 | + "compositePrimaryKeys": {}, | |
| 3815 | + "uniqueConstraints": {}, | |
| 3816 | + "policies": {}, | |
| 3817 | + "checkConstraints": {}, | |
| 3818 | + "isRLSEnabled": false | |
| 3819 | + }, | |
| 3820 | + "public.treatment_regimens": { | |
| 3821 | + "name": "treatment_regimens", | |
| 3822 | + "schema": "", | |
| 3823 | + "columns": { | |
| 3824 | + "id": { | |
| 3825 | + "name": "id", | |
| 3826 | + "type": "varchar(32)", | |
| 3827 | + "primaryKey": true, | |
| 3828 | + "notNull": true | |
| 3829 | + }, | |
| 3830 | + "slug": { | |
| 3831 | + "name": "slug", | |
| 3832 | + "type": "text", | |
| 3833 | + "primaryKey": false, | |
| 3834 | + "notNull": true | |
| 3835 | + }, | |
| 3836 | + "name": { | |
| 3837 | + "name": "name", | |
| 3838 | + "type": "text", | |
| 3839 | + "primaryKey": false, | |
| 3840 | + "notNull": true | |
| 3841 | + }, | |
| 3842 | + "component_drug_ids": { | |
| 3843 | + "name": "component_drug_ids", | |
| 3844 | + "type": "text[]", | |
| 3845 | + "primaryKey": false, | |
| 3846 | + "notNull": true, | |
| 3847 | + "default": "'{}'" | |
| 3848 | + }, | |
| 3849 | + "modality": { | |
| 3850 | + "name": "modality", | |
| 3851 | + "type": "text", | |
| 3852 | + "primaryKey": false, | |
| 3853 | + "notNull": true, | |
| 3854 | + "default": "'drug_combination'" | |
| 3855 | + }, | |
| 3856 | + "description": { | |
| 3857 | + "name": "description", | |
| 3858 | + "type": "text", | |
| 3859 | + "primaryKey": false, | |
| 3860 | + "notNull": false | |
| 3861 | + }, | |
| 3862 | + "created_at": { | |
| 3863 | + "name": "created_at", | |
| 3864 | + "type": "timestamp with time zone", | |
| 3865 | + "primaryKey": false, | |
| 3866 | + "notNull": true, | |
| 3867 | + "default": "now()" | |
| 3868 | + } | |
| 3869 | + }, | |
| 3870 | + "indexes": { | |
| 3871 | + "treatment_regimens_slug_uq": { | |
| 3872 | + "name": "treatment_regimens_slug_uq", | |
| 3873 | + "columns": [ | |
| 3874 | + { | |
| 3875 | + "expression": "slug", | |
| 3876 | + "isExpression": false, | |
| 3877 | + "asc": true, | |
| 3878 | + "nulls": "last" | |
| 3879 | + } | |
| 3880 | + ], | |
| 3881 | + "isUnique": true, | |
| 3882 | + "concurrently": false, | |
| 3883 | + "method": "btree", | |
| 3884 | + "with": {} | |
| 3885 | + } | |
| 3886 | + }, | |
| 3887 | + "foreignKeys": {}, | |
| 3888 | + "compositePrimaryKeys": {}, | |
| 3889 | + "uniqueConstraints": {}, | |
| 3890 | + "policies": {}, | |
| 3891 | + "checkConstraints": {}, | |
| 3892 | + "isRLSEnabled": false | |
| 3893 | + }, | |
| 3894 | + "public.clinical_trials": { | |
| 3895 | + "name": "clinical_trials", | |
| 3896 | + "schema": "", | |
| 3897 | + "columns": { | |
| 3898 | + "id": { | |
| 3899 | + "name": "id", | |
| 3900 | + "type": "varchar(32)", | |
| 3901 | + "primaryKey": true, | |
| 3902 | + "notNull": true | |
| 3903 | + }, | |
| 3904 | + "nct_id": { | |
| 3905 | + "name": "nct_id", | |
| 3906 | + "type": "text", | |
| 3907 | + "primaryKey": false, | |
| 3908 | + "notNull": true | |
| 3909 | + }, | |
| 3910 | + "brief_title": { | |
| 3911 | + "name": "brief_title", | |
| 3912 | + "type": "text", | |
| 3913 | + "primaryKey": false, | |
| 3914 | + "notNull": true | |
| 3915 | + }, | |
| 3916 | + "official_title": { | |
| 3917 | + "name": "official_title", | |
| 3918 | + "type": "text", | |
| 3919 | + "primaryKey": false, | |
| 3920 | + "notNull": false | |
| 3921 | + }, | |
| 3922 | + "acronym": { | |
| 3923 | + "name": "acronym", | |
| 3924 | + "type": "text", | |
| 3925 | + "primaryKey": false, | |
| 3926 | + "notNull": false | |
| 3927 | + }, | |
| 3928 | + "study_type": { | |
| 3929 | + "name": "study_type", | |
| 3930 | + "type": "text", | |
| 3931 | + "primaryKey": false, | |
| 3932 | + "notNull": false | |
| 3933 | + }, | |
| 3934 | + "phases": { | |
| 3935 | + "name": "phases", | |
| 3936 | + "type": "text[]", | |
| 3937 | + "primaryKey": false, | |
| 3938 | + "notNull": true, | |
| 3939 | + "default": "'{}'" | |
| 3940 | + }, | |
| 3941 | + "overall_status": { | |
| 3942 | + "name": "overall_status", | |
| 3943 | + "type": "text", | |
| 3944 | + "primaryKey": false, | |
| 3945 | + "notNull": false | |
| 3946 | + }, | |
| 3947 | + "why_stopped": { | |
| 3948 | + "name": "why_stopped", | |
| 3949 | + "type": "text", | |
| 3950 | + "primaryKey": false, | |
| 3951 | + "notNull": false | |
| 3952 | + }, | |
| 3953 | + "start_date": { | |
| 3954 | + "name": "start_date", | |
| 3955 | + "type": "text", | |
| 3956 | + "primaryKey": false, | |
| 3957 | + "notNull": false | |
| 3958 | + }, | |
| 3959 | + "primary_completion_date": { | |
| 3960 | + "name": "primary_completion_date", | |
| 3961 | + "type": "text", | |
| 3962 | + "primaryKey": false, | |
| 3963 | + "notNull": false | |
| 3964 | + }, | |
| 3965 | + "completion_date": { | |
| 3966 | + "name": "completion_date", | |
| 3967 | + "type": "text", | |
| 3968 | + "primaryKey": false, | |
| 3969 | + "notNull": false | |
| 3970 | + }, | |
| 3971 | + "first_posted_date": { | |
| 3972 | + "name": "first_posted_date", | |
| 3973 | + "type": "text", | |
| 3974 | + "primaryKey": false, | |
| 3975 | + "notNull": false | |
| 3976 | + }, | |
| 3977 | + "last_update_posted_date": { | |
| 3978 | + "name": "last_update_posted_date", | |
| 3979 | + "type": "text", | |
| 3980 | + "primaryKey": false, | |
| 3981 | + "notNull": false | |
| 3982 | + }, | |
| 3983 | + "results_first_posted_date": { | |
| 3984 | + "name": "results_first_posted_date", | |
| 3985 | + "type": "text", | |
| 3986 | + "primaryKey": false, | |
| 3987 | + "notNull": false | |
| 3988 | + }, | |
| 3989 | + "has_results": { | |
| 3990 | + "name": "has_results", | |
| 3991 | + "type": "boolean", | |
| 3992 | + "primaryKey": false, | |
| 3993 | + "notNull": true, | |
| 3994 | + "default": false | |
| 3995 | + }, | |
| 3996 | + "enrollment_count": { | |
| 3997 | + "name": "enrollment_count", | |
| 3998 | + "type": "integer", | |
| 3999 | + "primaryKey": false, | |
| 4000 | + "notNull": false | |
| 4001 | + }, | |
| 4002 | + "enrollment_type": { | |
| 4003 | + "name": "enrollment_type", | |
| 4004 | + "type": "text", | |
| 4005 | + "primaryKey": false, | |
| 4006 | + "notNull": false | |
| 4007 | + }, | |
| 4008 | + "lead_sponsor": { | |
| 4009 | + "name": "lead_sponsor", | |
| 4010 | + "type": "text", | |
| 4011 | + "primaryKey": false, | |
| 4012 | + "notNull": false | |
| 4013 | + }, | |
| 4014 | + "lead_sponsor_class": { | |
| 4015 | + "name": "lead_sponsor_class", | |
| 4016 | + "type": "text", | |
| 4017 | + "primaryKey": false, | |
| 4018 | + "notNull": false | |
| 4019 | + }, | |
| 4020 | + "collaborators": { | |
| 4021 | + "name": "collaborators", | |
| 4022 | + "type": "text[]", | |
| 4023 | + "primaryKey": false, | |
| 4024 | + "notNull": true, | |
| 4025 | + "default": "'{}'" | |
| 4026 | + }, | |
| 4027 | + "conditions": { | |
| 4028 | + "name": "conditions", | |
| 4029 | + "type": "text[]", | |
| 4030 | + "primaryKey": false, | |
| 4031 | + "notNull": true, | |
| 4032 | + "default": "'{}'" | |
| 4033 | + }, | |
| 4034 | + "keywords": { | |
| 4035 | + "name": "keywords", | |
| 4036 | + "type": "text[]", | |
| 4037 | + "primaryKey": false, | |
| 4038 | + "notNull": true, | |
| 4039 | + "default": "'{}'" | |
| 4040 | + }, | |
| 4041 | + "interventions": { | |
| 4042 | + "name": "interventions", | |
| 4043 | + "type": "jsonb", | |
| 4044 | + "primaryKey": false, | |
| 4045 | + "notNull": true, | |
| 4046 | + "default": "'[]'::jsonb" | |
| 4047 | + }, | |
| 4048 | + "arms": { | |
| 4049 | + "name": "arms", | |
| 4050 | + "type": "jsonb", | |
| 4051 | + "primaryKey": false, | |
| 4052 | + "notNull": true, | |
| 4053 | + "default": "'[]'::jsonb" | |
| 4054 | + }, | |
| 4055 | + "primary_outcomes": { | |
| 4056 | + "name": "primary_outcomes", | |
| 4057 | + "type": "jsonb", | |
| 4058 | + "primaryKey": false, | |
| 4059 | + "notNull": true, | |
| 4060 | + "default": "'[]'::jsonb" | |
| 4061 | + }, | |
| 4062 | + "secondary_outcomes": { | |
| 4063 | + "name": "secondary_outcomes", | |
| 4064 | + "type": "jsonb", | |
| 4065 | + "primaryKey": false, | |
| 4066 | + "notNull": true, | |
| 4067 | + "default": "'[]'::jsonb" | |
| 4068 | + }, | |
| 4069 | + "eligibility": { | |
| 4070 | + "name": "eligibility", | |
| 4071 | + "type": "jsonb", | |
| 4072 | + "primaryKey": false, | |
| 4073 | + "notNull": true, | |
| 4074 | + "default": "'{}'::jsonb" | |
| 4075 | + }, | |
| 4076 | + "sex": { | |
| 4077 | + "name": "sex", | |
| 4078 | + "type": "text", | |
| 4079 | + "primaryKey": false, | |
| 4080 | + "notNull": false | |
| 4081 | + }, | |
| 4082 | + "minimum_age": { | |
| 4083 | + "name": "minimum_age", | |
| 4084 | + "type": "text", | |
| 4085 | + "primaryKey": false, | |
| 4086 | + "notNull": false | |
| 4087 | + }, | |
| 4088 | + "maximum_age": { | |
| 4089 | + "name": "maximum_age", | |
| 4090 | + "type": "text", | |
| 4091 | + "primaryKey": false, | |
| 4092 | + "notNull": false | |
| 4093 | + }, | |
| 4094 | + "countries": { | |
| 4095 | + "name": "countries", | |
| 4096 | + "type": "text[]", | |
| 4097 | + "primaryKey": false, | |
| 4098 | + "notNull": true, | |
| 4099 | + "default": "'{}'" | |
| 4100 | + }, | |
| 4101 | + "locations_count": { | |
| 4102 | + "name": "locations_count", | |
| 4103 | + "type": "integer", | |
| 4104 | + "primaryKey": false, | |
| 4105 | + "notNull": true, | |
| 4106 | + "default": 0 | |
| 4107 | + }, | |
| 4108 | + "references": { | |
| 4109 | + "name": "references", | |
| 4110 | + "type": "jsonb", | |
| 4111 | + "primaryKey": false, | |
| 4112 | + "notNull": true, | |
| 4113 | + "default": "'[]'::jsonb" | |
| 4114 | + }, | |
| 4115 | + "brief_summary": { | |
| 4116 | + "name": "brief_summary", | |
| 4117 | + "type": "text", | |
| 4118 | + "primaryKey": false, | |
| 4119 | + "notNull": false | |
| 4120 | + }, | |
| 4121 | + "is_oncology": { | |
| 4122 | + "name": "is_oncology", | |
| 4123 | + "type": "boolean", | |
| 4124 | + "primaryKey": false, | |
| 4125 | + "notNull": true, | |
| 4126 | + "default": true | |
| 4127 | + }, | |
| 4128 | + "source_record_id": { | |
| 4129 | + "name": "source_record_id", | |
| 4130 | + "type": "integer", | |
| 4131 | + "primaryKey": false, | |
| 4132 | + "notNull": false | |
| 4133 | + }, | |
| 4134 | + "ingest_run_id": { | |
| 4135 | + "name": "ingest_run_id", | |
| 4136 | + "type": "text", | |
| 4137 | + "primaryKey": false, | |
| 4138 | + "notNull": false | |
| 4139 | + }, | |
| 4140 | + "created_at": { | |
| 4141 | + "name": "created_at", | |
| 4142 | + "type": "timestamp with time zone", | |
| 4143 | + "primaryKey": false, | |
| 4144 | + "notNull": true, | |
| 4145 | + "default": "now()" | |
| 4146 | + }, | |
| 4147 | + "updated_at": { | |
| 4148 | + "name": "updated_at", | |
| 4149 | + "type": "timestamp with time zone", | |
| 4150 | + "primaryKey": false, | |
| 4151 | + "notNull": true, | |
| 4152 | + "default": "now()" | |
| 4153 | + } | |
| 4154 | + }, | |
| 4155 | + "indexes": { | |
| 4156 | + "clinical_trials_nct_uq": { | |
| 4157 | + "name": "clinical_trials_nct_uq", | |
| 4158 | + "columns": [ | |
| 4159 | + { | |
| 4160 | + "expression": "nct_id", | |
| 4161 | + "isExpression": false, | |
| 4162 | + "asc": true, | |
| 4163 | + "nulls": "last" | |
| 4164 | + } | |
| 4165 | + ], | |
| 4166 | + "isUnique": true, | |
| 4167 | + "concurrently": false, | |
| 4168 | + "method": "btree", | |
| 4169 | + "with": {} | |
| 4170 | + }, | |
| 4171 | + "clinical_trials_status_idx": { | |
| 4172 | + "name": "clinical_trials_status_idx", | |
| 4173 | + "columns": [ | |
| 4174 | + { | |
| 4175 | + "expression": "overall_status", | |
| 4176 | + "isExpression": false, | |
| 4177 | + "asc": true, | |
| 4178 | + "nulls": "last" | |
| 4179 | + } | |
| 4180 | + ], | |
| 4181 | + "isUnique": false, | |
| 4182 | + "concurrently": false, | |
| 4183 | + "method": "btree", | |
| 4184 | + "with": {} | |
| 4185 | + }, | |
| 4186 | + "clinical_trials_updated_idx": { | |
| 4187 | + "name": "clinical_trials_updated_idx", | |
| 4188 | + "columns": [ | |
| 4189 | + { | |
| 4190 | + "expression": "last_update_posted_date", | |
| 4191 | + "isExpression": false, | |
| 4192 | + "asc": true, | |
| 4193 | + "nulls": "last" | |
| 4194 | + } | |
| 4195 | + ], | |
| 4196 | + "isUnique": false, | |
| 4197 | + "concurrently": false, | |
| 4198 | + "method": "btree", | |
| 4199 | + "with": {} | |
| 4200 | + }, | |
| 4201 | + "clinical_trials_sponsor_idx": { | |
| 4202 | + "name": "clinical_trials_sponsor_idx", | |
| 4203 | + "columns": [ | |
| 4204 | + { | |
| 4205 | + "expression": "lead_sponsor", | |
| 4206 | + "isExpression": false, | |
| 4207 | + "asc": true, | |
| 4208 | + "nulls": "last" | |
| 4209 | + } | |
| 4210 | + ], | |
| 4211 | + "isUnique": false, | |
| 4212 | + "concurrently": false, | |
| 4213 | + "method": "btree", | |
| 4214 | + "with": {} | |
| 4215 | + } | |
| 4216 | + }, | |
| 4217 | + "foreignKeys": {}, | |
| 4218 | + "compositePrimaryKeys": {}, | |
| 4219 | + "uniqueConstraints": {}, | |
| 4220 | + "policies": {}, | |
| 4221 | + "checkConstraints": {}, | |
| 4222 | + "isRLSEnabled": false | |
| 4223 | + }, | |
| 4224 | + "public.trial_conditions": { | |
| 4225 | + "name": "trial_conditions", | |
| 4226 | + "schema": "", | |
| 4227 | + "columns": { | |
| 4228 | + "id": { | |
| 4229 | + "name": "id", | |
| 4230 | + "type": "bigserial", | |
| 4231 | + "primaryKey": true, | |
| 4232 | + "notNull": true | |
| 4233 | + }, | |
| 4234 | + "trial_id": { | |
| 4235 | + "name": "trial_id", | |
| 4236 | + "type": "varchar(32)", | |
| 4237 | + "primaryKey": false, | |
| 4238 | + "notNull": true | |
| 4239 | + }, | |
| 4240 | + "condition_text": { | |
| 4241 | + "name": "condition_text", | |
| 4242 | + "type": "text", | |
| 4243 | + "primaryKey": false, | |
| 4244 | + "notNull": true | |
| 4245 | + }, | |
| 4246 | + "normalized": { | |
| 4247 | + "name": "normalized", | |
| 4248 | + "type": "text", | |
| 4249 | + "primaryKey": false, | |
| 4250 | + "notNull": true | |
| 4251 | + }, | |
| 4252 | + "cancer_id": { | |
| 4253 | + "name": "cancer_id", | |
| 4254 | + "type": "varchar(32)", | |
| 4255 | + "primaryKey": false, | |
| 4256 | + "notNull": false | |
| 4257 | + }, | |
| 4258 | + "match_type": { | |
| 4259 | + "name": "match_type", | |
| 4260 | + "type": "text", | |
| 4261 | + "primaryKey": false, | |
| 4262 | + "notNull": true, | |
| 4263 | + "default": "'UNRESOLVED'" | |
| 4264 | + }, | |
| 4265 | + "confidence": { | |
| 4266 | + "name": "confidence", | |
| 4267 | + "type": "real", | |
| 4268 | + "primaryKey": false, | |
| 4269 | + "notNull": false | |
| 4270 | + } | |
| 4271 | + }, | |
| 4272 | + "indexes": { | |
| 4273 | + "trial_conditions_uq": { | |
| 4274 | + "name": "trial_conditions_uq", | |
| 4275 | + "columns": [ | |
| 4276 | + { | |
| 4277 | + "expression": "trial_id", | |
| 4278 | + "isExpression": false, | |
| 4279 | + "asc": true, | |
| 4280 | + "nulls": "last" | |
| 4281 | + }, | |
| 4282 | + { | |
| 4283 | + "expression": "normalized", | |
| 4284 | + "isExpression": false, | |
| 4285 | + "asc": true, | |
| 4286 | + "nulls": "last" | |
| 4287 | + } | |
| 4288 | + ], | |
| 4289 | + "isUnique": true, | |
| 4290 | + "concurrently": false, | |
| 4291 | + "method": "btree", | |
| 4292 | + "with": {} | |
| 4293 | + }, | |
| 4294 | + "trial_conditions_cancer_idx": { | |
| 4295 | + "name": "trial_conditions_cancer_idx", | |
| 4296 | + "columns": [ | |
| 4297 | + { | |
| 4298 | + "expression": "cancer_id", | |
| 4299 | + "isExpression": false, | |
| 4300 | + "asc": true, | |
| 4301 | + "nulls": "last" | |
| 4302 | + } | |
| 4303 | + ], | |
| 4304 | + "isUnique": false, | |
| 4305 | + "concurrently": false, | |
| 4306 | + "method": "btree", | |
| 4307 | + "with": {} | |
| 4308 | + }, | |
| 4309 | + "trial_conditions_norm_idx": { | |
| 4310 | + "name": "trial_conditions_norm_idx", | |
| 4311 | + "columns": [ | |
| 4312 | + { | |
| 4313 | + "expression": "normalized", | |
| 4314 | + "isExpression": false, | |
| 4315 | + "asc": true, | |
| 4316 | + "nulls": "last" | |
| 4317 | + } | |
| 4318 | + ], | |
| 4319 | + "isUnique": false, | |
| 4320 | + "concurrently": false, | |
| 4321 | + "method": "btree", | |
| 4322 | + "with": {} | |
| 4323 | + } | |
| 4324 | + }, | |
| 4325 | + "foreignKeys": {}, | |
| 4326 | + "compositePrimaryKeys": {}, | |
| 4327 | + "uniqueConstraints": {}, | |
| 4328 | + "policies": {}, | |
| 4329 | + "checkConstraints": {}, | |
| 4330 | + "isRLSEnabled": false | |
| 4331 | + }, | |
| 4332 | + "public.trial_interventions": { | |
| 4333 | + "name": "trial_interventions", | |
| 4334 | + "schema": "", | |
| 4335 | + "columns": { | |
| 4336 | + "id": { | |
| 4337 | + "name": "id", | |
| 4338 | + "type": "bigserial", | |
| 4339 | + "primaryKey": true, | |
| 4340 | + "notNull": true | |
| 4341 | + }, | |
| 4342 | + "trial_id": { | |
| 4343 | + "name": "trial_id", | |
| 4344 | + "type": "varchar(32)", | |
| 4345 | + "primaryKey": false, | |
| 4346 | + "notNull": true | |
| 4347 | + }, | |
| 4348 | + "name": { | |
| 4349 | + "name": "name", | |
| 4350 | + "type": "text", | |
| 4351 | + "primaryKey": false, | |
| 4352 | + "notNull": true | |
| 4353 | + }, | |
| 4354 | + "normalized": { | |
| 4355 | + "name": "normalized", | |
| 4356 | + "type": "text", | |
| 4357 | + "primaryKey": false, | |
| 4358 | + "notNull": true | |
| 4359 | + }, | |
| 4360 | + "intervention_type": { | |
| 4361 | + "name": "intervention_type", | |
| 4362 | + "type": "text", | |
| 4363 | + "primaryKey": false, | |
| 4364 | + "notNull": false | |
| 4365 | + }, | |
| 4366 | + "drug_id": { | |
| 4367 | + "name": "drug_id", | |
| 4368 | + "type": "varchar(32)", | |
| 4369 | + "primaryKey": false, | |
| 4370 | + "notNull": false | |
| 4371 | + }, | |
| 4372 | + "match_type": { | |
| 4373 | + "name": "match_type", | |
| 4374 | + "type": "text", | |
| 4375 | + "primaryKey": false, | |
| 4376 | + "notNull": true, | |
| 4377 | + "default": "'UNRESOLVED'" | |
| 4378 | + } | |
| 4379 | + }, | |
| 4380 | + "indexes": { | |
| 4381 | + "trial_interventions_uq": { | |
| 4382 | + "name": "trial_interventions_uq", | |
| 4383 | + "columns": [ | |
| 4384 | + { | |
| 4385 | + "expression": "trial_id", | |
| 4386 | + "isExpression": false, | |
| 4387 | + "asc": true, | |
| 4388 | + "nulls": "last" | |
| 4389 | + }, | |
| 4390 | + { | |
| 4391 | + "expression": "normalized", | |
| 4392 | + "isExpression": false, | |
| 4393 | + "asc": true, | |
| 4394 | + "nulls": "last" | |
| 4395 | + } | |
| 4396 | + ], | |
| 4397 | + "isUnique": true, | |
| 4398 | + "concurrently": false, | |
| 4399 | + "method": "btree", | |
| 4400 | + "with": {} | |
| 4401 | + }, | |
| 4402 | + "trial_interventions_drug_idx": { | |
| 4403 | + "name": "trial_interventions_drug_idx", | |
| 4404 | + "columns": [ | |
| 4405 | + { | |
| 4406 | + "expression": "drug_id", | |
| 4407 | + "isExpression": false, | |
| 4408 | + "asc": true, | |
| 4409 | + "nulls": "last" | |
| 4410 | + } | |
| 4411 | + ], | |
| 4412 | + "isUnique": false, | |
| 4413 | + "concurrently": false, | |
| 4414 | + "method": "btree", | |
| 4415 | + "with": {} | |
| 4416 | + } | |
| 4417 | + }, | |
| 4418 | + "foreignKeys": {}, | |
| 4419 | + "compositePrimaryKeys": {}, | |
| 4420 | + "uniqueConstraints": {}, | |
| 4421 | + "policies": {}, | |
| 4422 | + "checkConstraints": {}, | |
| 4423 | + "isRLSEnabled": false | |
| 4424 | + }, | |
| 4425 | + "public.trial_locations": { | |
| 4426 | + "name": "trial_locations", | |
| 4427 | + "schema": "", | |
| 4428 | + "columns": { | |
| 4429 | + "id": { | |
| 4430 | + "name": "id", | |
| 4431 | + "type": "bigserial", | |
| 4432 | + "primaryKey": true, | |
| 4433 | + "notNull": true | |
| 4434 | + }, | |
| 4435 | + "trial_id": { | |
| 4436 | + "name": "trial_id", | |
| 4437 | + "type": "varchar(32)", | |
| 4438 | + "primaryKey": false, | |
| 4439 | + "notNull": true | |
| 4440 | + }, | |
| 4441 | + "facility": { | |
| 4442 | + "name": "facility", | |
| 4443 | + "type": "text", | |
| 4444 | + "primaryKey": false, | |
| 4445 | + "notNull": false | |
| 4446 | + }, | |
| 4447 | + "city": { | |
| 4448 | + "name": "city", | |
| 4449 | + "type": "text", | |
| 4450 | + "primaryKey": false, | |
| 4451 | + "notNull": false | |
| 4452 | + }, | |
| 4453 | + "state": { | |
| 4454 | + "name": "state", | |
| 4455 | + "type": "text", | |
| 4456 | + "primaryKey": false, | |
| 4457 | + "notNull": false | |
| 4458 | + }, | |
| 4459 | + "zip": { | |
| 4460 | + "name": "zip", | |
| 4461 | + "type": "text", | |
| 4462 | + "primaryKey": false, | |
| 4463 | + "notNull": false | |
| 4464 | + }, | |
| 4465 | + "country": { | |
| 4466 | + "name": "country", | |
| 4467 | + "type": "text", | |
| 4468 | + "primaryKey": false, | |
| 4469 | + "notNull": false | |
| 4470 | + }, | |
| 4471 | + "status": { | |
| 4472 | + "name": "status", | |
| 4473 | + "type": "text", | |
| 4474 | + "primaryKey": false, | |
| 4475 | + "notNull": false | |
| 4476 | + }, | |
| 4477 | + "lat": { | |
| 4478 | + "name": "lat", | |
| 4479 | + "type": "real", | |
| 4480 | + "primaryKey": false, | |
| 4481 | + "notNull": false | |
| 4482 | + }, | |
| 4483 | + "lng": { | |
| 4484 | + "name": "lng", | |
| 4485 | + "type": "real", | |
| 4486 | + "primaryKey": false, | |
| 4487 | + "notNull": false | |
| 4488 | + } | |
| 4489 | + }, | |
| 4490 | + "indexes": { | |
| 4491 | + "trial_locations_trial_idx": { | |
| 4492 | + "name": "trial_locations_trial_idx", | |
| 4493 | + "columns": [ | |
| 4494 | + { | |
| 4495 | + "expression": "trial_id", | |
| 4496 | + "isExpression": false, | |
| 4497 | + "asc": true, | |
| 4498 | + "nulls": "last" | |
| 4499 | + } | |
| 4500 | + ], | |
| 4501 | + "isUnique": false, | |
| 4502 | + "concurrently": false, | |
| 4503 | + "method": "btree", | |
| 4504 | + "with": {} | |
| 4505 | + }, | |
| 4506 | + "trial_locations_country_idx": { | |
| 4507 | + "name": "trial_locations_country_idx", | |
| 4508 | + "columns": [ | |
| 4509 | + { | |
| 4510 | + "expression": "country", | |
| 4511 | + "isExpression": false, | |
| 4512 | + "asc": true, | |
| 4513 | + "nulls": "last" | |
| 4514 | + } | |
| 4515 | + ], | |
| 4516 | + "isUnique": false, | |
| 4517 | + "concurrently": false, | |
| 4518 | + "method": "btree", | |
| 4519 | + "with": {} | |
| 4520 | + } | |
| 4521 | + }, | |
| 4522 | + "foreignKeys": {}, | |
| 4523 | + "compositePrimaryKeys": {}, | |
| 4524 | + "uniqueConstraints": {}, | |
| 4525 | + "policies": {}, | |
| 4526 | + "checkConstraints": {}, | |
| 4527 | + "isRLSEnabled": false | |
| 4528 | + }, | |
| 4529 | + "public.trial_pulse": { | |
| 4530 | + "name": "trial_pulse", | |
| 4531 | + "schema": "", | |
| 4532 | + "columns": { | |
| 4533 | + "id": { | |
| 4534 | + "name": "id", | |
| 4535 | + "type": "bigserial", | |
| 4536 | + "primaryKey": true, | |
| 4537 | + "notNull": true | |
| 4538 | + }, | |
| 4539 | + "day": { | |
| 4540 | + "name": "day", | |
| 4541 | + "type": "date", | |
| 4542 | + "primaryKey": false, | |
| 4543 | + "notNull": true | |
| 4544 | + }, | |
| 4545 | + "cancer_id": { | |
| 4546 | + "name": "cancer_id", | |
| 4547 | + "type": "varchar(32)", | |
| 4548 | + "primaryKey": false, | |
| 4549 | + "notNull": false | |
| 4550 | + }, | |
| 4551 | + "phase": { | |
| 4552 | + "name": "phase", | |
| 4553 | + "type": "text", | |
| 4554 | + "primaryKey": false, | |
| 4555 | + "notNull": false | |
| 4556 | + }, | |
| 4557 | + "new_trials": { | |
| 4558 | + "name": "new_trials", | |
| 4559 | + "type": "integer", | |
| 4560 | + "primaryKey": false, | |
| 4561 | + "notNull": true | |
| 4562 | + }, | |
| 4563 | + "updated_at": { | |
| 4564 | + "name": "updated_at", | |
| 4565 | + "type": "timestamp with time zone", | |
| 4566 | + "primaryKey": false, | |
| 4567 | + "notNull": true, | |
| 4568 | + "default": "now()" | |
| 4569 | + } | |
| 4570 | + }, | |
| 4571 | + "indexes": { | |
| 4572 | + "trial_pulse_uq": { | |
| 4573 | + "name": "trial_pulse_uq", | |
| 4574 | + "columns": [ | |
| 4575 | + { | |
| 4576 | + "expression": "day", | |
| 4577 | + "isExpression": false, | |
| 4578 | + "asc": true, | |
| 4579 | + "nulls": "last" | |
| 4580 | + }, | |
| 4581 | + { | |
| 4582 | + "expression": "cancer_id", | |
| 4583 | + "isExpression": false, | |
| 4584 | + "asc": true, | |
| 4585 | + "nulls": "last" | |
| 4586 | + }, | |
| 4587 | + { | |
| 4588 | + "expression": "phase", | |
| 4589 | + "isExpression": false, | |
| 4590 | + "asc": true, | |
| 4591 | + "nulls": "last" | |
| 4592 | + } | |
| 4593 | + ], | |
| 4594 | + "isUnique": true, | |
| 4595 | + "concurrently": false, | |
| 4596 | + "method": "btree", | |
| 4597 | + "with": {} | |
| 4598 | + } | |
| 4599 | + }, | |
| 4600 | + "foreignKeys": {}, | |
| 4601 | + "compositePrimaryKeys": {}, | |
| 4602 | + "uniqueConstraints": {}, | |
| 4603 | + "policies": {}, | |
| 4604 | + "checkConstraints": {}, | |
| 4605 | + "isRLSEnabled": false | |
| 4606 | + }, | |
| 4607 | + "public.literature_counts": { | |
| 4608 | + "name": "literature_counts", | |
| 4609 | + "schema": "", | |
| 4610 | + "columns": { | |
| 4611 | + "id": { | |
| 4612 | + "name": "id", | |
| 4613 | + "type": "bigserial", | |
| 4614 | + "primaryKey": true, | |
| 4615 | + "notNull": true | |
| 4616 | + }, | |
| 4617 | + "cancer_id": { | |
| 4618 | + "name": "cancer_id", | |
| 4619 | + "type": "varchar(32)", | |
| 4620 | + "primaryKey": false, | |
| 4621 | + "notNull": true | |
| 4622 | + }, | |
| 4623 | + "window_key": { | |
| 4624 | + "name": "window_key", | |
| 4625 | + "type": "text", | |
| 4626 | + "primaryKey": false, | |
| 4627 | + "notNull": true | |
| 4628 | + }, | |
| 4629 | + "window_start": { | |
| 4630 | + "name": "window_start", | |
| 4631 | + "type": "text", | |
| 4632 | + "primaryKey": false, | |
| 4633 | + "notNull": false | |
| 4634 | + }, | |
| 4635 | + "window_end": { | |
| 4636 | + "name": "window_end", | |
| 4637 | + "type": "text", | |
| 4638 | + "primaryKey": false, | |
| 4639 | + "notNull": false | |
| 4640 | + }, | |
| 4641 | + "query": { | |
| 4642 | + "name": "query", | |
| 4643 | + "type": "text", | |
| 4644 | + "primaryKey": false, | |
| 4645 | + "notNull": true | |
| 4646 | + }, | |
| 4647 | + "count": { | |
| 4648 | + "name": "count", | |
| 4649 | + "type": "integer", | |
| 4650 | + "primaryKey": false, | |
| 4651 | + "notNull": true | |
| 4652 | + }, | |
| 4653 | + "provenance_id": { | |
| 4654 | + "name": "provenance_id", | |
| 4655 | + "type": "integer", | |
| 4656 | + "primaryKey": false, | |
| 4657 | + "notNull": true | |
| 4658 | + }, | |
| 4659 | + "updated_at": { | |
| 4660 | + "name": "updated_at", | |
| 4661 | + "type": "timestamp with time zone", | |
| 4662 | + "primaryKey": false, | |
| 4663 | + "notNull": true, | |
| 4664 | + "default": "now()" | |
| 4665 | + } | |
| 4666 | + }, | |
| 4667 | + "indexes": { | |
| 4668 | + "literature_counts_uq": { | |
| 4669 | + "name": "literature_counts_uq", | |
| 4670 | + "columns": [ | |
| 4671 | + { | |
| 4672 | + "expression": "cancer_id", | |
| 4673 | + "isExpression": false, | |
| 4674 | + "asc": true, | |
| 4675 | + "nulls": "last" | |
| 4676 | + }, | |
| 4677 | + { | |
| 4678 | + "expression": "window_key", | |
| 4679 | + "isExpression": false, | |
| 4680 | + "asc": true, | |
| 4681 | + "nulls": "last" | |
| 4682 | + } | |
| 4683 | + ], | |
| 4684 | + "isUnique": true, | |
| 4685 | + "concurrently": false, | |
| 4686 | + "method": "btree", | |
| 4687 | + "with": {} | |
| 4688 | + }, | |
| 4689 | + "literature_counts_window_idx": { | |
| 4690 | + "name": "literature_counts_window_idx", | |
| 4691 | + "columns": [ | |
| 4692 | + { | |
| 4693 | + "expression": "window_key", | |
| 4694 | + "isExpression": false, | |
| 4695 | + "asc": true, | |
| 4696 | + "nulls": "last" | |
| 4697 | + }, | |
| 4698 | + { | |
| 4699 | + "expression": "count", | |
| 4700 | + "isExpression": false, | |
| 4701 | + "asc": true, | |
| 4702 | + "nulls": "last" | |
| 4703 | + } | |
| 4704 | + ], | |
| 4705 | + "isUnique": false, | |
| 4706 | + "concurrently": false, | |
| 4707 | + "method": "btree", | |
| 4708 | + "with": {} | |
| 4709 | + } | |
| 4710 | + }, | |
| 4711 | + "foreignKeys": {}, | |
| 4712 | + "compositePrimaryKeys": {}, | |
| 4713 | + "uniqueConstraints": {}, | |
| 4714 | + "policies": {}, | |
| 4715 | + "checkConstraints": {}, | |
| 4716 | + "isRLSEnabled": false | |
| 4717 | + }, | |
| 4718 | + "public.publication_entity_edges": { | |
| 4719 | + "name": "publication_entity_edges", | |
| 4720 | + "schema": "", | |
| 4721 | + "columns": { | |
| 4722 | + "id": { | |
| 4723 | + "name": "id", | |
| 4724 | + "type": "bigserial", | |
| 4725 | + "primaryKey": true, | |
| 4726 | + "notNull": true | |
| 4727 | + }, | |
| 4728 | + "publication_id": { | |
| 4729 | + "name": "publication_id", | |
| 4730 | + "type": "varchar(32)", | |
| 4731 | + "primaryKey": false, | |
| 4732 | + "notNull": true | |
| 4733 | + }, | |
| 4734 | + "entity_type": { | |
| 4735 | + "name": "entity_type", | |
| 4736 | + "type": "text", | |
| 4737 | + "primaryKey": false, | |
| 4738 | + "notNull": true | |
| 4739 | + }, | |
| 4740 | + "entity_id": { | |
| 4741 | + "name": "entity_id", | |
| 4742 | + "type": "text", | |
| 4743 | + "primaryKey": false, | |
| 4744 | + "notNull": true | |
| 4745 | + }, | |
| 4746 | + "method": { | |
| 4747 | + "name": "method", | |
| 4748 | + "type": "text", | |
| 4749 | + "primaryKey": false, | |
| 4750 | + "notNull": true | |
| 4751 | + }, | |
| 4752 | + "confidence": { | |
| 4753 | + "name": "confidence", | |
| 4754 | + "type": "real", | |
| 4755 | + "primaryKey": false, | |
| 4756 | + "notNull": false | |
| 4757 | + }, | |
| 4758 | + "status": { | |
| 4759 | + "name": "status", | |
| 4760 | + "type": "text", | |
| 4761 | + "primaryKey": false, | |
| 4762 | + "notNull": true, | |
| 4763 | + "default": "'candidate'" | |
| 4764 | + }, | |
| 4765 | + "source_id": { | |
| 4766 | + "name": "source_id", | |
| 4767 | + "type": "varchar(32)", | |
| 4768 | + "primaryKey": false, | |
| 4769 | + "notNull": false | |
| 4770 | + }, | |
| 4771 | + "ingest_run_id": { | |
| 4772 | + "name": "ingest_run_id", | |
| 4773 | + "type": "text", | |
| 4774 | + "primaryKey": false, | |
| 4775 | + "notNull": false | |
| 4776 | + }, | |
| 4777 | + "created_at": { | |
| 4778 | + "name": "created_at", | |
| 4779 | + "type": "timestamp with time zone", | |
| 4780 | + "primaryKey": false, | |
| 4781 | + "notNull": true, | |
| 4782 | + "default": "now()" | |
| 4783 | + } | |
| 4784 | + }, | |
| 4785 | + "indexes": { | |
| 4786 | + "pub_entity_edges_uq": { | |
| 4787 | + "name": "pub_entity_edges_uq", | |
| 4788 | + "columns": [ | |
| 4789 | + { | |
| 4790 | + "expression": "publication_id", | |
| 4791 | + "isExpression": false, | |
| 4792 | + "asc": true, | |
| 4793 | + "nulls": "last" | |
| 4794 | + }, | |
| 4795 | + { | |
| 4796 | + "expression": "entity_type", | |
| 4797 | + "isExpression": false, | |
| 4798 | + "asc": true, | |
| 4799 | + "nulls": "last" | |
| 4800 | + }, | |
| 4801 | + { | |
| 4802 | + "expression": "entity_id", | |
| 4803 | + "isExpression": false, | |
| 4804 | + "asc": true, | |
| 4805 | + "nulls": "last" | |
| 4806 | + }, | |
| 4807 | + { | |
| 4808 | + "expression": "method", | |
| 4809 | + "isExpression": false, | |
| 4810 | + "asc": true, | |
| 4811 | + "nulls": "last" | |
| 4812 | + } | |
| 4813 | + ], | |
| 4814 | + "isUnique": true, | |
| 4815 | + "concurrently": false, | |
| 4816 | + "method": "btree", | |
| 4817 | + "with": {} | |
| 4818 | + }, | |
| 4819 | + "pub_entity_edges_entity_idx": { | |
| 4820 | + "name": "pub_entity_edges_entity_idx", | |
| 4821 | + "columns": [ | |
| 4822 | + { | |
| 4823 | + "expression": "entity_type", | |
| 4824 | + "isExpression": false, | |
| 4825 | + "asc": true, | |
| 4826 | + "nulls": "last" | |
| 4827 | + }, | |
| 4828 | + { | |
| 4829 | + "expression": "entity_id", | |
| 4830 | + "isExpression": false, | |
| 4831 | + "asc": true, | |
| 4832 | + "nulls": "last" | |
| 4833 | + } | |
| 4834 | + ], | |
| 4835 | + "isUnique": false, | |
| 4836 | + "concurrently": false, | |
| 4837 | + "method": "btree", | |
| 4838 | + "with": {} | |
| 4839 | + } | |
| 4840 | + }, | |
| 4841 | + "foreignKeys": {}, | |
| 4842 | + "compositePrimaryKeys": {}, | |
| 4843 | + "uniqueConstraints": {}, | |
| 4844 | + "policies": {}, | |
| 4845 | + "checkConstraints": {}, | |
| 4846 | + "isRLSEnabled": false | |
| 4847 | + }, | |
| 4848 | + "public.publications": { | |
| 4849 | + "name": "publications", | |
| 4850 | + "schema": "", | |
| 4851 | + "columns": { | |
| 4852 | + "id": { | |
| 4853 | + "name": "id", | |
| 4854 | + "type": "varchar(32)", | |
| 4855 | + "primaryKey": true, | |
| 4856 | + "notNull": true | |
| 4857 | + }, | |
| 4858 | + "pmid": { | |
| 4859 | + "name": "pmid", | |
| 4860 | + "type": "text", | |
| 4861 | + "primaryKey": false, | |
| 4862 | + "notNull": false | |
| 4863 | + }, | |
| 4864 | + "doi": { | |
| 4865 | + "name": "doi", | |
| 4866 | + "type": "text", | |
| 4867 | + "primaryKey": false, | |
| 4868 | + "notNull": false | |
| 4869 | + }, | |
| 4870 | + "pmcid": { | |
| 4871 | + "name": "pmcid", | |
| 4872 | + "type": "text", | |
| 4873 | + "primaryKey": false, | |
| 4874 | + "notNull": false | |
| 4875 | + }, | |
| 4876 | + "title": { | |
| 4877 | + "name": "title", | |
| 4878 | + "type": "text", | |
| 4879 | + "primaryKey": false, | |
| 4880 | + "notNull": true | |
| 4881 | + }, | |
| 4882 | + "abstract": { | |
| 4883 | + "name": "abstract", | |
| 4884 | + "type": "text", | |
| 4885 | + "primaryKey": false, | |
| 4886 | + "notNull": false | |
| 4887 | + }, | |
| 4888 | + "journal": { | |
| 4889 | + "name": "journal", | |
| 4890 | + "type": "text", | |
| 4891 | + "primaryKey": false, | |
| 4892 | + "notNull": false | |
| 4893 | + }, | |
| 4894 | + "journal_iso": { | |
| 4895 | + "name": "journal_iso", | |
| 4896 | + "type": "text", | |
| 4897 | + "primaryKey": false, | |
| 4898 | + "notNull": false | |
| 4899 | + }, | |
| 4900 | + "pub_date": { | |
| 4901 | + "name": "pub_date", | |
| 4902 | + "type": "text", | |
| 4903 | + "primaryKey": false, | |
| 4904 | + "notNull": false | |
| 4905 | + }, | |
| 4906 | + "pub_year": { | |
| 4907 | + "name": "pub_year", | |
| 4908 | + "type": "integer", | |
| 4909 | + "primaryKey": false, | |
| 4910 | + "notNull": false | |
| 4911 | + }, | |
| 4912 | + "publication_types": { | |
| 4913 | + "name": "publication_types", | |
| 4914 | + "type": "text[]", | |
| 4915 | + "primaryKey": false, | |
| 4916 | + "notNull": true, | |
| 4917 | + "default": "'{}'" | |
| 4918 | + }, | |
| 4919 | + "mesh_terms": { | |
| 4920 | + "name": "mesh_terms", | |
| 4921 | + "type": "jsonb", | |
| 4922 | + "primaryKey": false, | |
| 4923 | + "notNull": true, | |
| 4924 | + "default": "'[]'::jsonb" | |
| 4925 | + }, | |
| 4926 | + "authors": { | |
| 4927 | + "name": "authors", | |
| 4928 | + "type": "jsonb", | |
| 4929 | + "primaryKey": false, | |
| 4930 | + "notNull": true, | |
| 4931 | + "default": "'[]'::jsonb" | |
| 4932 | + }, | |
| 4933 | + "language": { | |
| 4934 | + "name": "language", | |
| 4935 | + "type": "text", | |
| 4936 | + "primaryKey": false, | |
| 4937 | + "notNull": false | |
| 4938 | + }, | |
| 4939 | + "is_preprint": { | |
| 4940 | + "name": "is_preprint", | |
| 4941 | + "type": "boolean", | |
| 4942 | + "primaryKey": false, | |
| 4943 | + "notNull": true, | |
| 4944 | + "default": false | |
| 4945 | + }, | |
| 4946 | + "retracted": { | |
| 4947 | + "name": "retracted", | |
| 4948 | + "type": "boolean", | |
| 4949 | + "primaryKey": false, | |
| 4950 | + "notNull": true, | |
| 4951 | + "default": false | |
| 4952 | + }, | |
| 4953 | + "retraction_notice": { | |
| 4954 | + "name": "retraction_notice", | |
| 4955 | + "type": "text", | |
| 4956 | + "primaryKey": false, | |
| 4957 | + "notNull": false | |
| 4958 | + }, | |
| 4959 | + "nct_ids": { | |
| 4960 | + "name": "nct_ids", | |
| 4961 | + "type": "text[]", | |
| 4962 | + "primaryKey": false, | |
| 4963 | + "notNull": true, | |
| 4964 | + "default": "'{}'" | |
| 4965 | + }, | |
| 4966 | + "cited_by_count": { | |
| 4967 | + "name": "cited_by_count", | |
| 4968 | + "type": "integer", | |
| 4969 | + "primaryKey": false, | |
| 4970 | + "notNull": false | |
| 4971 | + }, | |
| 4972 | + "source_record_id": { | |
| 4973 | + "name": "source_record_id", | |
| 4974 | + "type": "integer", | |
| 4975 | + "primaryKey": false, | |
| 4976 | + "notNull": false | |
| 4977 | + }, | |
| 4978 | + "ingest_run_id": { | |
| 4979 | + "name": "ingest_run_id", | |
| 4980 | + "type": "text", | |
| 4981 | + "primaryKey": false, | |
| 4982 | + "notNull": false | |
| 4983 | + }, | |
| 4984 | + "created_at": { | |
| 4985 | + "name": "created_at", | |
| 4986 | + "type": "timestamp with time zone", | |
| 4987 | + "primaryKey": false, | |
| 4988 | + "notNull": true, | |
| 4989 | + "default": "now()" | |
| 4990 | + }, | |
| 4991 | + "updated_at": { | |
| 4992 | + "name": "updated_at", | |
| 4993 | + "type": "timestamp with time zone", | |
| 4994 | + "primaryKey": false, | |
| 4995 | + "notNull": true, | |
| 4996 | + "default": "now()" | |
| 4997 | + } | |
| 4998 | + }, | |
| 4999 | + "indexes": { | |
| 5000 | + "publications_pmid_uq": { | |
| 5001 | + "name": "publications_pmid_uq", | |
| 5002 | + "columns": [ | |
| 5003 | + { | |
| 5004 | + "expression": "pmid", | |
| 5005 | + "isExpression": false, | |
| 5006 | + "asc": true, | |
| 5007 | + "nulls": "last" | |
| 5008 | + } | |
| 5009 | + ], | |
| 5010 | + "isUnique": true, | |
| 5011 | + "concurrently": false, | |
| 5012 | + "method": "btree", | |
| 5013 | + "with": {} | |
| 5014 | + }, | |
| 5015 | + "publications_doi_idx": { | |
| 5016 | + "name": "publications_doi_idx", | |
| 5017 | + "columns": [ | |
| 5018 | + { | |
| 5019 | + "expression": "doi", | |
| 5020 | + "isExpression": false, | |
| 5021 | + "asc": true, | |
| 5022 | + "nulls": "last" | |
| 5023 | + } | |
| 5024 | + ], | |
| 5025 | + "isUnique": false, | |
| 5026 | + "concurrently": false, | |
| 5027 | + "method": "btree", | |
| 5028 | + "with": {} | |
| 5029 | + }, | |
| 5030 | + "publications_year_idx": { | |
| 5031 | + "name": "publications_year_idx", | |
| 5032 | + "columns": [ | |
| 5033 | + { | |
| 5034 | + "expression": "pub_year", | |
| 5035 | + "isExpression": false, | |
| 5036 | + "asc": true, | |
| 5037 | + "nulls": "last" | |
| 5038 | + } | |
| 5039 | + ], | |
| 5040 | + "isUnique": false, | |
| 5041 | + "concurrently": false, | |
| 5042 | + "method": "btree", | |
| 5043 | + "with": {} | |
| 5044 | + }, | |
| 5045 | + "publications_retracted_idx": { | |
| 5046 | + "name": "publications_retracted_idx", | |
| 5047 | + "columns": [ | |
| 5048 | + { | |
| 5049 | + "expression": "retracted", | |
| 5050 | + "isExpression": false, | |
| 5051 | + "asc": true, | |
| 5052 | + "nulls": "last" | |
| 5053 | + } | |
| 5054 | + ], | |
| 5055 | + "isUnique": false, | |
| 5056 | + "concurrently": false, | |
| 5057 | + "method": "btree", | |
| 5058 | + "with": {} | |
| 5059 | + } | |
| 5060 | + }, | |
| 5061 | + "foreignKeys": {}, | |
| 5062 | + "compositePrimaryKeys": {}, | |
| 5063 | + "uniqueConstraints": {}, | |
| 5064 | + "policies": {}, | |
| 5065 | + "checkConstraints": {}, | |
| 5066 | + "isRLSEnabled": false | |
| 5067 | + }, | |
| 5068 | + "public.civic_evidence_items": { | |
| 5069 | + "name": "civic_evidence_items", | |
| 5070 | + "schema": "", | |
| 5071 | + "columns": { | |
| 5072 | + "id": { | |
| 5073 | + "name": "id", | |
| 5074 | + "type": "bigserial", | |
| 5075 | + "primaryKey": true, | |
| 5076 | + "notNull": true | |
| 5077 | + }, | |
| 5078 | + "civic_id": { | |
| 5079 | + "name": "civic_id", | |
| 5080 | + "type": "integer", | |
| 5081 | + "primaryKey": false, | |
| 5082 | + "notNull": true | |
| 5083 | + }, | |
| 5084 | + "name": { | |
| 5085 | + "name": "name", | |
| 5086 | + "type": "text", | |
| 5087 | + "primaryKey": false, | |
| 5088 | + "notNull": false | |
| 5089 | + }, | |
| 5090 | + "molecular_profile_id": { | |
| 5091 | + "name": "molecular_profile_id", | |
| 5092 | + "type": "integer", | |
| 5093 | + "primaryKey": false, | |
| 5094 | + "notNull": false | |
| 5095 | + }, | |
| 5096 | + "molecular_profile_name": { | |
| 5097 | + "name": "molecular_profile_name", | |
| 5098 | + "type": "text", | |
| 5099 | + "primaryKey": false, | |
| 5100 | + "notNull": false | |
| 5101 | + }, | |
| 5102 | + "gene_symbols": { | |
| 5103 | + "name": "gene_symbols", | |
| 5104 | + "type": "text[]", | |
| 5105 | + "primaryKey": false, | |
| 5106 | + "notNull": true, | |
| 5107 | + "default": "'{}'" | |
| 5108 | + }, | |
| 5109 | + "gene_ids": { | |
| 5110 | + "name": "gene_ids", | |
| 5111 | + "type": "text[]", | |
| 5112 | + "primaryKey": false, | |
| 5113 | + "notNull": true, | |
| 5114 | + "default": "'{}'" | |
| 5115 | + }, | |
| 5116 | + "variant_ids": { | |
| 5117 | + "name": "variant_ids", | |
| 5118 | + "type": "text[]", | |
| 5119 | + "primaryKey": false, | |
| 5120 | + "notNull": true, | |
| 5121 | + "default": "'{}'" | |
| 5122 | + }, | |
| 5123 | + "civic_variant_ids": { | |
| 5124 | + "name": "civic_variant_ids", | |
| 5125 | + "type": "integer[]", | |
| 5126 | + "primaryKey": false, | |
| 5127 | + "notNull": true, | |
| 5128 | + "default": "'{}'" | |
| 5129 | + }, | |
| 5130 | + "disease_name": { | |
| 5131 | + "name": "disease_name", | |
| 5132 | + "type": "text", | |
| 5133 | + "primaryKey": false, | |
| 5134 | + "notNull": false | |
| 5135 | + }, | |
| 5136 | + "doid": { | |
| 5137 | + "name": "doid", | |
| 5138 | + "type": "text", | |
| 5139 | + "primaryKey": false, | |
| 5140 | + "notNull": false | |
| 5141 | + }, | |
| 5142 | + "cancer_id": { | |
| 5143 | + "name": "cancer_id", | |
| 5144 | + "type": "varchar(32)", | |
| 5145 | + "primaryKey": false, | |
| 5146 | + "notNull": false | |
| 5147 | + }, | |
| 5148 | + "cancer_match_type": { | |
| 5149 | + "name": "cancer_match_type", | |
| 5150 | + "type": "text", | |
| 5151 | + "primaryKey": false, | |
| 5152 | + "notNull": false | |
| 5153 | + }, | |
| 5154 | + "therapy_names": { | |
| 5155 | + "name": "therapy_names", | |
| 5156 | + "type": "text[]", | |
| 5157 | + "primaryKey": false, | |
| 5158 | + "notNull": true, | |
| 5159 | + "default": "'{}'" | |
| 5160 | + }, | |
| 5161 | + "therapy_ids": { | |
| 5162 | + "name": "therapy_ids", | |
| 5163 | + "type": "text[]", | |
| 5164 | + "primaryKey": false, | |
| 5165 | + "notNull": true, | |
| 5166 | + "default": "'{}'" | |
| 5167 | + }, | |
| 5168 | + "therapy_interaction_type": { | |
| 5169 | + "name": "therapy_interaction_type", | |
| 5170 | + "type": "text", | |
| 5171 | + "primaryKey": false, | |
| 5172 | + "notNull": false | |
| 5173 | + }, | |
| 5174 | + "evidence_type": { | |
| 5175 | + "name": "evidence_type", | |
| 5176 | + "type": "text", | |
| 5177 | + "primaryKey": false, | |
| 5178 | + "notNull": false | |
| 5179 | + }, | |
| 5180 | + "evidence_level": { | |
| 5181 | + "name": "evidence_level", | |
| 5182 | + "type": "text", | |
| 5183 | + "primaryKey": false, | |
| 5184 | + "notNull": false | |
| 5185 | + }, | |
| 5186 | + "evidence_direction": { | |
| 5187 | + "name": "evidence_direction", | |
| 5188 | + "type": "text", | |
| 5189 | + "primaryKey": false, | |
| 5190 | + "notNull": false | |
| 5191 | + }, | |
| 5192 | + "significance": { | |
| 5193 | + "name": "significance", | |
| 5194 | + "type": "text", | |
| 5195 | + "primaryKey": false, | |
| 5196 | + "notNull": false | |
| 5197 | + }, | |
| 5198 | + "evidence_rating": { | |
| 5199 | + "name": "evidence_rating", | |
| 5200 | + "type": "integer", | |
| 5201 | + "primaryKey": false, | |
| 5202 | + "notNull": false | |
| 5203 | + }, | |
| 5204 | + "status": { | |
| 5205 | + "name": "status", | |
| 5206 | + "type": "text", | |
| 5207 | + "primaryKey": false, | |
| 5208 | + "notNull": false | |
| 5209 | + }, | |
| 5210 | + "description": { | |
| 5211 | + "name": "description", | |
| 5212 | + "type": "text", | |
| 5213 | + "primaryKey": false, | |
| 5214 | + "notNull": false | |
| 5215 | + }, | |
| 5216 | + "pmid": { | |
| 5217 | + "name": "pmid", | |
| 5218 | + "type": "text", | |
| 5219 | + "primaryKey": false, | |
| 5220 | + "notNull": false | |
| 5221 | + }, | |
| 5222 | + "source_citation": { | |
| 5223 | + "name": "source_citation", | |
| 5224 | + "type": "text", | |
| 5225 | + "primaryKey": false, | |
| 5226 | + "notNull": false | |
| 5227 | + }, | |
| 5228 | + "phenotypes": { | |
| 5229 | + "name": "phenotypes", | |
| 5230 | + "type": "text[]", | |
| 5231 | + "primaryKey": false, | |
| 5232 | + "notNull": true, | |
| 5233 | + "default": "'{}'" | |
| 5234 | + }, | |
| 5235 | + "provenance_id": { | |
| 5236 | + "name": "provenance_id", | |
| 5237 | + "type": "integer", | |
| 5238 | + "primaryKey": false, | |
| 5239 | + "notNull": true | |
| 5240 | + }, | |
| 5241 | + "ingest_run_id": { | |
| 5242 | + "name": "ingest_run_id", | |
| 5243 | + "type": "text", | |
| 5244 | + "primaryKey": false, | |
| 5245 | + "notNull": false | |
| 5246 | + }, | |
| 5247 | + "updated_at": { | |
| 5248 | + "name": "updated_at", | |
| 5249 | + "type": "timestamp with time zone", | |
| 5250 | + "primaryKey": false, | |
| 5251 | + "notNull": true, | |
| 5252 | + "default": "now()" | |
| 5253 | + } | |
| 5254 | + }, | |
| 5255 | + "indexes": { | |
| 5256 | + "civic_evidence_uq": { | |
| 5257 | + "name": "civic_evidence_uq", | |
| 5258 | + "columns": [ | |
| 5259 | + { | |
| 5260 | + "expression": "civic_id", | |
| 5261 | + "isExpression": false, | |
| 5262 | + "asc": true, | |
| 5263 | + "nulls": "last" | |
| 5264 | + } | |
| 5265 | + ], | |
| 5266 | + "isUnique": true, | |
| 5267 | + "concurrently": false, | |
| 5268 | + "method": "btree", | |
| 5269 | + "with": {} | |
| 5270 | + }, | |
| 5271 | + "civic_evidence_cancer_idx": { | |
| 5272 | + "name": "civic_evidence_cancer_idx", | |
| 5273 | + "columns": [ | |
| 5274 | + { | |
| 5275 | + "expression": "cancer_id", | |
| 5276 | + "isExpression": false, | |
| 5277 | + "asc": true, | |
| 5278 | + "nulls": "last" | |
| 5279 | + } | |
| 5280 | + ], | |
| 5281 | + "isUnique": false, | |
| 5282 | + "concurrently": false, | |
| 5283 | + "method": "btree", | |
| 5284 | + "with": {} | |
| 5285 | + }, | |
| 5286 | + "civic_evidence_gene_idx": { | |
| 5287 | + "name": "civic_evidence_gene_idx", | |
| 5288 | + "columns": [ | |
| 5289 | + { | |
| 5290 | + "expression": "gene_symbols", | |
| 5291 | + "isExpression": false, | |
| 5292 | + "asc": true, | |
| 5293 | + "nulls": "last" | |
| 5294 | + } | |
| 5295 | + ], | |
| 5296 | + "isUnique": false, | |
| 5297 | + "concurrently": false, | |
| 5298 | + "method": "btree", | |
| 5299 | + "with": {} | |
| 5300 | + } | |
| 5301 | + }, | |
| 5302 | + "foreignKeys": {}, | |
| 5303 | + "compositePrimaryKeys": {}, | |
| 5304 | + "uniqueConstraints": {}, | |
| 5305 | + "policies": {}, | |
| 5306 | + "checkConstraints": {}, | |
| 5307 | + "isRLSEnabled": false | |
| 5308 | + }, | |
| 5309 | + "public.knowledge_edges": { | |
| 5310 | + "name": "knowledge_edges", | |
| 5311 | + "schema": "", | |
| 5312 | + "columns": { | |
| 5313 | + "id": { | |
| 5314 | + "name": "id", | |
| 5315 | + "type": "bigserial", | |
| 5316 | + "primaryKey": true, | |
| 5317 | + "notNull": true | |
| 5318 | + }, | |
| 5319 | + "source_entity_type": { | |
| 5320 | + "name": "source_entity_type", | |
| 5321 | + "type": "text", | |
| 5322 | + "primaryKey": false, | |
| 5323 | + "notNull": true | |
| 5324 | + }, | |
| 5325 | + "source_entity_id": { | |
| 5326 | + "name": "source_entity_id", | |
| 5327 | + "type": "text", | |
| 5328 | + "primaryKey": false, | |
| 5329 | + "notNull": true | |
| 5330 | + }, | |
| 5331 | + "target_entity_type": { | |
| 5332 | + "name": "target_entity_type", | |
| 5333 | + "type": "text", | |
| 5334 | + "primaryKey": false, | |
| 5335 | + "notNull": true | |
| 5336 | + }, | |
| 5337 | + "target_entity_id": { | |
| 5338 | + "name": "target_entity_id", | |
| 5339 | + "type": "text", | |
| 5340 | + "primaryKey": false, | |
| 5341 | + "notNull": true | |
| 5342 | + }, | |
| 5343 | + "relationship_type": { | |
| 5344 | + "name": "relationship_type", | |
| 5345 | + "type": "text", | |
| 5346 | + "primaryKey": false, | |
| 5347 | + "notNull": true | |
| 5348 | + }, | |
| 5349 | + "cancer_context_ids": { | |
| 5350 | + "name": "cancer_context_ids", | |
| 5351 | + "type": "text[]", | |
| 5352 | + "primaryKey": false, | |
| 5353 | + "notNull": true, | |
| 5354 | + "default": "'{}'" | |
| 5355 | + }, | |
| 5356 | + "predictive": { | |
| 5357 | + "name": "predictive", | |
| 5358 | + "type": "boolean", | |
| 5359 | + "primaryKey": false, | |
| 5360 | + "notNull": false | |
| 5361 | + }, | |
| 5362 | + "prognostic": { | |
| 5363 | + "name": "prognostic", | |
| 5364 | + "type": "boolean", | |
| 5365 | + "primaryKey": false, | |
| 5366 | + "notNull": false | |
| 5367 | + }, | |
| 5368 | + "diagnostic": { | |
| 5369 | + "name": "diagnostic", | |
| 5370 | + "type": "boolean", | |
| 5371 | + "primaryKey": false, | |
| 5372 | + "notNull": false | |
| 5373 | + }, | |
| 5374 | + "predisposing": { | |
| 5375 | + "name": "predisposing", | |
| 5376 | + "type": "boolean", | |
| 5377 | + "primaryKey": false, | |
| 5378 | + "notNull": false | |
| 5379 | + }, | |
| 5380 | + "direction": { | |
| 5381 | + "name": "direction", | |
| 5382 | + "type": "text", | |
| 5383 | + "primaryKey": false, | |
| 5384 | + "notNull": false | |
| 5385 | + }, | |
| 5386 | + "evidence_level": { | |
| 5387 | + "name": "evidence_level", | |
| 5388 | + "type": "text", | |
| 5389 | + "primaryKey": false, | |
| 5390 | + "notNull": false | |
| 5391 | + }, | |
| 5392 | + "evidence_score": { | |
| 5393 | + "name": "evidence_score", | |
| 5394 | + "type": "real", | |
| 5395 | + "primaryKey": false, | |
| 5396 | + "notNull": false | |
| 5397 | + }, | |
| 5398 | + "evidence_category": { | |
| 5399 | + "name": "evidence_category", | |
| 5400 | + "type": "text", | |
| 5401 | + "primaryKey": false, | |
| 5402 | + "notNull": true, | |
| 5403 | + "default": "'curated_evidence'" | |
| 5404 | + }, | |
| 5405 | + "status": { | |
| 5406 | + "name": "status", | |
| 5407 | + "type": "text", | |
| 5408 | + "primaryKey": false, | |
| 5409 | + "notNull": true, | |
| 5410 | + "default": "'active'" | |
| 5411 | + }, | |
| 5412 | + "source_id": { | |
| 5413 | + "name": "source_id", | |
| 5414 | + "type": "varchar(32)", | |
| 5415 | + "primaryKey": false, | |
| 5416 | + "notNull": true | |
| 5417 | + }, | |
| 5418 | + "source_record_id": { | |
| 5419 | + "name": "source_record_id", | |
| 5420 | + "type": "text", | |
| 5421 | + "primaryKey": false, | |
| 5422 | + "notNull": false | |
| 5423 | + }, | |
| 5424 | + "provenance_ids": { | |
| 5425 | + "name": "provenance_ids", | |
| 5426 | + "type": "integer[]", | |
| 5427 | + "primaryKey": false, | |
| 5428 | + "notNull": true, | |
| 5429 | + "default": "'{}'" | |
| 5430 | + }, | |
| 5431 | + "support_count": { | |
| 5432 | + "name": "support_count", | |
| 5433 | + "type": "integer", | |
| 5434 | + "primaryKey": false, | |
| 5435 | + "notNull": true, | |
| 5436 | + "default": 1 | |
| 5437 | + }, | |
| 5438 | + "first_seen_at": { | |
| 5439 | + "name": "first_seen_at", | |
| 5440 | + "type": "timestamp with time zone", | |
| 5441 | + "primaryKey": false, | |
| 5442 | + "notNull": true, | |
| 5443 | + "default": "now()" | |
| 5444 | + }, | |
| 5445 | + "last_seen_at": { | |
| 5446 | + "name": "last_seen_at", | |
| 5447 | + "type": "timestamp with time zone", | |
| 5448 | + "primaryKey": false, | |
| 5449 | + "notNull": true, | |
| 5450 | + "default": "now()" | |
| 5451 | + } | |
| 5452 | + }, | |
| 5453 | + "indexes": { | |
| 5454 | + "knowledge_edges_uq": { | |
| 5455 | + "name": "knowledge_edges_uq", | |
| 5456 | + "columns": [ | |
| 5457 | + { | |
| 5458 | + "expression": "source_entity_type", | |
| 5459 | + "isExpression": false, | |
| 5460 | + "asc": true, | |
| 5461 | + "nulls": "last" | |
| 5462 | + }, | |
| 5463 | + { | |
| 5464 | + "expression": "source_entity_id", | |
| 5465 | + "isExpression": false, | |
| 5466 | + "asc": true, | |
| 5467 | + "nulls": "last" | |
| 5468 | + }, | |
| 5469 | + { | |
| 5470 | + "expression": "target_entity_type", | |
| 5471 | + "isExpression": false, | |
| 5472 | + "asc": true, | |
| 5473 | + "nulls": "last" | |
| 5474 | + }, | |
| 5475 | + { | |
| 5476 | + "expression": "target_entity_id", | |
| 5477 | + "isExpression": false, | |
| 5478 | + "asc": true, | |
| 5479 | + "nulls": "last" | |
| 5480 | + }, | |
| 5481 | + { | |
| 5482 | + "expression": "relationship_type", | |
| 5483 | + "isExpression": false, | |
| 5484 | + "asc": true, | |
| 5485 | + "nulls": "last" | |
| 5486 | + }, | |
| 5487 | + { | |
| 5488 | + "expression": "source_id", | |
| 5489 | + "isExpression": false, | |
| 5490 | + "asc": true, | |
| 5491 | + "nulls": "last" | |
| 5492 | + }, | |
| 5493 | + { | |
| 5494 | + "expression": "source_record_id", | |
| 5495 | + "isExpression": false, | |
| 5496 | + "asc": true, | |
| 5497 | + "nulls": "last" | |
| 5498 | + } | |
| 5499 | + ], | |
| 5500 | + "isUnique": true, | |
| 5501 | + "concurrently": false, | |
| 5502 | + "method": "btree", | |
| 5503 | + "with": {} | |
| 5504 | + }, | |
| 5505 | + "knowledge_edges_source_idx": { | |
| 5506 | + "name": "knowledge_edges_source_idx", | |
| 5507 | + "columns": [ | |
| 5508 | + { | |
| 5509 | + "expression": "source_entity_type", | |
| 5510 | + "isExpression": false, | |
| 5511 | + "asc": true, | |
| 5512 | + "nulls": "last" | |
| 5513 | + }, | |
| 5514 | + { | |
| 5515 | + "expression": "source_entity_id", | |
| 5516 | + "isExpression": false, | |
| 5517 | + "asc": true, | |
| 5518 | + "nulls": "last" | |
| 5519 | + }, | |
| 5520 | + { | |
| 5521 | + "expression": "relationship_type", | |
| 5522 | + "isExpression": false, | |
| 5523 | + "asc": true, | |
| 5524 | + "nulls": "last" | |
| 5525 | + } | |
| 5526 | + ], | |
| 5527 | + "isUnique": false, | |
| 5528 | + "concurrently": false, | |
| 5529 | + "method": "btree", | |
| 5530 | + "with": {} | |
| 5531 | + }, | |
| 5532 | + "knowledge_edges_target_idx": { | |
| 5533 | + "name": "knowledge_edges_target_idx", | |
| 5534 | + "columns": [ | |
| 5535 | + { | |
| 5536 | + "expression": "target_entity_type", | |
| 5537 | + "isExpression": false, | |
| 5538 | + "asc": true, | |
| 5539 | + "nulls": "last" | |
| 5540 | + }, | |
| 5541 | + { | |
| 5542 | + "expression": "target_entity_id", | |
| 5543 | + "isExpression": false, | |
| 5544 | + "asc": true, | |
| 5545 | + "nulls": "last" | |
| 5546 | + }, | |
| 5547 | + { | |
| 5548 | + "expression": "relationship_type", | |
| 5549 | + "isExpression": false, | |
| 5550 | + "asc": true, | |
| 5551 | + "nulls": "last" | |
| 5552 | + } | |
| 5553 | + ], | |
| 5554 | + "isUnique": false, | |
| 5555 | + "concurrently": false, | |
| 5556 | + "method": "btree", | |
| 5557 | + "with": {} | |
| 5558 | + } | |
| 5559 | + }, | |
| 5560 | + "foreignKeys": {}, | |
| 5561 | + "compositePrimaryKeys": {}, | |
| 5562 | + "uniqueConstraints": {}, | |
| 5563 | + "policies": {}, | |
| 5564 | + "checkConstraints": {}, | |
| 5565 | + "isRLSEnabled": false | |
| 5566 | + }, | |
| 5567 | + "public.risk_factors": { | |
| 5568 | + "name": "risk_factors", | |
| 5569 | + "schema": "", | |
| 5570 | + "columns": { | |
| 5571 | + "id": { | |
| 5572 | + "name": "id", | |
| 5573 | + "type": "bigserial", | |
| 5574 | + "primaryKey": true, | |
| 5575 | + "notNull": true | |
| 5576 | + }, | |
| 5577 | + "slug": { | |
| 5578 | + "name": "slug", | |
| 5579 | + "type": "text", | |
| 5580 | + "primaryKey": false, | |
| 5581 | + "notNull": true | |
| 5582 | + }, | |
| 5583 | + "name": { | |
| 5584 | + "name": "name", | |
| 5585 | + "type": "text", | |
| 5586 | + "primaryKey": false, | |
| 5587 | + "notNull": true | |
| 5588 | + }, | |
| 5589 | + "kind": { | |
| 5590 | + "name": "kind", | |
| 5591 | + "type": "text", | |
| 5592 | + "primaryKey": false, | |
| 5593 | + "notNull": true | |
| 5594 | + }, | |
| 5595 | + "classification_authority": { | |
| 5596 | + "name": "classification_authority", | |
| 5597 | + "type": "text", | |
| 5598 | + "primaryKey": false, | |
| 5599 | + "notNull": false | |
| 5600 | + }, | |
| 5601 | + "classification": { | |
| 5602 | + "name": "classification", | |
| 5603 | + "type": "text", | |
| 5604 | + "primaryKey": false, | |
| 5605 | + "notNull": false | |
| 5606 | + }, | |
| 5607 | + "description": { | |
| 5608 | + "name": "description", | |
| 5609 | + "type": "text", | |
| 5610 | + "primaryKey": false, | |
| 5611 | + "notNull": false | |
| 5612 | + }, | |
| 5613 | + "created_at": { | |
| 5614 | + "name": "created_at", | |
| 5615 | + "type": "timestamp with time zone", | |
| 5616 | + "primaryKey": false, | |
| 5617 | + "notNull": true, | |
| 5618 | + "default": "now()" | |
| 5619 | + } | |
| 5620 | + }, | |
| 5621 | + "indexes": { | |
| 5622 | + "risk_factors_slug_uq": { | |
| 5623 | + "name": "risk_factors_slug_uq", | |
| 5624 | + "columns": [ | |
| 5625 | + { | |
| 5626 | + "expression": "slug", | |
| 5627 | + "isExpression": false, | |
| 5628 | + "asc": true, | |
| 5629 | + "nulls": "last" | |
| 5630 | + } | |
| 5631 | + ], | |
| 5632 | + "isUnique": true, | |
| 5633 | + "concurrently": false, | |
| 5634 | + "method": "btree", | |
| 5635 | + "with": {} | |
| 5636 | + } | |
| 5637 | + }, | |
| 5638 | + "foreignKeys": {}, | |
| 5639 | + "compositePrimaryKeys": {}, | |
| 5640 | + "uniqueConstraints": {}, | |
| 5641 | + "policies": {}, | |
| 5642 | + "checkConstraints": {}, | |
| 5643 | + "isRLSEnabled": false | |
| 5644 | + }, | |
| 5645 | + "public.epidemiology_observations": { | |
| 5646 | + "name": "epidemiology_observations", | |
| 5647 | + "schema": "", | |
| 5648 | + "columns": { | |
| 5649 | + "id": { | |
| 5650 | + "name": "id", | |
| 5651 | + "type": "bigserial", | |
| 5652 | + "primaryKey": true, | |
| 5653 | + "notNull": true | |
| 5654 | + }, | |
| 5655 | + "cancer_id": { | |
| 5656 | + "name": "cancer_id", | |
| 5657 | + "type": "varchar(32)", | |
| 5658 | + "primaryKey": false, | |
| 5659 | + "notNull": true | |
| 5660 | + }, | |
| 5661 | + "geography_id": { | |
| 5662 | + "name": "geography_id", | |
| 5663 | + "type": "varchar(32)", | |
| 5664 | + "primaryKey": false, | |
| 5665 | + "notNull": true | |
| 5666 | + }, | |
| 5667 | + "year": { | |
| 5668 | + "name": "year", | |
| 5669 | + "type": "integer", | |
| 5670 | + "primaryKey": false, | |
| 5671 | + "notNull": true | |
| 5672 | + }, | |
| 5673 | + "year_end": { | |
| 5674 | + "name": "year_end", | |
| 5675 | + "type": "integer", | |
| 5676 | + "primaryKey": false, | |
| 5677 | + "notNull": false | |
| 5678 | + }, | |
| 5679 | + "sex": { | |
| 5680 | + "name": "sex", | |
| 5681 | + "type": "text", | |
| 5682 | + "primaryKey": false, | |
| 5683 | + "notNull": true, | |
| 5684 | + "default": "'all'" | |
| 5685 | + }, | |
| 5686 | + "age_group": { | |
| 5687 | + "name": "age_group", | |
| 5688 | + "type": "text", | |
| 5689 | + "primaryKey": false, | |
| 5690 | + "notNull": true, | |
| 5691 | + "default": "'all'" | |
| 5692 | + }, | |
| 5693 | + "metric": { | |
| 5694 | + "name": "metric", | |
| 5695 | + "type": "text", | |
| 5696 | + "primaryKey": false, | |
| 5697 | + "notNull": true | |
| 5698 | + }, | |
| 5699 | + "value": { | |
| 5700 | + "name": "value", | |
| 5701 | + "type": "double precision", | |
| 5702 | + "primaryKey": false, | |
| 5703 | + "notNull": true | |
| 5704 | + }, | |
| 5705 | + "unit": { | |
| 5706 | + "name": "unit", | |
| 5707 | + "type": "text", | |
| 5708 | + "primaryKey": false, | |
| 5709 | + "notNull": true | |
| 5710 | + }, | |
| 5711 | + "lower_ci": { | |
| 5712 | + "name": "lower_ci", | |
| 5713 | + "type": "double precision", | |
| 5714 | + "primaryKey": false, | |
| 5715 | + "notNull": false | |
| 5716 | + }, | |
| 5717 | + "upper_ci": { | |
| 5718 | + "name": "upper_ci", | |
| 5719 | + "type": "double precision", | |
| 5720 | + "primaryKey": false, | |
| 5721 | + "notNull": false | |
| 5722 | + }, | |
| 5723 | + "standard_population": { | |
| 5724 | + "name": "standard_population", | |
| 5725 | + "type": "text", | |
| 5726 | + "primaryKey": false, | |
| 5727 | + "notNull": false | |
| 5728 | + }, | |
| 5729 | + "estimate_type": { | |
| 5730 | + "name": "estimate_type", | |
| 5731 | + "type": "text", | |
| 5732 | + "primaryKey": false, | |
| 5733 | + "notNull": true, | |
| 5734 | + "default": "'observed'" | |
| 5735 | + }, | |
| 5736 | + "site_definition": { | |
| 5737 | + "name": "site_definition", | |
| 5738 | + "type": "text", | |
| 5739 | + "primaryKey": false, | |
| 5740 | + "notNull": false | |
| 5741 | + }, | |
| 5742 | + "source_id": { | |
| 5743 | + "name": "source_id", | |
| 5744 | + "type": "varchar(32)", | |
| 5745 | + "primaryKey": false, | |
| 5746 | + "notNull": true | |
| 5747 | + }, | |
| 5748 | + "provenance_id": { | |
| 5749 | + "name": "provenance_id", | |
| 5750 | + "type": "integer", | |
| 5751 | + "primaryKey": false, | |
| 5752 | + "notNull": true | |
| 5753 | + }, | |
| 5754 | + "ingest_run_id": { | |
| 5755 | + "name": "ingest_run_id", | |
| 5756 | + "type": "text", | |
| 5757 | + "primaryKey": false, | |
| 5758 | + "notNull": false | |
| 5759 | + }, | |
| 5760 | + "updated_at": { | |
| 5761 | + "name": "updated_at", | |
| 5762 | + "type": "timestamp with time zone", | |
| 5763 | + "primaryKey": false, | |
| 5764 | + "notNull": true, | |
| 5765 | + "default": "now()" | |
| 5766 | + } | |
| 5767 | + }, | |
| 5768 | + "indexes": { | |
| 5769 | + "epi_obs_uq": { | |
| 5770 | + "name": "epi_obs_uq", | |
| 5771 | + "columns": [ | |
| 5772 | + { | |
| 5773 | + "expression": "cancer_id", | |
| 5774 | + "isExpression": false, | |
| 5775 | + "asc": true, | |
| 5776 | + "nulls": "last" | |
| 5777 | + }, | |
| 5778 | + { | |
| 5779 | + "expression": "geography_id", | |
| 5780 | + "isExpression": false, | |
| 5781 | + "asc": true, | |
| 5782 | + "nulls": "last" | |
| 5783 | + }, | |
| 5784 | + { | |
| 5785 | + "expression": "year", | |
| 5786 | + "isExpression": false, | |
| 5787 | + "asc": true, | |
| 5788 | + "nulls": "last" | |
| 5789 | + }, | |
| 5790 | + { | |
| 5791 | + "expression": "sex", | |
| 5792 | + "isExpression": false, | |
| 5793 | + "asc": true, | |
| 5794 | + "nulls": "last" | |
| 5795 | + }, | |
| 5796 | + { | |
| 5797 | + "expression": "age_group", | |
| 5798 | + "isExpression": false, | |
| 5799 | + "asc": true, | |
| 5800 | + "nulls": "last" | |
| 5801 | + }, | |
| 5802 | + { | |
| 5803 | + "expression": "metric", | |
| 5804 | + "isExpression": false, | |
| 5805 | + "asc": true, | |
| 5806 | + "nulls": "last" | |
| 5807 | + }, | |
| 5808 | + { | |
| 5809 | + "expression": "source_id", | |
| 5810 | + "isExpression": false, | |
| 5811 | + "asc": true, | |
| 5812 | + "nulls": "last" | |
| 5813 | + }, | |
| 5814 | + { | |
| 5815 | + "expression": "site_definition", | |
| 5816 | + "isExpression": false, | |
| 5817 | + "asc": true, | |
| 5818 | + "nulls": "last" | |
| 5819 | + } | |
| 5820 | + ], | |
| 5821 | + "isUnique": true, | |
| 5822 | + "concurrently": false, | |
| 5823 | + "method": "btree", | |
| 5824 | + "with": {} | |
| 5825 | + }, | |
| 5826 | + "epi_obs_lookup_idx": { | |
| 5827 | + "name": "epi_obs_lookup_idx", | |
| 5828 | + "columns": [ | |
| 5829 | + { | |
| 5830 | + "expression": "metric", | |
| 5831 | + "isExpression": false, | |
| 5832 | + "asc": true, | |
| 5833 | + "nulls": "last" | |
| 5834 | + }, | |
| 5835 | + { | |
| 5836 | + "expression": "geography_id", | |
| 5837 | + "isExpression": false, | |
| 5838 | + "asc": true, | |
| 5839 | + "nulls": "last" | |
| 5840 | + }, | |
| 5841 | + { | |
| 5842 | + "expression": "year", | |
| 5843 | + "isExpression": false, | |
| 5844 | + "asc": true, | |
| 5845 | + "nulls": "last" | |
| 5846 | + }, | |
| 5847 | + { | |
| 5848 | + "expression": "sex", | |
| 5849 | + "isExpression": false, | |
| 5850 | + "asc": true, | |
| 5851 | + "nulls": "last" | |
| 5852 | + } | |
| 5853 | + ], | |
| 5854 | + "isUnique": false, | |
| 5855 | + "concurrently": false, | |
| 5856 | + "method": "btree", | |
| 5857 | + "with": {} | |
| 5858 | + }, | |
| 5859 | + "epi_obs_cancer_idx": { | |
| 5860 | + "name": "epi_obs_cancer_idx", | |
| 5861 | + "columns": [ | |
| 5862 | + { | |
| 5863 | + "expression": "cancer_id", | |
| 5864 | + "isExpression": false, | |
| 5865 | + "asc": true, | |
| 5866 | + "nulls": "last" | |
| 5867 | + }, | |
| 5868 | + { | |
| 5869 | + "expression": "metric", | |
| 5870 | + "isExpression": false, | |
| 5871 | + "asc": true, | |
| 5872 | + "nulls": "last" | |
| 5873 | + } | |
| 5874 | + ], | |
| 5875 | + "isUnique": false, | |
| 5876 | + "concurrently": false, | |
| 5877 | + "method": "btree", | |
| 5878 | + "with": {} | |
| 5879 | + } | |
| 5880 | + }, | |
| 5881 | + "foreignKeys": {}, | |
| 5882 | + "compositePrimaryKeys": {}, | |
| 5883 | + "uniqueConstraints": {}, | |
| 5884 | + "policies": {}, | |
| 5885 | + "checkConstraints": {}, | |
| 5886 | + "isRLSEnabled": false | |
| 5887 | + }, | |
| 5888 | + "public.survival_observations": { | |
| 5889 | + "name": "survival_observations", | |
| 5890 | + "schema": "", | |
| 5891 | + "columns": { | |
| 5892 | + "id": { | |
| 5893 | + "name": "id", | |
| 5894 | + "type": "bigserial", | |
| 5895 | + "primaryKey": true, | |
| 5896 | + "notNull": true | |
| 5897 | + }, | |
| 5898 | + "cancer_id": { | |
| 5899 | + "name": "cancer_id", | |
| 5900 | + "type": "varchar(32)", | |
| 5901 | + "primaryKey": false, | |
| 5902 | + "notNull": true | |
| 5903 | + }, | |
| 5904 | + "geography_id": { | |
| 5905 | + "name": "geography_id", | |
| 5906 | + "type": "varchar(32)", | |
| 5907 | + "primaryKey": false, | |
| 5908 | + "notNull": false | |
| 5909 | + }, | |
| 5910 | + "stage": { | |
| 5911 | + "name": "stage", | |
| 5912 | + "type": "text", | |
| 5913 | + "primaryKey": false, | |
| 5914 | + "notNull": false | |
| 5915 | + }, | |
| 5916 | + "staging_system": { | |
| 5917 | + "name": "staging_system", | |
| 5918 | + "type": "text", | |
| 5919 | + "primaryKey": false, | |
| 5920 | + "notNull": false | |
| 5921 | + }, | |
| 5922 | + "sex": { | |
| 5923 | + "name": "sex", | |
| 5924 | + "type": "text", | |
| 5925 | + "primaryKey": false, | |
| 5926 | + "notNull": true, | |
| 5927 | + "default": "'all'" | |
| 5928 | + }, | |
| 5929 | + "age_group": { | |
| 5930 | + "name": "age_group", | |
| 5931 | + "type": "text", | |
| 5932 | + "primaryKey": false, | |
| 5933 | + "notNull": true, | |
| 5934 | + "default": "'all'" | |
| 5935 | + }, | |
| 5936 | + "diagnosis_period": { | |
| 5937 | + "name": "diagnosis_period", | |
| 5938 | + "type": "text", | |
| 5939 | + "primaryKey": false, | |
| 5940 | + "notNull": false | |
| 5941 | + }, | |
| 5942 | + "survival_type": { | |
| 5943 | + "name": "survival_type", | |
| 5944 | + "type": "text", | |
| 5945 | + "primaryKey": false, | |
| 5946 | + "notNull": true | |
| 5947 | + }, | |
| 5948 | + "duration_months": { | |
| 5949 | + "name": "duration_months", | |
| 5950 | + "type": "integer", | |
| 5951 | + "primaryKey": false, | |
| 5952 | + "notNull": true | |
| 5953 | + }, | |
| 5954 | + "probability": { | |
| 5955 | + "name": "probability", | |
| 5956 | + "type": "real", | |
| 5957 | + "primaryKey": false, | |
| 5958 | + "notNull": false | |
| 5959 | + }, | |
| 5960 | + "median_months": { | |
| 5961 | + "name": "median_months", | |
| 5962 | + "type": "real", | |
| 5963 | + "primaryKey": false, | |
| 5964 | + "notNull": false | |
| 5965 | + }, | |
| 5966 | + "cohort_size": { | |
| 5967 | + "name": "cohort_size", | |
| 5968 | + "type": "integer", | |
| 5969 | + "primaryKey": false, | |
| 5970 | + "notNull": false | |
| 5971 | + }, | |
| 5972 | + "lower_ci": { | |
| 5973 | + "name": "lower_ci", | |
| 5974 | + "type": "real", | |
| 5975 | + "primaryKey": false, | |
| 5976 | + "notNull": false | |
| 5977 | + }, | |
| 5978 | + "upper_ci": { | |
| 5979 | + "name": "upper_ci", | |
| 5980 | + "type": "real", | |
| 5981 | + "primaryKey": false, | |
| 5982 | + "notNull": false | |
| 5983 | + }, | |
| 5984 | + "method": { | |
| 5985 | + "name": "method", | |
| 5986 | + "type": "text", | |
| 5987 | + "primaryKey": false, | |
| 5988 | + "notNull": false | |
| 5989 | + }, | |
| 5990 | + "source_id": { | |
| 5991 | + "name": "source_id", | |
| 5992 | + "type": "varchar(32)", | |
| 5993 | + "primaryKey": false, | |
| 5994 | + "notNull": true | |
| 5995 | + }, | |
| 5996 | + "provenance_id": { | |
| 5997 | + "name": "provenance_id", | |
| 5998 | + "type": "integer", | |
| 5999 | + "primaryKey": false, | |
| 6000 | + "notNull": true | |
| 6001 | + }, | |
| 6002 | + "ingest_run_id": { | |
| 6003 | + "name": "ingest_run_id", | |
| 6004 | + "type": "text", | |
| 6005 | + "primaryKey": false, | |
| 6006 | + "notNull": false | |
| 6007 | + }, | |
| 6008 | + "updated_at": { | |
| 6009 | + "name": "updated_at", | |
| 6010 | + "type": "timestamp with time zone", | |
| 6011 | + "primaryKey": false, | |
| 6012 | + "notNull": true, | |
| 6013 | + "default": "now()" | |
| 6014 | + } | |
| 6015 | + }, | |
| 6016 | + "indexes": { | |
| 6017 | + "survival_obs_cancer_idx": { | |
| 6018 | + "name": "survival_obs_cancer_idx", | |
| 6019 | + "columns": [ | |
| 6020 | + { | |
| 6021 | + "expression": "cancer_id", | |
| 6022 | + "isExpression": false, | |
| 6023 | + "asc": true, | |
| 6024 | + "nulls": "last" | |
| 6025 | + }, | |
| 6026 | + { | |
| 6027 | + "expression": "survival_type", | |
| 6028 | + "isExpression": false, | |
| 6029 | + "asc": true, | |
| 6030 | + "nulls": "last" | |
| 6031 | + }, | |
| 6032 | + { | |
| 6033 | + "expression": "duration_months", | |
| 6034 | + "isExpression": false, | |
| 6035 | + "asc": true, | |
| 6036 | + "nulls": "last" | |
| 6037 | + } | |
| 6038 | + ], | |
| 6039 | + "isUnique": false, | |
| 6040 | + "concurrently": false, | |
| 6041 | + "method": "btree", | |
| 6042 | + "with": {} | |
| 6043 | + } | |
| 6044 | + }, | |
| 6045 | + "foreignKeys": {}, | |
| 6046 | + "compositePrimaryKeys": {}, | |
| 6047 | + "uniqueConstraints": {}, | |
| 6048 | + "policies": {}, | |
| 6049 | + "checkConstraints": {}, | |
| 6050 | + "isRLSEnabled": false | |
| 6051 | + }, | |
| 6052 | + "public.ai_answers": { | |
| 6053 | + "name": "ai_answers", | |
| 6054 | + "schema": "", | |
| 6055 | + "columns": { | |
| 6056 | + "id": { | |
| 6057 | + "name": "id", | |
| 6058 | + "type": "bigserial", | |
| 6059 | + "primaryKey": true, | |
| 6060 | + "notNull": true | |
| 6061 | + }, | |
| 6062 | + "kind": { | |
| 6063 | + "name": "kind", | |
| 6064 | + "type": "text", | |
| 6065 | + "primaryKey": false, | |
| 6066 | + "notNull": true | |
| 6067 | + }, | |
| 6068 | + "subject_id": { | |
| 6069 | + "name": "subject_id", | |
| 6070 | + "type": "text", | |
| 6071 | + "primaryKey": false, | |
| 6072 | + "notNull": false | |
| 6073 | + }, | |
| 6074 | + "question_hash": { | |
| 6075 | + "name": "question_hash", | |
| 6076 | + "type": "text", | |
| 6077 | + "primaryKey": false, | |
| 6078 | + "notNull": true | |
| 6079 | + }, | |
| 6080 | + "question": { | |
| 6081 | + "name": "question", | |
| 6082 | + "type": "text", | |
| 6083 | + "primaryKey": false, | |
| 6084 | + "notNull": false | |
| 6085 | + }, | |
| 6086 | + "answer": { | |
| 6087 | + "name": "answer", | |
| 6088 | + "type": "jsonb", | |
| 6089 | + "primaryKey": false, | |
| 6090 | + "notNull": true | |
| 6091 | + }, | |
| 6092 | + "model": { | |
| 6093 | + "name": "model", | |
| 6094 | + "type": "text", | |
| 6095 | + "primaryKey": false, | |
| 6096 | + "notNull": true | |
| 6097 | + }, | |
| 6098 | + "prompt_version": { | |
| 6099 | + "name": "prompt_version", | |
| 6100 | + "type": "text", | |
| 6101 | + "primaryKey": false, | |
| 6102 | + "notNull": true | |
| 6103 | + }, | |
| 6104 | + "source_snapshot": { | |
| 6105 | + "name": "source_snapshot", | |
| 6106 | + "type": "jsonb", | |
| 6107 | + "primaryKey": false, | |
| 6108 | + "notNull": true, | |
| 6109 | + "default": "'{}'::jsonb" | |
| 6110 | + }, | |
| 6111 | + "data_as_of": { | |
| 6112 | + "name": "data_as_of", | |
| 6113 | + "type": "timestamp with time zone", | |
| 6114 | + "primaryKey": false, | |
| 6115 | + "notNull": true | |
| 6116 | + }, | |
| 6117 | + "created_at": { | |
| 6118 | + "name": "created_at", | |
| 6119 | + "type": "timestamp with time zone", | |
| 6120 | + "primaryKey": false, | |
| 6121 | + "notNull": true, | |
| 6122 | + "default": "now()" | |
| 6123 | + } | |
| 6124 | + }, | |
| 6125 | + "indexes": { | |
| 6126 | + "ai_answers_uq": { | |
| 6127 | + "name": "ai_answers_uq", | |
| 6128 | + "columns": [ | |
| 6129 | + { | |
| 6130 | + "expression": "kind", | |
| 6131 | + "isExpression": false, | |
| 6132 | + "asc": true, | |
| 6133 | + "nulls": "last" | |
| 6134 | + }, | |
| 6135 | + { | |
| 6136 | + "expression": "question_hash", | |
| 6137 | + "isExpression": false, | |
| 6138 | + "asc": true, | |
| 6139 | + "nulls": "last" | |
| 6140 | + }, | |
| 6141 | + { | |
| 6142 | + "expression": "prompt_version", | |
| 6143 | + "isExpression": false, | |
| 6144 | + "asc": true, | |
| 6145 | + "nulls": "last" | |
| 6146 | + } | |
| 6147 | + ], | |
| 6148 | + "isUnique": true, | |
| 6149 | + "concurrently": false, | |
| 6150 | + "method": "btree", | |
| 6151 | + "with": {} | |
| 6152 | + } | |
| 6153 | + }, | |
| 6154 | + "foreignKeys": {}, | |
| 6155 | + "compositePrimaryKeys": {}, | |
| 6156 | + "uniqueConstraints": {}, | |
| 6157 | + "policies": {}, | |
| 6158 | + "checkConstraints": {}, | |
| 6159 | + "isRLSEnabled": false | |
| 6160 | + }, | |
| 6161 | + "public.api_keys": { | |
| 6162 | + "name": "api_keys", | |
| 6163 | + "schema": "", | |
| 6164 | + "columns": { | |
| 6165 | + "id": { | |
| 6166 | + "name": "id", | |
| 6167 | + "type": "bigserial", | |
| 6168 | + "primaryKey": true, | |
| 6169 | + "notNull": true | |
| 6170 | + }, | |
| 6171 | + "key_hash": { | |
| 6172 | + "name": "key_hash", | |
| 6173 | + "type": "text", | |
| 6174 | + "primaryKey": false, | |
| 6175 | + "notNull": true | |
| 6176 | + }, | |
| 6177 | + "prefix": { | |
| 6178 | + "name": "prefix", | |
| 6179 | + "type": "text", | |
| 6180 | + "primaryKey": false, | |
| 6181 | + "notNull": true | |
| 6182 | + }, | |
| 6183 | + "label": { | |
| 6184 | + "name": "label", | |
| 6185 | + "type": "text", | |
| 6186 | + "primaryKey": false, | |
| 6187 | + "notNull": false | |
| 6188 | + }, | |
| 6189 | + "owner_email": { | |
| 6190 | + "name": "owner_email", | |
| 6191 | + "type": "text", | |
| 6192 | + "primaryKey": false, | |
| 6193 | + "notNull": false | |
| 6194 | + }, | |
| 6195 | + "tier": { | |
| 6196 | + "name": "tier", | |
| 6197 | + "type": "text", | |
| 6198 | + "primaryKey": false, | |
| 6199 | + "notNull": true, | |
| 6200 | + "default": "'free'" | |
| 6201 | + }, | |
| 6202 | + "rate_limit_per_minute": { | |
| 6203 | + "name": "rate_limit_per_minute", | |
| 6204 | + "type": "integer", | |
| 6205 | + "primaryKey": false, | |
| 6206 | + "notNull": true, | |
| 6207 | + "default": 60 | |
| 6208 | + }, | |
| 6209 | + "active": { | |
| 6210 | + "name": "active", | |
| 6211 | + "type": "boolean", | |
| 6212 | + "primaryKey": false, | |
| 6213 | + "notNull": true, | |
| 6214 | + "default": true | |
| 6215 | + }, | |
| 6216 | + "last_used_at": { | |
| 6217 | + "name": "last_used_at", | |
| 6218 | + "type": "timestamp with time zone", | |
| 6219 | + "primaryKey": false, | |
| 6220 | + "notNull": false | |
| 6221 | + }, | |
| 6222 | + "created_at": { | |
| 6223 | + "name": "created_at", | |
| 6224 | + "type": "timestamp with time zone", | |
| 6225 | + "primaryKey": false, | |
| 6226 | + "notNull": true, | |
| 6227 | + "default": "now()" | |
| 6228 | + } | |
| 6229 | + }, | |
| 6230 | + "indexes": { | |
| 6231 | + "api_keys_hash_uq": { | |
| 6232 | + "name": "api_keys_hash_uq", | |
| 6233 | + "columns": [ | |
| 6234 | + { | |
| 6235 | + "expression": "key_hash", | |
| 6236 | + "isExpression": false, | |
| 6237 | + "asc": true, | |
| 6238 | + "nulls": "last" | |
| 6239 | + } | |
| 6240 | + ], | |
| 6241 | + "isUnique": true, | |
| 6242 | + "concurrently": false, | |
| 6243 | + "method": "btree", | |
| 6244 | + "with": {} | |
| 6245 | + } | |
| 6246 | + }, | |
| 6247 | + "foreignKeys": {}, | |
| 6248 | + "compositePrimaryKeys": {}, | |
| 6249 | + "uniqueConstraints": {}, | |
| 6250 | + "policies": {}, | |
| 6251 | + "checkConstraints": {}, | |
| 6252 | + "isRLSEnabled": false | |
| 6253 | + }, | |
| 6254 | + "public.entity_counters": { | |
| 6255 | + "name": "entity_counters", | |
| 6256 | + "schema": "", | |
| 6257 | + "columns": { | |
| 6258 | + "id": { | |
| 6259 | + "name": "id", | |
| 6260 | + "type": "bigserial", | |
| 6261 | + "primaryKey": true, | |
| 6262 | + "notNull": true | |
| 6263 | + }, | |
| 6264 | + "entity_type": { | |
| 6265 | + "name": "entity_type", | |
| 6266 | + "type": "text", | |
| 6267 | + "primaryKey": false, | |
| 6268 | + "notNull": true | |
| 6269 | + }, | |
| 6270 | + "entity_id": { | |
| 6271 | + "name": "entity_id", | |
| 6272 | + "type": "text", | |
| 6273 | + "primaryKey": false, | |
| 6274 | + "notNull": true | |
| 6275 | + }, | |
| 6276 | + "trial_count": { | |
| 6277 | + "name": "trial_count", | |
| 6278 | + "type": "integer", | |
| 6279 | + "primaryKey": false, | |
| 6280 | + "notNull": true, | |
| 6281 | + "default": 0 | |
| 6282 | + }, | |
| 6283 | + "active_trial_count": { | |
| 6284 | + "name": "active_trial_count", | |
| 6285 | + "type": "integer", | |
| 6286 | + "primaryKey": false, | |
| 6287 | + "notNull": true, | |
| 6288 | + "default": 0 | |
| 6289 | + }, | |
| 6290 | + "recruiting_trial_count": { | |
| 6291 | + "name": "recruiting_trial_count", | |
| 6292 | + "type": "integer", | |
| 6293 | + "primaryKey": false, | |
| 6294 | + "notNull": true, | |
| 6295 | + "default": 0 | |
| 6296 | + }, | |
| 6297 | + "phase3_trial_count": { | |
| 6298 | + "name": "phase3_trial_count", | |
| 6299 | + "type": "integer", | |
| 6300 | + "primaryKey": false, | |
| 6301 | + "notNull": true, | |
| 6302 | + "default": 0 | |
| 6303 | + }, | |
| 6304 | + "publication_count": { | |
| 6305 | + "name": "publication_count", | |
| 6306 | + "type": "integer", | |
| 6307 | + "primaryKey": false, | |
| 6308 | + "notNull": true, | |
| 6309 | + "default": 0 | |
| 6310 | + }, | |
| 6311 | + "publication_count_5y": { | |
| 6312 | + "name": "publication_count_5y", | |
| 6313 | + "type": "integer", | |
| 6314 | + "primaryKey": false, | |
| 6315 | + "notNull": true, | |
| 6316 | + "default": 0 | |
| 6317 | + }, | |
| 6318 | + "publication_count_12m": { | |
| 6319 | + "name": "publication_count_12m", | |
| 6320 | + "type": "integer", | |
| 6321 | + "primaryKey": false, | |
| 6322 | + "notNull": true, | |
| 6323 | + "default": 0 | |
| 6324 | + }, | |
| 6325 | + "gene_count": { | |
| 6326 | + "name": "gene_count", | |
| 6327 | + "type": "integer", | |
| 6328 | + "primaryKey": false, | |
| 6329 | + "notNull": true, | |
| 6330 | + "default": 0 | |
| 6331 | + }, | |
| 6332 | + "variant_count": { | |
| 6333 | + "name": "variant_count", | |
| 6334 | + "type": "integer", | |
| 6335 | + "primaryKey": false, | |
| 6336 | + "notNull": true, | |
| 6337 | + "default": 0 | |
| 6338 | + }, | |
| 6339 | + "drug_count": { | |
| 6340 | + "name": "drug_count", | |
| 6341 | + "type": "integer", | |
| 6342 | + "primaryKey": false, | |
| 6343 | + "notNull": true, | |
| 6344 | + "default": 0 | |
| 6345 | + }, | |
| 6346 | + "approved_drug_count": { | |
| 6347 | + "name": "approved_drug_count", | |
| 6348 | + "type": "integer", | |
| 6349 | + "primaryKey": false, | |
| 6350 | + "notNull": true, | |
| 6351 | + "default": 0 | |
| 6352 | + }, | |
| 6353 | + "evidence_count": { | |
| 6354 | + "name": "evidence_count", | |
| 6355 | + "type": "integer", | |
| 6356 | + "primaryKey": false, | |
| 6357 | + "notNull": true, | |
| 6358 | + "default": 0 | |
| 6359 | + }, | |
| 6360 | + "cohort_count": { | |
| 6361 | + "name": "cohort_count", | |
| 6362 | + "type": "integer", | |
| 6363 | + "primaryKey": false, | |
| 6364 | + "notNull": true, | |
| 6365 | + "default": 0 | |
| 6366 | + }, | |
| 6367 | + "subtype_count": { | |
| 6368 | + "name": "subtype_count", | |
| 6369 | + "type": "integer", | |
| 6370 | + "primaryKey": false, | |
| 6371 | + "notNull": true, | |
| 6372 | + "default": 0 | |
| 6373 | + }, | |
| 6374 | + "descendant_count": { | |
| 6375 | + "name": "descendant_count", | |
| 6376 | + "type": "integer", | |
| 6377 | + "primaryKey": false, | |
| 6378 | + "notNull": true, | |
| 6379 | + "default": 0 | |
| 6380 | + }, | |
| 6381 | + "epidemiology_obs_count": { | |
| 6382 | + "name": "epidemiology_obs_count", | |
| 6383 | + "type": "integer", | |
| 6384 | + "primaryKey": false, | |
| 6385 | + "notNull": true, | |
| 6386 | + "default": 0 | |
| 6387 | + }, | |
| 6388 | + "survival_obs_count": { | |
| 6389 | + "name": "survival_obs_count", | |
| 6390 | + "type": "integer", | |
| 6391 | + "primaryKey": false, | |
| 6392 | + "notNull": true, | |
| 6393 | + "default": 0 | |
| 6394 | + }, | |
| 6395 | + "completeness": { | |
| 6396 | + "name": "completeness", | |
| 6397 | + "type": "jsonb", | |
| 6398 | + "primaryKey": false, | |
| 6399 | + "notNull": true, | |
| 6400 | + "default": "'{}'::jsonb" | |
| 6401 | + }, | |
| 6402 | + "updated_at": { | |
| 6403 | + "name": "updated_at", | |
| 6404 | + "type": "timestamp with time zone", | |
| 6405 | + "primaryKey": false, | |
| 6406 | + "notNull": true, | |
| 6407 | + "default": "now()" | |
| 6408 | + } | |
| 6409 | + }, | |
| 6410 | + "indexes": { | |
| 6411 | + "entity_counters_uq": { | |
| 6412 | + "name": "entity_counters_uq", | |
| 6413 | + "columns": [ | |
| 6414 | + { | |
| 6415 | + "expression": "entity_type", | |
| 6416 | + "isExpression": false, | |
| 6417 | + "asc": true, | |
| 6418 | + "nulls": "last" | |
| 6419 | + }, | |
| 6420 | + { | |
| 6421 | + "expression": "entity_id", | |
| 6422 | + "isExpression": false, | |
| 6423 | + "asc": true, | |
| 6424 | + "nulls": "last" | |
| 6425 | + } | |
| 6426 | + ], | |
| 6427 | + "isUnique": true, | |
| 6428 | + "concurrently": false, | |
| 6429 | + "method": "btree", | |
| 6430 | + "with": {} | |
| 6431 | + }, | |
| 6432 | + "entity_counters_trials_idx": { | |
| 6433 | + "name": "entity_counters_trials_idx", | |
| 6434 | + "columns": [ | |
| 6435 | + { | |
| 6436 | + "expression": "entity_type", | |
| 6437 | + "isExpression": false, | |
| 6438 | + "asc": true, | |
| 6439 | + "nulls": "last" | |
| 6440 | + }, | |
| 6441 | + { | |
| 6442 | + "expression": "active_trial_count", | |
| 6443 | + "isExpression": false, | |
| 6444 | + "asc": true, | |
| 6445 | + "nulls": "last" | |
| 6446 | + } | |
| 6447 | + ], | |
| 6448 | + "isUnique": false, | |
| 6449 | + "concurrently": false, | |
| 6450 | + "method": "btree", | |
| 6451 | + "with": {} | |
| 6452 | + } | |
| 6453 | + }, | |
| 6454 | + "foreignKeys": {}, | |
| 6455 | + "compositePrimaryKeys": {}, | |
| 6456 | + "uniqueConstraints": {}, | |
| 6457 | + "policies": {}, | |
| 6458 | + "checkConstraints": {}, | |
| 6459 | + "isRLSEnabled": false | |
| 6460 | + }, | |
| 6461 | + "public.metric_definitions": { | |
| 6462 | + "name": "metric_definitions", | |
| 6463 | + "schema": "", | |
| 6464 | + "columns": { | |
| 6465 | + "id": { | |
| 6466 | + "name": "id", | |
| 6467 | + "type": "varchar(32)", | |
| 6468 | + "primaryKey": true, | |
| 6469 | + "notNull": true | |
| 6470 | + }, | |
| 6471 | + "slug": { | |
| 6472 | + "name": "slug", | |
| 6473 | + "type": "text", | |
| 6474 | + "primaryKey": false, | |
| 6475 | + "notNull": true | |
| 6476 | + }, | |
| 6477 | + "name": { | |
| 6478 | + "name": "name", | |
| 6479 | + "type": "text", | |
| 6480 | + "primaryKey": false, | |
| 6481 | + "notNull": true | |
| 6482 | + }, | |
| 6483 | + "description": { | |
| 6484 | + "name": "description", | |
| 6485 | + "type": "text", | |
| 6486 | + "primaryKey": false, | |
| 6487 | + "notNull": true | |
| 6488 | + }, | |
| 6489 | + "formula": { | |
| 6490 | + "name": "formula", | |
| 6491 | + "type": "text", | |
| 6492 | + "primaryKey": false, | |
| 6493 | + "notNull": true | |
| 6494 | + }, | |
| 6495 | + "formula_version": { | |
| 6496 | + "name": "formula_version", | |
| 6497 | + "type": "text", | |
| 6498 | + "primaryKey": false, | |
| 6499 | + "notNull": true | |
| 6500 | + }, | |
| 6501 | + "unit": { | |
| 6502 | + "name": "unit", | |
| 6503 | + "type": "text", | |
| 6504 | + "primaryKey": false, | |
| 6505 | + "notNull": true | |
| 6506 | + }, | |
| 6507 | + "higher_is_worse": { | |
| 6508 | + "name": "higher_is_worse", | |
| 6509 | + "type": "boolean", | |
| 6510 | + "primaryKey": false, | |
| 6511 | + "notNull": false | |
| 6512 | + }, | |
| 6513 | + "aggregation": { | |
| 6514 | + "name": "aggregation", | |
| 6515 | + "type": "text", | |
| 6516 | + "primaryKey": false, | |
| 6517 | + "notNull": false | |
| 6518 | + }, | |
| 6519 | + "valid_dimensions": { | |
| 6520 | + "name": "valid_dimensions", | |
| 6521 | + "type": "text[]", | |
| 6522 | + "primaryKey": false, | |
| 6523 | + "notNull": true, | |
| 6524 | + "default": "'{}'" | |
| 6525 | + }, | |
| 6526 | + "source_slugs": { | |
| 6527 | + "name": "source_slugs", | |
| 6528 | + "type": "text[]", | |
| 6529 | + "primaryKey": false, | |
| 6530 | + "notNull": true, | |
| 6531 | + "default": "'{}'" | |
| 6532 | + }, | |
| 6533 | + "category": { | |
| 6534 | + "name": "category", | |
| 6535 | + "type": "text", | |
| 6536 | + "primaryKey": false, | |
| 6537 | + "notNull": true | |
| 6538 | + }, | |
| 6539 | + "eligibility": { | |
| 6540 | + "name": "eligibility", | |
| 6541 | + "type": "jsonb", | |
| 6542 | + "primaryKey": false, | |
| 6543 | + "notNull": true, | |
| 6544 | + "default": "'{}'::jsonb" | |
| 6545 | + }, | |
| 6546 | + "experimental": { | |
| 6547 | + "name": "experimental", | |
| 6548 | + "type": "boolean", | |
| 6549 | + "primaryKey": false, | |
| 6550 | + "notNull": true, | |
| 6551 | + "default": false | |
| 6552 | + }, | |
| 6553 | + "created_at": { | |
| 6554 | + "name": "created_at", | |
| 6555 | + "type": "timestamp with time zone", | |
| 6556 | + "primaryKey": false, | |
| 6557 | + "notNull": true, | |
| 6558 | + "default": "now()" | |
| 6559 | + }, | |
| 6560 | + "updated_at": { | |
| 6561 | + "name": "updated_at", | |
| 6562 | + "type": "timestamp with time zone", | |
| 6563 | + "primaryKey": false, | |
| 6564 | + "notNull": true, | |
| 6565 | + "default": "now()" | |
| 6566 | + } | |
| 6567 | + }, | |
| 6568 | + "indexes": { | |
| 6569 | + "metric_definitions_slug_uq": { | |
| 6570 | + "name": "metric_definitions_slug_uq", | |
| 6571 | + "columns": [ | |
| 6572 | + { | |
| 6573 | + "expression": "slug", | |
| 6574 | + "isExpression": false, | |
| 6575 | + "asc": true, | |
| 6576 | + "nulls": "last" | |
| 6577 | + } | |
| 6578 | + ], | |
| 6579 | + "isUnique": true, | |
| 6580 | + "concurrently": false, | |
| 6581 | + "method": "btree", | |
| 6582 | + "with": {} | |
| 6583 | + } | |
| 6584 | + }, | |
| 6585 | + "foreignKeys": {}, | |
| 6586 | + "compositePrimaryKeys": {}, | |
| 6587 | + "uniqueConstraints": {}, | |
| 6588 | + "policies": {}, | |
| 6589 | + "checkConstraints": {}, | |
| 6590 | + "isRLSEnabled": false | |
| 6591 | + }, | |
| 6592 | + "public.ranking_snapshots": { | |
| 6593 | + "name": "ranking_snapshots", | |
| 6594 | + "schema": "", | |
| 6595 | + "columns": { | |
| 6596 | + "id": { | |
| 6597 | + "name": "id", | |
| 6598 | + "type": "bigserial", | |
| 6599 | + "primaryKey": true, | |
| 6600 | + "notNull": true | |
| 6601 | + }, | |
| 6602 | + "metric_id": { | |
| 6603 | + "name": "metric_id", | |
| 6604 | + "type": "varchar(32)", | |
| 6605 | + "primaryKey": false, | |
| 6606 | + "notNull": true | |
| 6607 | + }, | |
| 6608 | + "metric_slug": { | |
| 6609 | + "name": "metric_slug", | |
| 6610 | + "type": "text", | |
| 6611 | + "primaryKey": false, | |
| 6612 | + "notNull": true | |
| 6613 | + }, | |
| 6614 | + "scope_key": { | |
| 6615 | + "name": "scope_key", | |
| 6616 | + "type": "text", | |
| 6617 | + "primaryKey": false, | |
| 6618 | + "notNull": true | |
| 6619 | + }, | |
| 6620 | + "geography": { | |
| 6621 | + "name": "geography", | |
| 6622 | + "type": "text", | |
| 6623 | + "primaryKey": false, | |
| 6624 | + "notNull": true, | |
| 6625 | + "default": "'WORLD'" | |
| 6626 | + }, | |
| 6627 | + "sex": { | |
| 6628 | + "name": "sex", | |
| 6629 | + "type": "text", | |
| 6630 | + "primaryKey": false, | |
| 6631 | + "notNull": true, | |
| 6632 | + "default": "'all'" | |
| 6633 | + }, | |
| 6634 | + "age_group": { | |
| 6635 | + "name": "age_group", | |
| 6636 | + "type": "text", | |
| 6637 | + "primaryKey": false, | |
| 6638 | + "notNull": true, | |
| 6639 | + "default": "'all'" | |
| 6640 | + }, | |
| 6641 | + "year": { | |
| 6642 | + "name": "year", | |
| 6643 | + "type": "integer", | |
| 6644 | + "primaryKey": false, | |
| 6645 | + "notNull": false | |
| 6646 | + }, | |
| 6647 | + "entity_level": { | |
| 6648 | + "name": "entity_level", | |
| 6649 | + "type": "text", | |
| 6650 | + "primaryKey": false, | |
| 6651 | + "notNull": true, | |
| 6652 | + "default": "'top'" | |
| 6653 | + }, | |
| 6654 | + "formula_version": { | |
| 6655 | + "name": "formula_version", | |
| 6656 | + "type": "text", | |
| 6657 | + "primaryKey": false, | |
| 6658 | + "notNull": true | |
| 6659 | + }, | |
| 6660 | + "eligible_entities": { | |
| 6661 | + "name": "eligible_entities", | |
| 6662 | + "type": "integer", | |
| 6663 | + "primaryKey": false, | |
| 6664 | + "notNull": true | |
| 6665 | + }, | |
| 6666 | + "inputs_hash": { | |
| 6667 | + "name": "inputs_hash", | |
| 6668 | + "type": "text", | |
| 6669 | + "primaryKey": false, | |
| 6670 | + "notNull": true | |
| 6671 | + }, | |
| 6672 | + "source_ids": { | |
| 6673 | + "name": "source_ids", | |
| 6674 | + "type": "text[]", | |
| 6675 | + "primaryKey": false, | |
| 6676 | + "notNull": true, | |
| 6677 | + "default": "'{}'" | |
| 6678 | + }, | |
| 6679 | + "is_current": { | |
| 6680 | + "name": "is_current", | |
| 6681 | + "type": "boolean", | |
| 6682 | + "primaryKey": false, | |
| 6683 | + "notNull": true, | |
| 6684 | + "default": true | |
| 6685 | + }, | |
| 6686 | + "generated_at": { | |
| 6687 | + "name": "generated_at", | |
| 6688 | + "type": "timestamp with time zone", | |
| 6689 | + "primaryKey": false, | |
| 6690 | + "notNull": true, | |
| 6691 | + "default": "now()" | |
| 6692 | + } | |
| 6693 | + }, | |
| 6694 | + "indexes": { | |
| 6695 | + "ranking_snapshots_lookup_idx": { | |
| 6696 | + "name": "ranking_snapshots_lookup_idx", | |
| 6697 | + "columns": [ | |
| 6698 | + { | |
| 6699 | + "expression": "metric_slug", | |
| 6700 | + "isExpression": false, | |
| 6701 | + "asc": true, | |
| 6702 | + "nulls": "last" | |
| 6703 | + }, | |
| 6704 | + { | |
| 6705 | + "expression": "scope_key", | |
| 6706 | + "isExpression": false, | |
| 6707 | + "asc": true, | |
| 6708 | + "nulls": "last" | |
| 6709 | + }, | |
| 6710 | + { | |
| 6711 | + "expression": "is_current", | |
| 6712 | + "isExpression": false, | |
| 6713 | + "asc": true, | |
| 6714 | + "nulls": "last" | |
| 6715 | + } | |
| 6716 | + ], | |
| 6717 | + "isUnique": false, | |
| 6718 | + "concurrently": false, | |
| 6719 | + "method": "btree", | |
| 6720 | + "with": {} | |
| 6721 | + } | |
| 6722 | + }, | |
| 6723 | + "foreignKeys": {}, | |
| 6724 | + "compositePrimaryKeys": {}, | |
| 6725 | + "uniqueConstraints": {}, | |
| 6726 | + "policies": {}, | |
| 6727 | + "checkConstraints": {}, | |
| 6728 | + "isRLSEnabled": false | |
| 6729 | + }, | |
| 6730 | + "public.rankings": { | |
| 6731 | + "name": "rankings", | |
| 6732 | + "schema": "", | |
| 6733 | + "columns": { | |
| 6734 | + "id": { | |
| 6735 | + "name": "id", | |
| 6736 | + "type": "bigserial", | |
| 6737 | + "primaryKey": true, | |
| 6738 | + "notNull": true | |
| 6739 | + }, | |
| 6740 | + "snapshot_id": { | |
| 6741 | + "name": "snapshot_id", | |
| 6742 | + "type": "integer", | |
| 6743 | + "primaryKey": false, | |
| 6744 | + "notNull": true | |
| 6745 | + }, | |
| 6746 | + "metric_slug": { | |
| 6747 | + "name": "metric_slug", | |
| 6748 | + "type": "text", | |
| 6749 | + "primaryKey": false, | |
| 6750 | + "notNull": true | |
| 6751 | + }, | |
| 6752 | + "scope_key": { | |
| 6753 | + "name": "scope_key", | |
| 6754 | + "type": "text", | |
| 6755 | + "primaryKey": false, | |
| 6756 | + "notNull": true | |
| 6757 | + }, | |
| 6758 | + "cancer_id": { | |
| 6759 | + "name": "cancer_id", | |
| 6760 | + "type": "varchar(32)", | |
| 6761 | + "primaryKey": false, | |
| 6762 | + "notNull": true | |
| 6763 | + }, | |
| 6764 | + "rank": { | |
| 6765 | + "name": "rank", | |
| 6766 | + "type": "integer", | |
| 6767 | + "primaryKey": false, | |
| 6768 | + "notNull": true | |
| 6769 | + }, | |
| 6770 | + "eligible_entities": { | |
| 6771 | + "name": "eligible_entities", | |
| 6772 | + "type": "integer", | |
| 6773 | + "primaryKey": false, | |
| 6774 | + "notNull": true | |
| 6775 | + }, | |
| 6776 | + "percentile": { | |
| 6777 | + "name": "percentile", | |
| 6778 | + "type": "real", | |
| 6779 | + "primaryKey": false, | |
| 6780 | + "notNull": true | |
| 6781 | + }, | |
| 6782 | + "value": { | |
| 6783 | + "name": "value", | |
| 6784 | + "type": "double precision", | |
| 6785 | + "primaryKey": false, | |
| 6786 | + "notNull": true | |
| 6787 | + }, | |
| 6788 | + "unit": { | |
| 6789 | + "name": "unit", | |
| 6790 | + "type": "text", | |
| 6791 | + "primaryKey": false, | |
| 6792 | + "notNull": true | |
| 6793 | + }, | |
| 6794 | + "confidence": { | |
| 6795 | + "name": "confidence", | |
| 6796 | + "type": "text", | |
| 6797 | + "primaryKey": false, | |
| 6798 | + "notNull": true, | |
| 6799 | + "default": "'MEDIUM'" | |
| 6800 | + }, | |
| 6801 | + "inputs": { | |
| 6802 | + "name": "inputs", | |
| 6803 | + "type": "jsonb", | |
| 6804 | + "primaryKey": false, | |
| 6805 | + "notNull": true, | |
| 6806 | + "default": "'{}'::jsonb" | |
| 6807 | + }, | |
| 6808 | + "breakdown": { | |
| 6809 | + "name": "breakdown", | |
| 6810 | + "type": "jsonb", | |
| 6811 | + "primaryKey": false, | |
| 6812 | + "notNull": false | |
| 6813 | + }, | |
| 6814 | + "previous_rank": { | |
| 6815 | + "name": "previous_rank", | |
| 6816 | + "type": "integer", | |
| 6817 | + "primaryKey": false, | |
| 6818 | + "notNull": false | |
| 6819 | + }, | |
| 6820 | + "generated_at": { | |
| 6821 | + "name": "generated_at", | |
| 6822 | + "type": "timestamp with time zone", | |
| 6823 | + "primaryKey": false, | |
| 6824 | + "notNull": true, | |
| 6825 | + "default": "now()" | |
| 6826 | + } | |
| 6827 | + }, | |
| 6828 | + "indexes": { | |
| 6829 | + "rankings_uq": { | |
| 6830 | + "name": "rankings_uq", | |
| 6831 | + "columns": [ | |
| 6832 | + { | |
| 6833 | + "expression": "snapshot_id", | |
| 6834 | + "isExpression": false, | |
| 6835 | + "asc": true, | |
| 6836 | + "nulls": "last" | |
| 6837 | + }, | |
| 6838 | + { | |
| 6839 | + "expression": "cancer_id", | |
| 6840 | + "isExpression": false, | |
| 6841 | + "asc": true, | |
| 6842 | + "nulls": "last" | |
| 6843 | + } | |
| 6844 | + ], | |
| 6845 | + "isUnique": true, | |
| 6846 | + "concurrently": false, | |
| 6847 | + "method": "btree", | |
| 6848 | + "with": {} | |
| 6849 | + }, | |
| 6850 | + "rankings_cancer_idx": { | |
| 6851 | + "name": "rankings_cancer_idx", | |
| 6852 | + "columns": [ | |
| 6853 | + { | |
| 6854 | + "expression": "cancer_id", | |
| 6855 | + "isExpression": false, | |
| 6856 | + "asc": true, | |
| 6857 | + "nulls": "last" | |
| 6858 | + }, | |
| 6859 | + { | |
| 6860 | + "expression": "metric_slug", | |
| 6861 | + "isExpression": false, | |
| 6862 | + "asc": true, | |
| 6863 | + "nulls": "last" | |
| 6864 | + } | |
| 6865 | + ], | |
| 6866 | + "isUnique": false, | |
| 6867 | + "concurrently": false, | |
| 6868 | + "method": "btree", | |
| 6869 | + "with": {} | |
| 6870 | + }, | |
| 6871 | + "rankings_lookup_idx": { | |
| 6872 | + "name": "rankings_lookup_idx", | |
| 6873 | + "columns": [ | |
| 6874 | + { | |
| 6875 | + "expression": "metric_slug", | |
| 6876 | + "isExpression": false, | |
| 6877 | + "asc": true, | |
| 6878 | + "nulls": "last" | |
| 6879 | + }, | |
| 6880 | + { | |
| 6881 | + "expression": "scope_key", | |
| 6882 | + "isExpression": false, | |
| 6883 | + "asc": true, | |
| 6884 | + "nulls": "last" | |
| 6885 | + }, | |
| 6886 | + { | |
| 6887 | + "expression": "rank", | |
| 6888 | + "isExpression": false, | |
| 6889 | + "asc": true, | |
| 6890 | + "nulls": "last" | |
| 6891 | + } | |
| 6892 | + ], | |
| 6893 | + "isUnique": false, | |
| 6894 | + "concurrently": false, | |
| 6895 | + "method": "btree", | |
| 6896 | + "with": {} | |
| 6897 | + } | |
| 6898 | + }, | |
| 6899 | + "foreignKeys": {}, | |
| 6900 | + "compositePrimaryKeys": {}, | |
| 6901 | + "uniqueConstraints": {}, | |
| 6902 | + "policies": {}, | |
| 6903 | + "checkConstraints": {}, | |
| 6904 | + "isRLSEnabled": false | |
| 6905 | + } | |
| 6906 | + }, | |
| 6907 | + "enums": {}, | |
| 6908 | + "schemas": {}, | |
| 6909 | + "sequences": {}, | |
| 6910 | + "roles": {}, | |
| 6911 | + "policies": {}, | |
| 6912 | + "views": {}, | |
| 6913 | + "_meta": { | |
| 6914 | + "columns": {}, | |
| 6915 | + "schemas": {}, | |
| 6916 | + "tables": {} | |
| 6917 | + } | |
| 6918 | +} | |
| \ No newline at end of file | ||
added
packages/database/migrations/meta/_journal.json
+13 −0
@@ -0,0 +1,13 @@ | ||
| 1 | +{ | |
| 2 | + "version": "7", | |
| 3 | + "dialect": "postgresql", | |
| 4 | + "entries": [ | |
| 5 | + { | |
| 6 | + "idx": 0, | |
| 7 | + "version": "7", | |
| 8 | + "when": 1788858213248, | |
| 9 | + "tag": "0000_small_blazing_skull", | |
| 10 | + "breakpoints": true | |
| 11 | + } | |
| 12 | + ] | |
| 13 | +} | |
| \ No newline at end of file | ||
added
packages/database/package.json
+37 −0
@@ -0,0 +1,37 @@ | ||
| 1 | +{ | |
| 2 | + "name": "@cancerindex/database", | |
| 3 | + "version": "0.1.0", | |
| 4 | + "private": true, | |
| 5 | + "type": "module", | |
| 6 | + "exports": { | |
| 7 | + ".": { | |
| 8 | + "types": "./src/index.ts", | |
| 9 | + "default": "./src/index.ts" | |
| 10 | + }, | |
| 11 | + "./schema": { | |
| 12 | + "types": "./src/schema/index.ts", | |
| 13 | + "default": "./src/schema/index.ts" | |
| 14 | + } | |
| 15 | + }, | |
| 16 | + "scripts": { | |
| 17 | + "build": "tsc -p tsconfig.json --noEmit", | |
| 18 | + "typecheck": "tsc -p tsconfig.json --noEmit", | |
| 19 | + "test": "vitest run --passWithNoTests", | |
| 20 | + "generate": "drizzle-kit generate", | |
| 21 | + "migrate": "tsx src/migrate.ts", | |
| 22 | + "seed": "tsx src/seed.ts", | |
| 23 | + "studio": "drizzle-kit studio" | |
| 24 | + }, | |
| 25 | + "dependencies": { | |
| 26 | + "@cancerindex/shared": "workspace:*", | |
| 27 | + "drizzle-orm": "^0.45.0", | |
| 28 | + "postgres": "^3.4.7" | |
| 29 | + }, | |
| 30 | + "devDependencies": { | |
| 31 | + "@types/node": "^24.0.0", | |
| 32 | + "drizzle-kit": "^0.31.0", | |
| 33 | + "tsx": "^4.20.0", | |
| 34 | + "typescript": "^5.9.3", | |
| 35 | + "vitest": "^3.2.0" | |
| 36 | + } | |
| 37 | +} | |
added
packages/database/src/client.ts
+42 −0
@@ -0,0 +1,42 @@ | ||
| 1 | +import postgres from 'postgres'; | |
| 2 | +import { drizzle, type PostgresJsDatabase } from 'drizzle-orm/postgres-js'; | |
| 3 | +import * as schema from './schema/index.js'; | |
| 4 | + | |
| 5 | +export type Database = PostgresJsDatabase<typeof schema>; | |
| 6 | + | |
| 7 | +let _sql: ReturnType<typeof postgres> | null = null; | |
| 8 | +let _db: Database | null = null; | |
| 9 | + | |
| 10 | +export interface DbOptions { | |
| 11 | + url?: string; | |
| 12 | + max?: number; | |
| 13 | +} | |
| 14 | + | |
| 15 | +/** Lazily created singleton; safe from Next.js server components, Fastify and workers alike. */ | |
| 16 | +export function getSql(opts: DbOptions = {}) { | |
| 17 | + if (!_sql) { | |
| 18 | + const url = opts.url ?? process.env.DATABASE_URL ?? 'postgres://localhost:5432/cancerindex'; | |
| 19 | + _sql = postgres(url, { | |
| 20 | + max: opts.max ?? Number(process.env.DB_POOL_MAX ?? 10), | |
| 21 | + idle_timeout: 30, | |
| 22 | + connect_timeout: 10, | |
| 23 | + prepare: true, | |
| 24 | + transform: { undefined: null }, | |
| 25 | + onnotice: () => {}, | |
| 26 | + }); | |
| 27 | + } | |
| 28 | + return _sql; | |
| 29 | +} | |
| 30 | + | |
| 31 | +export function getDb(opts: DbOptions = {}): Database { | |
| 32 | + if (!_db) _db = drizzle(getSql(opts), { schema, casing: 'snake_case' }); | |
| 33 | + return _db; | |
| 34 | +} | |
| 35 | + | |
| 36 | +export async function closeDb(): Promise<void> { | |
| 37 | + if (_sql) { | |
| 38 | + await _sql.end({ timeout: 5 }); | |
| 39 | + _sql = null; | |
| 40 | + _db = null; | |
| 41 | + } | |
| 42 | +} | |
added
packages/database/src/ids.ts
+25 −0
@@ -0,0 +1,25 @@ | ||
| 1 | +import { sql } from 'drizzle-orm'; | |
| 2 | +import { formatId, type IdNamespace } from '@cancerindex/shared'; | |
| 3 | +import type { Database } from './client.js'; | |
| 4 | + | |
| 5 | +/** | |
| 6 | + * Mint stable public identifiers from per-namespace counters (CLAUDE.md §6). | |
| 7 | + * Atomic upsert-increment; `count` ids are reserved in one round trip. | |
| 8 | + */ | |
| 9 | +export async function mintIds(db: Database, ns: IdNamespace, count = 1): Promise<string[]> { | |
| 10 | + if (count < 1) return []; | |
| 11 | + const rows = await db.execute<{ next: string | number }>(sql` | |
| 12 | + INSERT INTO id_sequences (namespace, next) VALUES (${ns}, ${count + 1}) | |
| 13 | + ON CONFLICT (namespace) DO UPDATE SET next = id_sequences.next + ${count} | |
| 14 | + RETURNING next | |
| 15 | + `); | |
| 16 | + const next = Number(rows[0]?.next ?? 0); | |
| 17 | + const first = next - count; | |
| 18 | + return Array.from({ length: count }, (_, i) => formatId(ns, first + i)); | |
| 19 | +} | |
| 20 | + | |
| 21 | +export async function mintId(db: Database, ns: IdNamespace): Promise<string> { | |
| 22 | + const [id] = await mintIds(db, ns, 1); | |
| 23 | + if (!id) throw new Error('mint failed'); | |
| 24 | + return id; | |
| 25 | +} | |
added
packages/database/src/index.ts
+5 −0
@@ -0,0 +1,5 @@ | ||
| 1 | +export * from './client.js'; | |
| 2 | +export * from './ids.js'; | |
| 3 | +export * as schema from './schema/index.js'; | |
| 4 | +export * from './schema/index.js'; | |
| 5 | +export { runMigrations } from './migrate.js'; | |
added
packages/database/src/migrate.ts
+40 −0
@@ -0,0 +1,40 @@ | ||
| 1 | +import { migrate } from 'drizzle-orm/postgres-js/migrator'; | |
| 2 | +import { fileURLToPath } from 'node:url'; | |
| 3 | +import path from 'node:path'; | |
| 4 | +import { getDb, getSql, closeDb } from './client.js'; | |
| 5 | + | |
| 6 | +/** | |
| 7 | + * Runs SQL migrations from ./migrations (CLAUDE.md §284). Extensions are created first because | |
| 8 | + * drizzle-kit does not manage them; pgvector is optional and only enabled when available. | |
| 9 | + */ | |
| 10 | +export async function runMigrations(): Promise<void> { | |
| 11 | + const sql = getSql({ max: 1 }); | |
| 12 | + await sql`CREATE EXTENSION IF NOT EXISTS pg_trgm`; | |
| 13 | + await sql`CREATE EXTENSION IF NOT EXISTS unaccent`; | |
| 14 | + const [vec] = await sql`SELECT 1 AS ok FROM pg_available_extensions WHERE name = 'vector'`; | |
| 15 | + if (vec) await sql`CREATE EXTENSION IF NOT EXISTS vector`; | |
| 16 | + else console.warn('[migrate] pgvector not available — semantic search disabled until installed'); | |
| 17 | + const here = path.dirname(fileURLToPath(import.meta.url)); | |
| 18 | + await migrate(getDb({ max: 1 }), { migrationsFolder: path.resolve(here, '../migrations') }); | |
| 19 | + // Trigram indexes for fuzzy search (not expressible in drizzle schema). | |
| 20 | + await sql`CREATE INDEX IF NOT EXISTS cancer_aliases_trgm_idx ON cancer_aliases USING gin (normalized gin_trgm_ops)`; | |
| 21 | + await sql`CREATE INDEX IF NOT EXISTS cancers_name_trgm_idx ON cancers USING gin (lower(canonical_name) gin_trgm_ops)`; | |
| 22 | + await sql`CREATE INDEX IF NOT EXISTS genes_symbol_trgm_idx ON genes USING gin (lower(symbol) gin_trgm_ops)`; | |
| 23 | + await sql`CREATE INDEX IF NOT EXISTS drugs_name_trgm_idx ON drugs USING gin (lower(name) gin_trgm_ops)`; | |
| 24 | + await sql`CREATE INDEX IF NOT EXISTS trials_title_trgm_idx ON clinical_trials USING gin (lower(brief_title) gin_trgm_ops)`; | |
| 25 | + await sql`CREATE INDEX IF NOT EXISTS publications_title_trgm_idx ON publications USING gin (lower(title) gin_trgm_ops)`; | |
| 26 | +} | |
| 27 | + | |
| 28 | +const isMain = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url); | |
| 29 | +if (isMain) { | |
| 30 | + runMigrations() | |
| 31 | + .then(async () => { | |
| 32 | + console.log('[migrate] done'); | |
| 33 | + await closeDb(); | |
| 34 | + }) | |
| 35 | + .catch(async (err) => { | |
| 36 | + console.error('[migrate] failed', err); | |
| 37 | + await closeDb(); | |
| 38 | + process.exit(1); | |
| 39 | + }); | |
| 40 | +} | |
added
packages/database/src/schema/_common.ts
+23 −0
@@ -0,0 +1,23 @@ | ||
| 1 | +import { timestamp, varchar, customType } from 'drizzle-orm/pg-core'; | |
| 2 | + | |
| 3 | +export const createdAt = () => timestamp('created_at', { withTimezone: true }).notNull().defaultNow(); | |
| 4 | +export const updatedAt = () => timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(); | |
| 5 | + | |
| 6 | +/** Public CancerIndex identifier column (CI-CAN-00000001 …). */ | |
| 7 | +export const ciId = (name = 'id') => varchar(name, { length: 32 }); | |
| 8 | + | |
| 9 | +/** pgvector column (embedding model is stored alongside, never mix models in one column without it). */ | |
| 10 | +export const vector = customType<{ data: number[]; driverData: string; config: { dimensions: number } }>({ | |
| 11 | + dataType(config) { | |
| 12 | + return `vector(${config?.dimensions ?? 1536})`; | |
| 13 | + }, | |
| 14 | + toDriver(value) { | |
| 15 | + return `[${value.join(',')}]`; | |
| 16 | + }, | |
| 17 | + fromDriver(value) { | |
| 18 | + return value | |
| 19 | + .slice(1, -1) | |
| 20 | + .split(',') | |
| 21 | + .map((v) => Number(v)); | |
| 22 | + }, | |
| 23 | +}); | |
added
packages/database/src/schema/cancers.ts
+139 −0
@@ -0,0 +1,139 @@ | ||
| 1 | +import { pgTable, text, integer, boolean, bigserial, index, uniqueIndex, real, jsonb } from 'drizzle-orm/pg-core'; | |
| 2 | +import { ciId, createdAt, updatedAt } from './_common.js'; | |
| 3 | + | |
| 4 | +/** Canonical cancer entity (CLAUDE.md §5). One row per recognized disease concept, never per alias. */ | |
| 5 | +export const cancers = pgTable( | |
| 6 | + 'cancers', | |
| 7 | + { | |
| 8 | + id: ciId().primaryKey(), // CI-CAN-… | |
| 9 | + slug: text('slug').notNull(), | |
| 10 | + canonicalName: text('canonical_name').notNull(), | |
| 11 | + shortName: text('short_name'), | |
| 12 | + entityType: text('entity_type').notNull().default('cancer'), // cancer | cancer_family | histology | subtype | molecular_subtype | hematologic_malignancy | precursor_condition | other | |
| 13 | + malignant: boolean('malignant').notNull().default(true), | |
| 14 | + solidTumor: boolean('solid_tumor').notNull().default(true), | |
| 15 | + hematologic: boolean('hematologic').notNull().default(false), | |
| 16 | + pediatricRelevant: boolean('pediatric_relevant').notNull().default(false), | |
| 17 | + rareCancer: boolean('rare_cancer'), // null = unknown (no incidence data yet) | |
| 18 | + topLevel: boolean('top_level').notNull().default(false), // member of the mutually exclusive global ranking set (§247) | |
| 19 | + description: text('description'), | |
| 20 | + descriptionProvenanceId: integer('description_provenance_id'), | |
| 21 | + primaryNcitCode: text('primary_ncit_code'), | |
| 22 | + primaryOncotreeCode: text('primary_oncotree_code'), | |
| 23 | + depth: integer('depth').notNull().default(0), // depth in the NCIt-derived hierarchy from root "Neoplasm" | |
| 24 | + status: text('status').notNull().default('active'), // active | deprecated | merged | |
| 25 | + mergedInto: ciId('merged_into'), | |
| 26 | + deprecatedReason: text('deprecated_reason'), | |
| 27 | + classificationVersion: text('classification_version'), | |
| 28 | + semanticTypes: text('semantic_types').array().notNull().default([]), | |
| 29 | + createdAt: createdAt(), | |
| 30 | + updatedAt: updatedAt(), | |
| 31 | + }, | |
| 32 | + (t) => [ | |
| 33 | + uniqueIndex('cancers_slug_uq').on(t.slug), | |
| 34 | + uniqueIndex('cancers_ncit_uq').on(t.primaryNcitCode), | |
| 35 | + index('cancers_name_idx').on(t.canonicalName), | |
| 36 | + index('cancers_type_idx').on(t.entityType, t.malignant, t.topLevel), | |
| 37 | + ], | |
| 38 | +); | |
| 39 | + | |
| 40 | +export const cancerAliases = pgTable( | |
| 41 | + 'cancer_aliases', | |
| 42 | + { | |
| 43 | + id: bigserial('id', { mode: 'number' }).primaryKey(), | |
| 44 | + cancerId: ciId('cancer_id').notNull(), | |
| 45 | + alias: text('alias').notNull(), | |
| 46 | + normalized: text('normalized').notNull(), | |
| 47 | + aliasType: text('alias_type').notNull().default('synonym'), // preferred | synonym | abbreviation | historical | deprecated | display | |
| 48 | + sourceId: ciId('source_id'), | |
| 49 | + sourceTerminology: text('source_terminology'), // NCIt synonym source (e.g. CTRP, caDSR) or connector id | |
| 50 | + language: text('language').notNull().default('en'), | |
| 51 | + }, | |
| 52 | + (t) => [uniqueIndex('cancer_aliases_uq').on(t.cancerId, t.normalized, t.aliasType), index('cancer_aliases_norm_idx').on(t.normalized)], | |
| 53 | +); | |
| 54 | + | |
| 55 | +/** Multi-dimensional hierarchy (CLAUDE.md §4): several trees coexist. */ | |
| 56 | +export const cancerHierarchy = pgTable( | |
| 57 | + 'cancer_hierarchy', | |
| 58 | + { | |
| 59 | + id: bigserial('id', { mode: 'number' }).primaryKey(), | |
| 60 | + parentId: ciId('parent_id').notNull(), | |
| 61 | + childId: ciId('child_id').notNull(), | |
| 62 | + hierarchyType: text('hierarchy_type').notNull(), // ncit | oncotree | anatomical | histological | molecular | who | icd | seer | |
| 63 | + sourceId: ciId('source_id'), | |
| 64 | + }, | |
| 65 | + (t) => [uniqueIndex('cancer_hierarchy_uq').on(t.parentId, t.childId, t.hierarchyType), index('cancer_hierarchy_child_idx').on(t.childId)], | |
| 66 | +); | |
| 67 | + | |
| 68 | +/** Cross-reference codes (CLAUDE.md §220-221, §347): searchable, never buried in JSON. */ | |
| 69 | +export const cancerCodes = pgTable( | |
| 70 | + 'cancer_codes', | |
| 71 | + { | |
| 72 | + id: bigserial('id', { mode: 'number' }).primaryKey(), | |
| 73 | + cancerId: ciId('cancer_id').notNull(), | |
| 74 | + system: text('system').notNull(), // ncit | icd10 | icd10cm | icdo_topography | icdo_morphology | doid | oncotree | umls | mesh | mondo | seer_site | efo | orphanet | gdc_project | |
| 75 | + code: text('code').notNull(), | |
| 76 | + matchType: text('match_type').notNull().default('EXACT_IDENTIFIER'), | |
| 77 | + sourceId: ciId('source_id'), | |
| 78 | + validFrom: text('valid_from'), | |
| 79 | + validTo: text('valid_to'), | |
| 80 | + }, | |
| 81 | + (t) => [uniqueIndex('cancer_codes_uq').on(t.cancerId, t.system, t.code), index('cancer_codes_lookup_idx').on(t.system, t.code)], | |
| 82 | +); | |
| 83 | + | |
| 84 | +export const anatomicalSites = pgTable( | |
| 85 | + 'anatomical_sites', | |
| 86 | + { | |
| 87 | + id: ciId().primaryKey(), // CI-ANAT-… | |
| 88 | + name: text('name').notNull(), | |
| 89 | + slug: text('slug').notNull(), | |
| 90 | + ncitCode: text('ncit_code'), | |
| 91 | + uberonId: text('uberon_id'), | |
| 92 | + parentId: ciId('parent_id'), | |
| 93 | + system: text('system'), // e.g. Digestive, Respiratory, Hematopoietic | |
| 94 | + }, | |
| 95 | + (t) => [uniqueIndex('anatomical_sites_slug_uq').on(t.slug)], | |
| 96 | +); | |
| 97 | + | |
| 98 | +export const cancerAnatomy = pgTable( | |
| 99 | + 'cancer_anatomy', | |
| 100 | + { | |
| 101 | + id: bigserial('id', { mode: 'number' }).primaryKey(), | |
| 102 | + cancerId: ciId('cancer_id').notNull(), | |
| 103 | + siteId: ciId('site_id').notNull(), | |
| 104 | + relation: text('relation').notNull().default('primary'), // primary | metastatic | |
| 105 | + sourceId: ciId('source_id'), | |
| 106 | + }, | |
| 107 | + (t) => [uniqueIndex('cancer_anatomy_uq').on(t.cancerId, t.siteId, t.relation)], | |
| 108 | +); | |
| 109 | + | |
| 110 | +/** Canonical geography (CLAUDE.md §117). */ | |
| 111 | +export const geographies = pgTable( | |
| 112 | + 'geographies', | |
| 113 | + { | |
| 114 | + id: ciId().primaryKey(), // CI-GEO-… | |
| 115 | + slug: text('slug').notNull(), | |
| 116 | + name: text('name').notNull(), | |
| 117 | + kind: text('kind').notNull(), // world | region | who_region | country | subdivision | |
| 118 | + iso2: text('iso2'), | |
| 119 | + iso3: text('iso3'), | |
| 120 | + parentId: ciId('parent_id'), | |
| 121 | + whoRegion: text('who_region'), | |
| 122 | + population: integer('population'), | |
| 123 | + populationYear: integer('population_year'), | |
| 124 | + }, | |
| 125 | + (t) => [uniqueIndex('geographies_slug_uq').on(t.slug), index('geographies_iso3_idx').on(t.iso3)], | |
| 126 | +); | |
| 127 | + | |
| 128 | +/** Cohort definitions (CLAUDE.md §218): attribute combinations that are not taxonomy nodes. */ | |
| 129 | +export const cohortDefinitions = pgTable('cohort_definitions', { | |
| 130 | + id: bigserial('id', { mode: 'number' }).primaryKey(), | |
| 131 | + name: text('name').notNull(), | |
| 132 | + cancerId: ciId('cancer_id').notNull(), | |
| 133 | + biomarkerIds: text('biomarker_ids').array().notNull().default([]), | |
| 134 | + variantIds: text('variant_ids').array().notNull().default([]), | |
| 135 | + stage: text('stage'), | |
| 136 | + attributes: jsonb('attributes').$type<Record<string, unknown>>().notNull().default({}), | |
| 137 | + confidence: real('confidence'), | |
| 138 | + createdAt: createdAt(), | |
| 139 | +}); | |
added
packages/database/src/schema/drugs.ts
+84 −0
@@ -0,0 +1,84 @@ | ||
| 1 | +import { pgTable, text, integer, bigserial, index, uniqueIndex, boolean, jsonb } from 'drizzle-orm/pg-core'; | |
| 2 | +import { ciId, createdAt, updatedAt } from './_common.js'; | |
| 3 | + | |
| 4 | +/** Drugs (CLAUDE.md §7, §344): brand names are aliases, never separate molecules. */ | |
| 5 | +export const drugs = pgTable( | |
| 6 | + 'drugs', | |
| 7 | + { | |
| 8 | + id: ciId().primaryKey(), // CI-DRUG-… | |
| 9 | + slug: text('slug').notNull(), | |
| 10 | + name: text('name').notNull(), // generic / INN preferred | |
| 11 | + kind: text('kind'), // small_molecule | monoclonal_antibody | adc | bispecific | car_t | cell_therapy | gene_therapy | vaccine | radiopharmaceutical | chemotherapy | hormonal | immunotherapy | targeted | other | |
| 12 | + ncitCode: text('ncit_code'), | |
| 13 | + chemblId: text('chembl_id'), | |
| 14 | + civicTherapyId: integer('civic_therapy_id'), | |
| 15 | + drugbankId: text('drugbank_id'), | |
| 16 | + pubchemCid: text('pubchem_cid'), | |
| 17 | + unii: text('unii'), | |
| 18 | + mechanism: text('mechanism'), | |
| 19 | + targetGeneIds: text('target_gene_ids').array().notNull().default([]), | |
| 20 | + developmentStatus: text('development_status'), | |
| 21 | + description: text('description'), | |
| 22 | + createdAt: createdAt(), | |
| 23 | + updatedAt: updatedAt(), | |
| 24 | + }, | |
| 25 | + (t) => [uniqueIndex('drugs_slug_uq').on(t.slug), index('drugs_ncit_idx').on(t.ncitCode), index('drugs_civic_idx').on(t.civicTherapyId), index('drugs_chembl_idx').on(t.chemblId)], | |
| 26 | +); | |
| 27 | + | |
| 28 | +export const drugAliases = pgTable( | |
| 29 | + 'drug_aliases', | |
| 30 | + { | |
| 31 | + id: bigserial('id', { mode: 'number' }).primaryKey(), | |
| 32 | + drugId: ciId('drug_id').notNull(), | |
| 33 | + alias: text('alias').notNull(), | |
| 34 | + normalized: text('normalized').notNull(), | |
| 35 | + aliasType: text('alias_type').notNull().default('synonym'), // generic | brand | development_code | salt | synonym | |
| 36 | + sourceId: ciId('source_id'), | |
| 37 | + }, | |
| 38 | + (t) => [uniqueIndex('drug_aliases_uq').on(t.drugId, t.normalized, t.aliasType), index('drug_aliases_norm_idx').on(t.normalized)], | |
| 39 | +); | |
| 40 | + | |
| 41 | +/** Regimens / combinations (CLAUDE.md §53). */ | |
| 42 | +export const treatmentRegimens = pgTable( | |
| 43 | + 'treatment_regimens', | |
| 44 | + { | |
| 45 | + id: ciId().primaryKey(), // CI-TRT-… | |
| 46 | + slug: text('slug').notNull(), | |
| 47 | + name: text('name').notNull(), | |
| 48 | + componentDrugIds: text('component_drug_ids').array().notNull().default([]), | |
| 49 | + modality: text('modality').notNull().default('drug_combination'), // drug | drug_combination | surgery | radiotherapy | transplantation | cell_therapy | surveillance | |
| 50 | + description: text('description'), | |
| 51 | + createdAt: createdAt(), | |
| 52 | + }, | |
| 53 | + (t) => [uniqueIndex('treatment_regimens_slug_uq').on(t.slug)], | |
| 54 | +); | |
| 55 | + | |
| 56 | +/** Country-aware regulatory status (CLAUDE.md §13). Never a bare approved=true. */ | |
| 57 | +export const drugApprovals = pgTable( | |
| 58 | + 'drug_approvals', | |
| 59 | + { | |
| 60 | + id: bigserial('id', { mode: 'number' }).primaryKey(), | |
| 61 | + drugId: ciId('drug_id').notNull(), | |
| 62 | + cancerId: ciId('cancer_id'), | |
| 63 | + biomarkerIds: text('biomarker_ids').array().notNull().default([]), | |
| 64 | + tumorAgnostic: boolean('tumor_agnostic').notNull().default(false), | |
| 65 | + jurisdiction: text('jurisdiction').notNull(), // US | CA | EU | UK | AU | JP | CH | OTHER | |
| 66 | + authority: text('authority').notNull(), // FDA | Health Canada | EMA | MHRA | TGA | PMDA | Swissmedic | |
| 67 | + indication: text('indication').notNull(), | |
| 68 | + lineOfTherapy: text('line_of_therapy'), | |
| 69 | + diseaseStage: text('disease_stage'), | |
| 70 | + approvalType: text('approval_type'), | |
| 71 | + accelerated: boolean('accelerated'), | |
| 72 | + conditional: boolean('conditional'), | |
| 73 | + approvalDate: text('approval_date'), | |
| 74 | + withdrawalDate: text('withdrawal_date'), | |
| 75 | + status: text('status').notNull(), // approved | conditional | accelerated | withdrawn | superseded | |
| 76 | + applicationNumber: text('application_number'), | |
| 77 | + sourceId: ciId('source_id').notNull(), | |
| 78 | + provenanceId: integer('provenance_id').notNull(), | |
| 79 | + raw: jsonb('raw'), | |
| 80 | + createdAt: createdAt(), | |
| 81 | + updatedAt: updatedAt(), | |
| 82 | + }, | |
| 83 | + (t) => [index('drug_approvals_drug_idx').on(t.drugId), index('drug_approvals_cancer_idx').on(t.cancerId)], | |
| 84 | +); | |
added
packages/database/src/schema/epidemiology.ts
+61 −0
@@ -0,0 +1,61 @@ | ||
| 1 | +import { pgTable, text, integer, bigserial, index, uniqueIndex, real, doublePrecision } from 'drizzle-orm/pg-core'; | |
| 2 | +import { ciId, updatedAt } from './_common.js'; | |
| 3 | + | |
| 4 | +/** Time-aware epidemiology observations (CLAUDE.md §71): never overwrite a year with another. */ | |
| 5 | +export const epidemiologyObservations = pgTable( | |
| 6 | + 'epidemiology_observations', | |
| 7 | + { | |
| 8 | + id: bigserial('id', { mode: 'number' }).primaryKey(), | |
| 9 | + cancerId: ciId('cancer_id').notNull(), | |
| 10 | + geographyId: ciId('geography_id').notNull(), | |
| 11 | + year: integer('year').notNull(), | |
| 12 | + yearEnd: integer('year_end'), // for multi-year aggregates (e.g. 2018-2022) | |
| 13 | + sex: text('sex').notNull().default('all'), // all | male | female | |
| 14 | + ageGroup: text('age_group').notNull().default('all'), | |
| 15 | + metric: text('metric').notNull(), // incidence_count | incidence_rate | as_incidence_rate | mortality_count | mortality_rate | as_mortality_rate | prevalence | prevalence_5y | |
| 16 | + value: doublePrecision('value').notNull(), | |
| 17 | + unit: text('unit').notNull(), // count | per_100k | |
| 18 | + lowerCi: doublePrecision('lower_ci'), | |
| 19 | + upperCi: doublePrecision('upper_ci'), | |
| 20 | + standardPopulation: text('standard_population'), // e.g. "US 2000 standard", "World (Segi)" | |
| 21 | + estimateType: text('estimate_type').notNull().default('observed'), // observed | estimated | projected | |
| 22 | + siteDefinition: text('site_definition'), // source's own site label / ICD code range | |
| 23 | + sourceId: ciId('source_id').notNull(), | |
| 24 | + provenanceId: integer('provenance_id').notNull(), | |
| 25 | + ingestRunId: text('ingest_run_id'), | |
| 26 | + updatedAt: updatedAt(), | |
| 27 | + }, | |
| 28 | + (t) => [ | |
| 29 | + uniqueIndex('epi_obs_uq').on(t.cancerId, t.geographyId, t.year, t.sex, t.ageGroup, t.metric, t.sourceId, t.siteDefinition), | |
| 30 | + index('epi_obs_lookup_idx').on(t.metric, t.geographyId, t.year, t.sex), | |
| 31 | + index('epi_obs_cancer_idx').on(t.cancerId, t.metric), | |
| 32 | + ], | |
| 33 | +); | |
| 34 | + | |
| 35 | +/** Survival requires context (CLAUDE.md §72). */ | |
| 36 | +export const survivalObservations = pgTable( | |
| 37 | + 'survival_observations', | |
| 38 | + { | |
| 39 | + id: bigserial('id', { mode: 'number' }).primaryKey(), | |
| 40 | + cancerId: ciId('cancer_id').notNull(), | |
| 41 | + geographyId: ciId('geography_id'), | |
| 42 | + stage: text('stage'), | |
| 43 | + stagingSystem: text('staging_system'), | |
| 44 | + sex: text('sex').notNull().default('all'), | |
| 45 | + ageGroup: text('age_group').notNull().default('all'), | |
| 46 | + diagnosisPeriod: text('diagnosis_period'), | |
| 47 | + survivalType: text('survival_type').notNull(), // overall | relative | cause_specific | progression_free | disease_free | net | |
| 48 | + durationMonths: integer('duration_months').notNull(), | |
| 49 | + probability: real('probability'), // 0..1 | |
| 50 | + medianMonths: real('median_months'), | |
| 51 | + cohortSize: integer('cohort_size'), | |
| 52 | + lowerCi: real('lower_ci'), | |
| 53 | + upperCi: real('upper_ci'), | |
| 54 | + method: text('method'), | |
| 55 | + sourceId: ciId('source_id').notNull(), | |
| 56 | + provenanceId: integer('provenance_id').notNull(), | |
| 57 | + ingestRunId: text('ingest_run_id'), | |
| 58 | + updatedAt: updatedAt(), | |
| 59 | + }, | |
| 60 | + (t) => [index('survival_obs_cancer_idx').on(t.cancerId, t.survivalType, t.durationMonths)], | |
| 61 | +); | |
added
packages/database/src/schema/evidence.ts
+89 −0
@@ -0,0 +1,89 @@ | ||
| 1 | +import { pgTable, text, integer, bigserial, index, uniqueIndex, boolean, jsonb, real, timestamp } from 'drizzle-orm/pg-core'; | |
| 2 | +import { ciId, createdAt, updatedAt } from './_common.js'; | |
| 3 | + | |
| 4 | +/** Knowledge graph edge with mandatory context and provenance (CLAUDE.md §28-29, §244). */ | |
| 5 | +export const knowledgeEdges = pgTable( | |
| 6 | + 'knowledge_edges', | |
| 7 | + { | |
| 8 | + id: bigserial('id', { mode: 'number' }).primaryKey(), | |
| 9 | + sourceEntityType: text('source_entity_type').notNull(), // cancer | gene | variant | drug | biomarker | trial | publication | pathway | risk_factor | |
| 10 | + sourceEntityId: text('source_entity_id').notNull(), | |
| 11 | + targetEntityType: text('target_entity_type').notNull(), | |
| 12 | + targetEntityId: text('target_entity_id').notNull(), | |
| 13 | + relationshipType: text('relationship_type').notNull(), // HAS_SUBTYPE | OCCURS_IN | ASSOCIATED_WITH | HAS_VARIANT | HAS_BIOMARKER | TREATED_BY | STUDIED_IN | DESCRIBED_BY | TARGETS | APPROVED_FOR | INVESTIGATED_FOR | PREDICTS_RESPONSE_TO | CONFERS_RESISTANCE_TO | PREDISPOSES_TO | PROGNOSTIC_IN | DIAGNOSTIC_OF | |
| 14 | + cancerContextIds: text('cancer_context_ids').array().notNull().default([]), | |
| 15 | + predictive: boolean('predictive'), | |
| 16 | + prognostic: boolean('prognostic'), | |
| 17 | + diagnostic: boolean('diagnostic'), | |
| 18 | + predisposing: boolean('predisposing'), | |
| 19 | + direction: text('direction'), // supports | resistance | sensitivity | neutral | unknown | |
| 20 | + evidenceLevel: text('evidence_level'), // source-native level (e.g. CIViC A-E) — never re-scaled silently | |
| 21 | + evidenceScore: real('evidence_score'), | |
| 22 | + evidenceCategory: text('evidence_category').notNull().default('curated_evidence'), // observed_data | published_evidence | curated_evidence | regulatory_status | clinical_guideline | computed_metric | |
| 23 | + status: text('status').notNull().default('active'), // active | candidate | superseded | retracted_basis | |
| 24 | + sourceId: ciId('source_id').notNull(), | |
| 25 | + sourceRecordId: text('source_record_id'), | |
| 26 | + provenanceIds: integer('provenance_ids').array().notNull().default([]), | |
| 27 | + supportCount: integer('support_count').notNull().default(1), | |
| 28 | + firstSeenAt: timestamp('first_seen_at', { withTimezone: true }).notNull().defaultNow(), | |
| 29 | + lastSeenAt: timestamp('last_seen_at', { withTimezone: true }).notNull().defaultNow(), | |
| 30 | + }, | |
| 31 | + (t) => [ | |
| 32 | + uniqueIndex('knowledge_edges_uq').on(t.sourceEntityType, t.sourceEntityId, t.targetEntityType, t.targetEntityId, t.relationshipType, t.sourceId, t.sourceRecordId), | |
| 33 | + index('knowledge_edges_source_idx').on(t.sourceEntityType, t.sourceEntityId, t.relationshipType), | |
| 34 | + index('knowledge_edges_target_idx').on(t.targetEntityType, t.targetEntityId, t.relationshipType), | |
| 35 | + ], | |
| 36 | +); | |
| 37 | + | |
| 38 | +/** CIViC evidence items kept in their native structure (CLAUDE.md §10.9). */ | |
| 39 | +export const civicEvidenceItems = pgTable( | |
| 40 | + 'civic_evidence_items', | |
| 41 | + { | |
| 42 | + id: bigserial('id', { mode: 'number' }).primaryKey(), | |
| 43 | + civicId: integer('civic_id').notNull(), | |
| 44 | + name: text('name'), | |
| 45 | + molecularProfileId: integer('molecular_profile_id'), | |
| 46 | + molecularProfileName: text('molecular_profile_name'), | |
| 47 | + geneSymbols: text('gene_symbols').array().notNull().default([]), | |
| 48 | + geneIds: text('gene_ids').array().notNull().default([]), | |
| 49 | + variantIds: text('variant_ids').array().notNull().default([]), | |
| 50 | + civicVariantIds: integer('civic_variant_ids').array().notNull().default([]), | |
| 51 | + diseaseName: text('disease_name'), | |
| 52 | + doid: text('doid'), | |
| 53 | + cancerId: ciId('cancer_id'), | |
| 54 | + cancerMatchType: text('cancer_match_type'), | |
| 55 | + therapyNames: text('therapy_names').array().notNull().default([]), | |
| 56 | + therapyIds: text('therapy_ids').array().notNull().default([]), // CI-DRUG-… | |
| 57 | + therapyInteractionType: text('therapy_interaction_type'), | |
| 58 | + evidenceType: text('evidence_type'), // PREDICTIVE | PROGNOSTIC | DIAGNOSTIC | PREDISPOSING | ONCOGENIC | FUNCTIONAL | |
| 59 | + evidenceLevel: text('evidence_level'), // A-E | |
| 60 | + evidenceDirection: text('evidence_direction'), // SUPPORTS | DOES_NOT_SUPPORT | |
| 61 | + significance: text('significance'), // SENSITIVITYRESPONSE | RESISTANCE | … | |
| 62 | + evidenceRating: integer('evidence_rating'), | |
| 63 | + status: text('status'), // ACCEPTED | SUBMITTED | REJECTED | |
| 64 | + description: text('description'), | |
| 65 | + pmid: text('pmid'), | |
| 66 | + sourceCitation: text('source_citation'), | |
| 67 | + phenotypes: text('phenotypes').array().notNull().default([]), | |
| 68 | + provenanceId: integer('provenance_id').notNull(), | |
| 69 | + ingestRunId: text('ingest_run_id'), | |
| 70 | + updatedAt: updatedAt(), | |
| 71 | + }, | |
| 72 | + (t) => [uniqueIndex('civic_evidence_uq').on(t.civicId), index('civic_evidence_cancer_idx').on(t.cancerId), index('civic_evidence_gene_idx').on(t.geneSymbols)], | |
| 73 | +); | |
| 74 | + | |
| 75 | +/** Risk factors / carcinogens as first-class entities (CLAUDE.md §74, §133). */ | |
| 76 | +export const riskFactors = pgTable( | |
| 77 | + 'risk_factors', | |
| 78 | + { | |
| 79 | + id: bigserial('id', { mode: 'number' }).primaryKey(), | |
| 80 | + slug: text('slug').notNull(), | |
| 81 | + name: text('name').notNull(), | |
| 82 | + kind: text('kind').notNull(), // behavioral | infectious | environmental | occupational | genetic | hormonal | demographic | carcinogen | |
| 83 | + classificationAuthority: text('classification_authority'), | |
| 84 | + classification: text('classification'), | |
| 85 | + description: text('description'), | |
| 86 | + createdAt: createdAt(), | |
| 87 | + }, | |
| 88 | + (t) => [uniqueIndex('risk_factors_slug_uq').on(t.slug)], | |
| 89 | +); | |
added
packages/database/src/schema/genomics.ts
+184 −0
@@ -0,0 +1,184 @@ | ||
| 1 | +import { pgTable, text, integer, bigserial, index, uniqueIndex, real, jsonb, boolean, timestamp } from 'drizzle-orm/pg-core'; | |
| 2 | +import { ciId, createdAt, updatedAt } from './_common.js'; | |
| 3 | + | |
| 4 | +/** Genes — HGNC is authoritative for symbols (CLAUDE.md §11, §143). */ | |
| 5 | +export const genes = pgTable( | |
| 6 | + 'genes', | |
| 7 | + { | |
| 8 | + id: ciId().primaryKey(), // CI-GENE-… | |
| 9 | + hgncId: text('hgnc_id'), // HGNC:11998 | |
| 10 | + symbol: text('symbol').notNull(), | |
| 11 | + name: text('name'), | |
| 12 | + locusType: text('locus_type'), | |
| 13 | + locusGroup: text('locus_group'), | |
| 14 | + location: text('location'), | |
| 15 | + chromosome: text('chromosome'), | |
| 16 | + ensemblGeneId: text('ensembl_gene_id'), | |
| 17 | + ncbiGeneId: text('ncbi_gene_id'), | |
| 18 | + omimIds: text('omim_ids').array().notNull().default([]), | |
| 19 | + uniprotIds: text('uniprot_ids').array().notNull().default([]), | |
| 20 | + refseqAccession: text('refseq_accession'), | |
| 21 | + prevSymbols: text('prev_symbols').array().notNull().default([]), | |
| 22 | + aliasSymbols: text('alias_symbols').array().notNull().default([]), | |
| 23 | + geneFamilies: text('gene_families').array().notNull().default([]), | |
| 24 | + status: text('status').notNull().default('Approved'), | |
| 25 | + isCancerGene: boolean('is_cancer_gene').notNull().default(false), // has ≥1 curated cancer edge (derived) | |
| 26 | + civicGeneId: integer('civic_gene_id'), | |
| 27 | + description: text('description'), | |
| 28 | + createdAt: createdAt(), | |
| 29 | + updatedAt: updatedAt(), | |
| 30 | + }, | |
| 31 | + (t) => [uniqueIndex('genes_symbol_uq').on(t.symbol), uniqueIndex('genes_hgnc_uq').on(t.hgncId), index('genes_ensembl_idx').on(t.ensemblGeneId), index('genes_ncbi_idx').on(t.ncbiGeneId)], | |
| 32 | +); | |
| 33 | + | |
| 34 | +export const geneAliases = pgTable( | |
| 35 | + 'gene_aliases', | |
| 36 | + { | |
| 37 | + id: bigserial('id', { mode: 'number' }).primaryKey(), | |
| 38 | + geneId: ciId('gene_id').notNull(), | |
| 39 | + alias: text('alias').notNull(), | |
| 40 | + aliasType: text('alias_type').notNull(), // prev_symbol | alias_symbol | prev_name | alias_name | |
| 41 | + sourceId: ciId('source_id'), | |
| 42 | + }, | |
| 43 | + (t) => [uniqueIndex('gene_aliases_uq').on(t.geneId, t.alias, t.aliasType), index('gene_aliases_alias_idx').on(t.alias)], | |
| 44 | +); | |
| 45 | + | |
| 46 | +/** Variants (CLAUDE.md §235-239): coordinates always carry assembly; original + normalized kept. */ | |
| 47 | +export const variants = pgTable( | |
| 48 | + 'variants', | |
| 49 | + { | |
| 50 | + id: ciId().primaryKey(), // CI-VAR-… | |
| 51 | + slug: text('slug').notNull(), | |
| 52 | + geneId: ciId('gene_id'), | |
| 53 | + geneSymbol: text('gene_symbol'), | |
| 54 | + name: text('name').notNull(), // e.g. "V600E", "Exon 19 Deletion", "Amplification" | |
| 55 | + variantType: text('variant_type'), // SO-style: SNV | MNV | insertion | deletion | indel | fusion | amplification | deletion_cna | loss_of_heterozygosity | promoter_mutation | splice | expression | epigenetic | structural | other | |
| 56 | + hgvsG: text('hgvs_g'), | |
| 57 | + hgvsC: text('hgvs_c'), | |
| 58 | + hgvsP: text('hgvs_p'), | |
| 59 | + assembly: text('assembly'), // GRCh37 | GRCh38 | |
| 60 | + chromosome: text('chromosome'), | |
| 61 | + start: integer('start'), | |
| 62 | + end: integer('end'), | |
| 63 | + referenceBases: text('reference_bases'), | |
| 64 | + alternateBases: text('alternate_bases'), | |
| 65 | + coordinates: jsonb('coordinates').$type<Array<Record<string, unknown>>>().notNull().default([]), // per-assembly list | |
| 66 | + clinvarVariationId: text('clinvar_variation_id'), | |
| 67 | + civicVariantId: integer('civic_variant_id'), | |
| 68 | + dbsnpIds: text('dbsnp_ids').array().notNull().default([]), | |
| 69 | + fusionPartners: text('fusion_partners').array().notNull().default([]), // [5' gene, 3' gene] | |
| 70 | + createdAt: createdAt(), | |
| 71 | + updatedAt: updatedAt(), | |
| 72 | + }, | |
| 73 | + (t) => [uniqueIndex('variants_slug_uq').on(t.slug), index('variants_gene_idx').on(t.geneId), index('variants_clinvar_idx').on(t.clinvarVariationId), index('variants_civic_idx').on(t.civicVariantId)], | |
| 74 | +); | |
| 75 | + | |
| 76 | +export const variantAliases = pgTable( | |
| 77 | + 'variant_aliases', | |
| 78 | + { | |
| 79 | + id: bigserial('id', { mode: 'number' }).primaryKey(), | |
| 80 | + variantId: ciId('variant_id').notNull(), | |
| 81 | + alias: text('alias').notNull(), | |
| 82 | + sourceId: ciId('source_id'), | |
| 83 | + }, | |
| 84 | + (t) => [uniqueIndex('variant_aliases_uq').on(t.variantId, t.alias)], | |
| 85 | +); | |
| 86 | + | |
| 87 | +/** ClinVar interpretations (CLAUDE.md §10.7): structured, never flattened. */ | |
| 88 | +export const variantClinicalSignificance = pgTable( | |
| 89 | + 'variant_clinical_significance', | |
| 90 | + { | |
| 91 | + id: bigserial('id', { mode: 'number' }).primaryKey(), | |
| 92 | + variantId: ciId('variant_id').notNull(), | |
| 93 | + clinvarVariationId: text('clinvar_variation_id').notNull(), | |
| 94 | + clinicalSignificance: text('clinical_significance').notNull(), | |
| 95 | + reviewStatus: text('review_status'), | |
| 96 | + starRating: integer('star_rating'), | |
| 97 | + lastEvaluated: text('last_evaluated'), | |
| 98 | + conditions: text('conditions').array().notNull().default([]), | |
| 99 | + conditionCancerIds: text('condition_cancer_ids').array().notNull().default([]), | |
| 100 | + originSimple: text('origin_simple'), | |
| 101 | + numberSubmitters: integer('number_submitters'), | |
| 102 | + provenanceId: integer('provenance_id').notNull(), | |
| 103 | + ingestRunId: text('ingest_run_id'), | |
| 104 | + updatedAt: updatedAt(), | |
| 105 | + }, | |
| 106 | + (t) => [uniqueIndex('variant_clinsig_uq').on(t.clinvarVariationId)], | |
| 107 | +); | |
| 108 | + | |
| 109 | +export const biomarkers = pgTable( | |
| 110 | + 'biomarkers', | |
| 111 | + { | |
| 112 | + id: ciId().primaryKey(), // CI-BIO-… | |
| 113 | + slug: text('slug').notNull(), | |
| 114 | + name: text('name').notNull(), | |
| 115 | + kind: text('kind').notNull(), // gene_mutation | protein_expression | hormone_receptor | immune_marker | msi | tmb | hrd | ctdna | methylation | signature | cell_surface | other | |
| 116 | + geneId: ciId('gene_id'), | |
| 117 | + ncitCode: text('ncit_code'), | |
| 118 | + description: text('description'), | |
| 119 | + measurement: jsonb('measurement').$type<Record<string, unknown>>().notNull().default({}), // assay/clone/scoring/thresholds (§241-243) | |
| 120 | + createdAt: createdAt(), | |
| 121 | + updatedAt: updatedAt(), | |
| 122 | + }, | |
| 123 | + (t) => [uniqueIndex('biomarkers_slug_uq').on(t.slug)], | |
| 124 | +); | |
| 125 | + | |
| 126 | +/** Genomic studies / cohorts (GDC projects, cBioPortal studies…) — original study IDs preserved. */ | |
| 127 | +export const genomicCohorts = pgTable( | |
| 128 | + 'genomic_cohorts', | |
| 129 | + { | |
| 130 | + id: ciId().primaryKey(), // CI-STUDY-… | |
| 131 | + sourceId: ciId('source_id').notNull(), | |
| 132 | + studyId: text('study_id').notNull(), // TCGA-PAAD | |
| 133 | + name: text('name').notNull(), | |
| 134 | + program: text('program'), | |
| 135 | + primarySites: text('primary_sites').array().notNull().default([]), | |
| 136 | + diseaseTypes: text('disease_types').array().notNull().default([]), | |
| 137 | + cancerId: ciId('cancer_id'), | |
| 138 | + cancerMatchType: text('cancer_match_type'), | |
| 139 | + caseCount: integer('case_count'), | |
| 140 | + casesWithSsm: integer('cases_with_ssm'), // denominator for mutation frequencies | |
| 141 | + dataRelease: text('data_release'), | |
| 142 | + accessLevel: text('access_level').notNull().default('open'), | |
| 143 | + url: text('url'), | |
| 144 | + provenanceId: integer('provenance_id'), | |
| 145 | + updatedAt: updatedAt(), | |
| 146 | + }, | |
| 147 | + (t) => [uniqueIndex('genomic_cohorts_uq').on(t.sourceId, t.studyId), index('genomic_cohorts_cancer_idx').on(t.cancerId)], | |
| 148 | +); | |
| 149 | + | |
| 150 | +/** Gene alteration frequency per cohort — denominator is mandatory (CLAUDE.md §261-262). */ | |
| 151 | +export const cancerGeneFrequencies = pgTable( | |
| 152 | + 'cancer_gene_frequencies', | |
| 153 | + { | |
| 154 | + id: bigserial('id', { mode: 'number' }).primaryKey(), | |
| 155 | + cohortId: ciId('cohort_id').notNull(), | |
| 156 | + cancerId: ciId('cancer_id'), | |
| 157 | + geneId: ciId('gene_id'), | |
| 158 | + geneSymbol: text('gene_symbol').notNull(), | |
| 159 | + alterationType: text('alteration_type').notNull().default('ssm'), // ssm | cnv_gain | cnv_loss | fusion | |
| 160 | + casesAffected: integer('cases_affected').notNull(), | |
| 161 | + casesProfiled: integer('cases_profiled').notNull(), | |
| 162 | + frequency: real('frequency').notNull(), | |
| 163 | + rank: integer('rank'), | |
| 164 | + dataRelease: text('data_release'), | |
| 165 | + provenanceId: integer('provenance_id').notNull(), | |
| 166 | + updatedAt: updatedAt(), | |
| 167 | + }, | |
| 168 | + (t) => [uniqueIndex('cancer_gene_freq_uq').on(t.cohortId, t.geneSymbol, t.alterationType), index('cancer_gene_freq_cancer_idx').on(t.cancerId, t.frequency), index('cancer_gene_freq_gene_idx').on(t.geneId)], | |
| 169 | +); | |
| 170 | + | |
| 171 | +export const entityEmbeddings = pgTable( | |
| 172 | + 'entity_embeddings', | |
| 173 | + { | |
| 174 | + id: bigserial('id', { mode: 'number' }).primaryKey(), | |
| 175 | + entityType: text('entity_type').notNull(), | |
| 176 | + entityId: text('entity_id').notNull(), | |
| 177 | + model: text('model').notNull(), | |
| 178 | + dimensions: integer('dimensions').notNull(), | |
| 179 | + textHash: text('text_hash').notNull(), | |
| 180 | + embedding: text('embedding'), // stored via raw SQL cast to vector(n); model recorded on the row | |
| 181 | + createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), | |
| 182 | + }, | |
| 183 | + (t) => [uniqueIndex('entity_embeddings_uq').on(t.entityType, t.entityId, t.model)], | |
| 184 | +); | |
added
packages/database/src/schema/index.ts
+9 −0
@@ -0,0 +1,9 @@ | ||
| 1 | +export * from './sources.js'; | |
| 2 | +export * from './cancers.js'; | |
| 3 | +export * from './genomics.js'; | |
| 4 | +export * from './drugs.js'; | |
| 5 | +export * from './trials.js'; | |
| 6 | +export * from './publications.js'; | |
| 7 | +export * from './evidence.js'; | |
| 8 | +export * from './epidemiology.js'; | |
| 9 | +export * from './rankings.js'; | |
added
packages/database/src/schema/publications.ts
+68 −0
@@ -0,0 +1,68 @@ | ||
| 1 | +import { pgTable, text, integer, bigserial, index, uniqueIndex, boolean, jsonb, real } from 'drizzle-orm/pg-core'; | |
| 2 | +import { ciId, createdAt, updatedAt } from './_common.js'; | |
| 3 | + | |
| 4 | +/** Publications (CLAUDE.md §10.6, §342): one entity per paper across PubMed/DOI/Europe PMC. */ | |
| 5 | +export const publications = pgTable( | |
| 6 | + 'publications', | |
| 7 | + { | |
| 8 | + id: ciId().primaryKey(), // CI-PUB-… | |
| 9 | + pmid: text('pmid'), | |
| 10 | + doi: text('doi'), | |
| 11 | + pmcid: text('pmcid'), | |
| 12 | + title: text('title').notNull(), | |
| 13 | + abstract: text('abstract'), // stored only where NLM terms permit (abstract text may carry publisher copyright; we store it for indexing, display truncated with link) | |
| 14 | + journal: text('journal'), | |
| 15 | + journalIso: text('journal_iso'), | |
| 16 | + pubDate: text('pub_date'), | |
| 17 | + pubYear: integer('pub_year'), | |
| 18 | + publicationTypes: text('publication_types').array().notNull().default([]), | |
| 19 | + meshTerms: jsonb('mesh_terms').$type<Array<{ descriptor: string; ui?: string; major: boolean; qualifiers?: string[] }>>().notNull().default([]), | |
| 20 | + authors: jsonb('authors').$type<Array<{ name: string; affiliation?: string; orcid?: string }>>().notNull().default([]), | |
| 21 | + language: text('language'), | |
| 22 | + isPreprint: boolean('is_preprint').notNull().default(false), | |
| 23 | + retracted: boolean('retracted').notNull().default(false), | |
| 24 | + retractionNotice: text('retraction_notice'), | |
| 25 | + nctIds: text('nct_ids').array().notNull().default([]), | |
| 26 | + citedByCount: integer('cited_by_count'), | |
| 27 | + sourceRecordId: integer('source_record_id'), | |
| 28 | + ingestRunId: text('ingest_run_id'), | |
| 29 | + createdAt: createdAt(), | |
| 30 | + updatedAt: updatedAt(), | |
| 31 | + }, | |
| 32 | + (t) => [uniqueIndex('publications_pmid_uq').on(t.pmid), index('publications_doi_idx').on(t.doi), index('publications_year_idx').on(t.pubYear), index('publications_retracted_idx').on(t.retracted)], | |
| 33 | +); | |
| 34 | + | |
| 35 | +/** publication → entity links (CLAUDE.md §165-166, §271): extraction method + status preserved. */ | |
| 36 | +export const publicationEntityEdges = pgTable( | |
| 37 | + 'publication_entity_edges', | |
| 38 | + { | |
| 39 | + id: bigserial('id', { mode: 'number' }).primaryKey(), | |
| 40 | + publicationId: ciId('publication_id').notNull(), | |
| 41 | + entityType: text('entity_type').notNull(), // cancer | gene | variant | drug | biomarker | trial | |
| 42 | + entityId: text('entity_id').notNull(), | |
| 43 | + method: text('method').notNull(), // mesh | dictionary | registry_reference | civic_curation | ner | llm | curator | |
| 44 | + confidence: real('confidence'), | |
| 45 | + status: text('status').notNull().default('candidate'), // candidate | validated | rejected | |
| 46 | + sourceId: ciId('source_id'), | |
| 47 | + ingestRunId: text('ingest_run_id'), | |
| 48 | + createdAt: createdAt(), | |
| 49 | + }, | |
| 50 | + (t) => [uniqueIndex('pub_entity_edges_uq').on(t.publicationId, t.entityType, t.entityId, t.method), index('pub_entity_edges_entity_idx').on(t.entityType, t.entityId)], | |
| 51 | +); | |
| 52 | + | |
| 53 | +/** Research activity counts per cancer from PubMed queries — the formula (query string) is stored (CLAUDE.md §251). */ | |
| 54 | +export const literatureCounts = pgTable( | |
| 55 | + 'literature_counts', | |
| 56 | + { | |
| 57 | + id: bigserial('id', { mode: 'number' }).primaryKey(), | |
| 58 | + cancerId: ciId('cancer_id').notNull(), | |
| 59 | + windowKey: text('window_key').notNull(), // all | 12m | 5y | 10y | y2015 … | |
| 60 | + windowStart: text('window_start'), | |
| 61 | + windowEnd: text('window_end'), | |
| 62 | + query: text('query').notNull(), // exact PubMed query used | |
| 63 | + count: integer('count').notNull(), | |
| 64 | + provenanceId: integer('provenance_id').notNull(), | |
| 65 | + computedAt: updatedAt(), | |
| 66 | + }, | |
| 67 | + (t) => [uniqueIndex('literature_counts_uq').on(t.cancerId, t.windowKey), index('literature_counts_window_idx').on(t.windowKey, t.count)], | |
| 68 | +); | |
added
packages/database/src/schema/rankings.ts
+138 −0
@@ -0,0 +1,138 @@ | ||
| 1 | +import { pgTable, text, integer, bigserial, index, uniqueIndex, real, jsonb, boolean, timestamp, doublePrecision } from 'drizzle-orm/pg-core'; | |
| 2 | +import { ciId, createdAt, updatedAt } from './_common.js'; | |
| 3 | + | |
| 4 | +/** Metric catalog (CLAUDE.md §250-251): every derived number has a versioned formula. */ | |
| 5 | +export const metricDefinitions = pgTable( | |
| 6 | + 'metric_definitions', | |
| 7 | + { | |
| 8 | + id: ciId().primaryKey(), // CI-METRIC-… | |
| 9 | + slug: text('slug').notNull(), // active_trials | mortality_incidence_ratio | … | |
| 10 | + name: text('name').notNull(), | |
| 11 | + description: text('description').notNull(), | |
| 12 | + formula: text('formula').notNull(), | |
| 13 | + formulaVersion: text('formula_version').notNull(), // ci-mir-v1 | |
| 14 | + unit: text('unit').notNull(), | |
| 15 | + higherIsWorse: boolean('higher_is_worse'), | |
| 16 | + aggregation: text('aggregation'), | |
| 17 | + validDimensions: text('valid_dimensions').array().notNull().default([]), | |
| 18 | + sourceSlugs: text('source_slugs').array().notNull().default([]), | |
| 19 | + category: text('category').notNull(), // burden | lethality | trend | rarity | treatment | trials | research | molecular | unmet_need | composite | |
| 20 | + eligibility: jsonb('eligibility').$type<Record<string, unknown>>().notNull().default({}), | |
| 21 | + experimental: boolean('experimental').notNull().default(false), | |
| 22 | + createdAt: createdAt(), | |
| 23 | + updatedAt: updatedAt(), | |
| 24 | + }, | |
| 25 | + (t) => [uniqueIndex('metric_definitions_slug_uq').on(t.slug)], | |
| 26 | +); | |
| 27 | + | |
| 28 | +/** Ranking snapshot = one metric × one scope × one formula version at one time (CLAUDE.md §33, §184). */ | |
| 29 | +export const rankingSnapshots = pgTable( | |
| 30 | + 'ranking_snapshots', | |
| 31 | + { | |
| 32 | + id: bigserial('id', { mode: 'number' }).primaryKey(), | |
| 33 | + metricId: ciId('metric_id').notNull(), | |
| 34 | + metricSlug: text('metric_slug').notNull(), | |
| 35 | + scopeKey: text('scope_key').notNull(), // e.g. "geo=world|sex=all|age=all|year=2022|level=top" | |
| 36 | + geography: text('geography').notNull().default('WORLD'), | |
| 37 | + sex: text('sex').notNull().default('all'), | |
| 38 | + ageGroup: text('age_group').notNull().default('all'), | |
| 39 | + year: integer('year'), | |
| 40 | + entityLevel: text('entity_level').notNull().default('top'), // top | histology | subtype | rare | all | |
| 41 | + formulaVersion: text('formula_version').notNull(), | |
| 42 | + eligibleEntities: integer('eligible_entities').notNull(), | |
| 43 | + inputsHash: text('inputs_hash').notNull(), | |
| 44 | + sourceIds: text('source_ids').array().notNull().default([]), | |
| 45 | + isCurrent: boolean('is_current').notNull().default(true), | |
| 46 | + generatedAt: timestamp('generated_at', { withTimezone: true }).notNull().defaultNow(), | |
| 47 | + }, | |
| 48 | + (t) => [index('ranking_snapshots_lookup_idx').on(t.metricSlug, t.scopeKey, t.isCurrent)], | |
| 49 | +); | |
| 50 | + | |
| 51 | +export const rankings = pgTable( | |
| 52 | + 'rankings', | |
| 53 | + { | |
| 54 | + id: bigserial('id', { mode: 'number' }).primaryKey(), | |
| 55 | + snapshotId: integer('snapshot_id').notNull(), | |
| 56 | + metricSlug: text('metric_slug').notNull(), | |
| 57 | + scopeKey: text('scope_key').notNull(), | |
| 58 | + cancerId: ciId('cancer_id').notNull(), | |
| 59 | + rank: integer('rank').notNull(), | |
| 60 | + eligibleEntities: integer('eligible_entities').notNull(), | |
| 61 | + percentile: real('percentile').notNull(), | |
| 62 | + value: doublePrecision('value').notNull(), | |
| 63 | + unit: text('unit').notNull(), | |
| 64 | + confidence: text('confidence').notNull().default('MEDIUM'), // HIGH | MEDIUM | LOW | INSUFFICIENT_DATA | |
| 65 | + inputs: jsonb('inputs').$type<Record<string, unknown>>().notNull().default({}), // lineage: observation ids, counts, formula inputs | |
| 66 | + breakdown: jsonb('breakdown').$type<Record<string, number>>(), // composite components | |
| 67 | + previousRank: integer('previous_rank'), | |
| 68 | + generatedAt: timestamp('generated_at', { withTimezone: true }).notNull().defaultNow(), | |
| 69 | + }, | |
| 70 | + (t) => [uniqueIndex('rankings_uq').on(t.snapshotId, t.cancerId), index('rankings_cancer_idx').on(t.cancerId, t.metricSlug), index('rankings_lookup_idx').on(t.metricSlug, t.scopeKey, t.rank)], | |
| 71 | +); | |
| 72 | + | |
| 73 | +/** Precomputed counters per entity, refreshed deterministically from canonical relations (CLAUDE.md §287). */ | |
| 74 | +export const entityCounters = pgTable( | |
| 75 | + 'entity_counters', | |
| 76 | + { | |
| 77 | + id: bigserial('id', { mode: 'number' }).primaryKey(), | |
| 78 | + entityType: text('entity_type').notNull(), | |
| 79 | + entityId: text('entity_id').notNull(), | |
| 80 | + trialCount: integer('trial_count').notNull().default(0), | |
| 81 | + activeTrialCount: integer('active_trial_count').notNull().default(0), | |
| 82 | + recruitingTrialCount: integer('recruiting_trial_count').notNull().default(0), | |
| 83 | + phase3TrialCount: integer('phase3_trial_count').notNull().default(0), | |
| 84 | + publicationCount: integer('publication_count').notNull().default(0), | |
| 85 | + publicationCount5y: integer('publication_count_5y').notNull().default(0), | |
| 86 | + publicationCount12m: integer('publication_count_12m').notNull().default(0), | |
| 87 | + geneCount: integer('gene_count').notNull().default(0), | |
| 88 | + variantCount: integer('variant_count').notNull().default(0), | |
| 89 | + drugCount: integer('drug_count').notNull().default(0), | |
| 90 | + approvedDrugCount: integer('approved_drug_count').notNull().default(0), | |
| 91 | + evidenceCount: integer('evidence_count').notNull().default(0), | |
| 92 | + cohortCount: integer('cohort_count').notNull().default(0), | |
| 93 | + subtypeCount: integer('subtype_count').notNull().default(0), | |
| 94 | + descendantCount: integer('descendant_count').notNull().default(0), | |
| 95 | + epidemiologyObsCount: integer('epidemiology_obs_count').notNull().default(0), | |
| 96 | + survivalObsCount: integer('survival_obs_count').notNull().default(0), | |
| 97 | + completeness: jsonb('completeness').$type<Record<string, number>>().notNull().default({}), | |
| 98 | + computedAt: updatedAt(), | |
| 99 | + }, | |
| 100 | + (t) => [uniqueIndex('entity_counters_uq').on(t.entityType, t.entityId), index('entity_counters_trials_idx').on(t.entityType, t.activeTrialCount)], | |
| 101 | +); | |
| 102 | + | |
| 103 | +/** Public API keys (CLAUDE.md §190-191). */ | |
| 104 | +export const apiKeys = pgTable( | |
| 105 | + 'api_keys', | |
| 106 | + { | |
| 107 | + id: bigserial('id', { mode: 'number' }).primaryKey(), | |
| 108 | + keyHash: text('key_hash').notNull(), | |
| 109 | + prefix: text('prefix').notNull(), | |
| 110 | + label: text('label'), | |
| 111 | + ownerEmail: text('owner_email'), | |
| 112 | + tier: text('tier').notNull().default('free'), // free | research | pro | institutional | |
| 113 | + rateLimitPerMinute: integer('rate_limit_per_minute').notNull().default(60), | |
| 114 | + active: boolean('active').notNull().default(true), | |
| 115 | + lastUsedAt: timestamp('last_used_at', { withTimezone: true }), | |
| 116 | + createdAt: createdAt(), | |
| 117 | + }, | |
| 118 | + (t) => [uniqueIndex('api_keys_hash_uq').on(t.keyHash)], | |
| 119 | +); | |
| 120 | + | |
| 121 | +/** Cached AI syntheses (CLAUDE.md §44, §322) — model, prompt version and record snapshot recorded. */ | |
| 122 | +export const aiAnswers = pgTable( | |
| 123 | + 'ai_answers', | |
| 124 | + { | |
| 125 | + id: bigserial('id', { mode: 'number' }).primaryKey(), | |
| 126 | + kind: text('kind').notNull(), // cancer_summary | ask | |
| 127 | + subjectId: text('subject_id'), | |
| 128 | + questionHash: text('question_hash').notNull(), | |
| 129 | + question: text('question'), | |
| 130 | + answer: jsonb('answer').$type<Record<string, unknown>>().notNull(), | |
| 131 | + model: text('model').notNull(), | |
| 132 | + promptVersion: text('prompt_version').notNull(), | |
| 133 | + sourceSnapshot: jsonb('source_snapshot').$type<Record<string, unknown>>().notNull().default({}), | |
| 134 | + dataAsOf: timestamp('data_as_of', { withTimezone: true }).notNull(), | |
| 135 | + createdAt: createdAt(), | |
| 136 | + }, | |
| 137 | + (t) => [uniqueIndex('ai_answers_uq').on(t.kind, t.questionHash, t.promptVersion)], | |
| 138 | +); | |
added
packages/database/src/schema/sources.ts
+228 −0
@@ -0,0 +1,228 @@ | ||
| 1 | +import { pgTable, text, integer, bigint, bigserial, boolean, jsonb, timestamp, index, uniqueIndex, real, varchar } from 'drizzle-orm/pg-core'; | |
| 2 | +import { ciId, createdAt, updatedAt } from './_common.js'; | |
| 3 | + | |
| 4 | +/** Per-namespace counters for stable public IDs (never exposed, never reused). */ | |
| 5 | +export const idSequences = pgTable('id_sequences', { | |
| 6 | + namespace: varchar('namespace', { length: 16 }).primaryKey(), | |
| 7 | + next: bigint('next', { mode: 'number' }).notNull().default(1), | |
| 8 | +}); | |
| 9 | + | |
| 10 | +/** Source registry + license registry (CLAUDE.md §9, §95, §142). Seeded from connector manifests. */ | |
| 11 | +export const sources = pgTable( | |
| 12 | + 'sources', | |
| 13 | + { | |
| 14 | + id: ciId().primaryKey(), // CI-SOURCE-… | |
| 15 | + slug: text('slug').notNull(), // connector id, e.g. "clinicaltrials" | |
| 16 | + name: text('name').notNull(), | |
| 17 | + organization: text('organization'), | |
| 18 | + category: text('category').notNull(), // terminology | genomics | epidemiology | trials | literature | variants | drugs | regulatory | |
| 19 | + description: text('description'), | |
| 20 | + homepage: text('homepage'), | |
| 21 | + docsUrl: text('docs_url'), | |
| 22 | + termsUrl: text('terms_url'), | |
| 23 | + accessType: text('access_type').notNull(), // api|bulk|rss|ftp|graphql|rest|scrape|manual | |
| 24 | + accessAuth: text('access_auth').notNull(), // none|api_key|oauth|account|controlled | |
| 25 | + license: text('license'), | |
| 26 | + licenseStatus: text('license_status').notNull().default('review'), // review | approved | restricted | blocked | |
| 27 | + commercialUse: text('commercial_use').notNull().default('unknown'), // allowed | restricted | prohibited | unknown | |
| 28 | + redistribution: text('redistribution').notNull().default('unknown'), // allowed | attribution | restricted | prohibited | unknown | |
| 29 | + attribution: text('attribution'), | |
| 30 | + licenseReviewedAt: timestamp('license_reviewed_at', { withTimezone: true }), | |
| 31 | + approvedForProduction: boolean('approved_for_production').notNull().default(false), | |
| 32 | + updateFrequency: text('update_frequency'), | |
| 33 | + supportsIncremental: boolean('supports_incremental').notNull().default(false), | |
| 34 | + entities: text('entities').array().notNull().default([]), | |
| 35 | + metrics: text('metrics').array().notNull().default([]), | |
| 36 | + rateLimit: text('rate_limit'), | |
| 37 | + status: text('status').notNull().default('planned'), // planned | active | paused | degraded | awaiting_credentials | review | retired | |
| 38 | + tier: integer('tier').notNull().default(0), | |
| 39 | + manifest: jsonb('manifest').$type<Record<string, unknown>>().notNull().default({}), | |
| 40 | + createdAt: createdAt(), | |
| 41 | + updatedAt: updatedAt(), | |
| 42 | + }, | |
| 43 | + (t) => [uniqueIndex('sources_slug_uq').on(t.slug)], | |
| 44 | +); | |
| 45 | + | |
| 46 | +/** Every connector execution (CLAUDE.md §24, §176). */ | |
| 47 | +export const ingestRuns = pgTable( | |
| 48 | + 'ingest_runs', | |
| 49 | + { | |
| 50 | + id: text('id').primaryKey(), // ING-CLINICALTRIALS-20260908-000019 | |
| 51 | + connectorId: text('connector_id').notNull(), | |
| 52 | + sourceId: ciId('source_id').notNull(), | |
| 53 | + mode: text('mode').notNull().default('incremental'), // full | incremental | backfill | dry_run | probe | |
| 54 | + status: text('status').notNull().default('running'), // running | succeeded | failed | partial | aborted | |
| 55 | + startedAt: timestamp('started_at', { withTimezone: true }).notNull().defaultNow(), | |
| 56 | + finishedAt: timestamp('finished_at', { withTimezone: true }), | |
| 57 | + durationMs: integer('duration_ms'), | |
| 58 | + recordsFetched: integer('records_fetched').notNull().default(0), | |
| 59 | + recordsCreated: integer('records_created').notNull().default(0), | |
| 60 | + recordsUpdated: integer('records_updated').notNull().default(0), | |
| 61 | + recordsUnchanged: integer('records_unchanged').notNull().default(0), | |
| 62 | + recordsRejected: integer('records_rejected').notNull().default(0), | |
| 63 | + httpRequests: integer('http_requests').notNull().default(0), | |
| 64 | + httpFailures: integer('http_failures').notNull().default(0), | |
| 65 | + rateLimitEvents: integer('rate_limit_events').notNull().default(0), | |
| 66 | + validationFailures: integer('validation_failures').notNull().default(0), | |
| 67 | + schemaDrift: jsonb('schema_drift').$type<unknown[]>().notNull().default([]), | |
| 68 | + cursorBefore: jsonb('cursor_before'), | |
| 69 | + cursorAfter: jsonb('cursor_after'), | |
| 70 | + error: text('error'), | |
| 71 | + log: jsonb('log').$type<Array<{ t: string; level: string; msg: string }>>().notNull().default([]), | |
| 72 | + datasetVersion: text('dataset_version'), | |
| 73 | + anomaly: text('anomaly'), // set when a destructive update was refused (CLAUDE.md §171) | |
| 74 | + }, | |
| 75 | + (t) => [index('ingest_runs_connector_idx').on(t.connectorId, t.startedAt)], | |
| 76 | +); | |
| 77 | + | |
| 78 | +export const connectorCursors = pgTable('connector_cursors', { | |
| 79 | + connectorId: text('connector_id').primaryKey(), | |
| 80 | + cursor: jsonb('cursor').$type<Record<string, unknown>>().notNull().default({}), | |
| 81 | + lastSuccessAt: timestamp('last_success_at', { withTimezone: true }), | |
| 82 | + lastAttemptAt: timestamp('last_attempt_at', { withTimezone: true }), | |
| 83 | + paused: boolean('paused').notNull().default(false), | |
| 84 | + health: text('health').notNull().default('unknown'), // healthy | degraded | failing | review | awaiting_credentials | unknown | |
| 85 | + healthDetail: text('health_detail'), | |
| 86 | + updatedAt: updatedAt(), | |
| 87 | +}); | |
| 88 | + | |
| 89 | +/** Schema-drift detection: observed field names/types per connector entity (CLAUDE.md §25). */ | |
| 90 | +export const connectorFieldStats = pgTable( | |
| 91 | + 'connector_field_stats', | |
| 92 | + { | |
| 93 | + id: bigserial('id', { mode: 'number' }).primaryKey(), | |
| 94 | + connectorId: text('connector_id').notNull(), | |
| 95 | + entity: text('entity').notNull(), | |
| 96 | + field: text('field').notNull(), | |
| 97 | + types: text('types').array().notNull().default([]), | |
| 98 | + seenCount: integer('seen_count').notNull().default(0), | |
| 99 | + nullCount: integer('null_count').notNull().default(0), | |
| 100 | + firstSeenRun: text('first_seen_run'), | |
| 101 | + lastSeenRun: text('last_seen_run'), | |
| 102 | + updatedAt: updatedAt(), | |
| 103 | + }, | |
| 104 | + (t) => [uniqueIndex('connector_field_stats_uq').on(t.connectorId, t.entity, t.field)], | |
| 105 | +); | |
| 106 | + | |
| 107 | +/** Source-native records (idempotency key = source + entity + source record id; CLAUDE.md §91-92). */ | |
| 108 | +export const sourceRecords = pgTable( | |
| 109 | + 'source_records', | |
| 110 | + { | |
| 111 | + id: bigserial('id', { mode: 'number' }).primaryKey(), | |
| 112 | + sourceId: ciId('source_id').notNull(), | |
| 113 | + entityKind: text('entity_kind').notNull(), | |
| 114 | + sourceRecordId: text('source_record_id').notNull(), | |
| 115 | + payloadHash: text('payload_hash').notNull(), | |
| 116 | + rawPath: text('raw_path'), // data lake path /raw/{source}/{date}/{entity}/{id}.json.gz | |
| 117 | + status: text('status').notNull().default('active'), // active | deprecated | retracted | withdrawn | source_missing | |
| 118 | + firstSeenRun: text('first_seen_run'), | |
| 119 | + lastSeenRun: text('last_seen_run'), | |
| 120 | + retrievedAt: timestamp('retrieved_at', { withTimezone: true }).notNull().defaultNow(), | |
| 121 | + sourceUpdatedAt: timestamp('source_updated_at', { withTimezone: true }), | |
| 122 | + canonicalType: text('canonical_type'), | |
| 123 | + canonicalId: text('canonical_id'), | |
| 124 | + createdAt: createdAt(), | |
| 125 | + updatedAt: updatedAt(), | |
| 126 | + }, | |
| 127 | + (t) => [ | |
| 128 | + uniqueIndex('source_records_uq').on(t.sourceId, t.entityKind, t.sourceRecordId), | |
| 129 | + index('source_records_canonical_idx').on(t.canonicalType, t.canonicalId), | |
| 130 | + ], | |
| 131 | +); | |
| 132 | + | |
| 133 | +/** Provenance record (CLAUDE.md §2). Referenced by observations, edges, descriptions, rankings. */ | |
| 134 | +export const provenance = pgTable( | |
| 135 | + 'provenance', | |
| 136 | + { | |
| 137 | + id: bigserial('id', { mode: 'number' }).primaryKey(), | |
| 138 | + publicId: ciId('public_id'), // CI-PROV-… (minted lazily when exposed) | |
| 139 | + sourceId: ciId('source_id').notNull(), | |
| 140 | + sourceRecordId: text('source_record_id'), | |
| 141 | + sourceUrl: text('source_url'), | |
| 142 | + dataset: text('dataset'), | |
| 143 | + datasetVersion: text('dataset_version'), | |
| 144 | + publicationId: ciId('publication_id'), | |
| 145 | + pmid: text('pmid'), | |
| 146 | + doi: text('doi'), | |
| 147 | + retrievedAt: timestamp('retrieved_at', { withTimezone: true }).notNull(), | |
| 148 | + publishedAt: text('published_at'), | |
| 149 | + updatedAtSource: text('updated_at_source'), | |
| 150 | + geography: text('geography'), | |
| 151 | + population: text('population'), | |
| 152 | + cohortSize: integer('cohort_size'), | |
| 153 | + methodology: text('methodology'), | |
| 154 | + evidenceType: text('evidence_type').notNull(), | |
| 155 | + accessLevel: text('access_level').notNull().default('open'), | |
| 156 | + confidence: real('confidence'), | |
| 157 | + license: text('license'), | |
| 158 | + ingestRunId: text('ingest_run_id'), | |
| 159 | + createdAt: createdAt(), | |
| 160 | + }, | |
| 161 | + (t) => [index('provenance_source_idx').on(t.sourceId, t.sourceRecordId), index('provenance_pmid_idx').on(t.pmid)], | |
| 162 | +); | |
| 163 | + | |
| 164 | +/** Unknown disease/drug labels are never discarded (CLAUDE.md §222). */ | |
| 165 | +export const unresolvedLabels = pgTable( | |
| 166 | + 'unresolved_labels', | |
| 167 | + { | |
| 168 | + id: bigserial('id', { mode: 'number' }).primaryKey(), | |
| 169 | + sourceId: ciId('source_id').notNull(), | |
| 170 | + entityKind: text('entity_kind').notNull(), // cancer | drug | gene | biomarker | |
| 171 | + sourceText: text('source_text').notNull(), | |
| 172 | + normalized: text('normalized').notNull(), | |
| 173 | + context: jsonb('context').$type<Record<string, unknown>>().notNull().default({}), | |
| 174 | + count: integer('count').notNull().default(1), | |
| 175 | + status: text('status').notNull().default('open'), // open | mapped | rejected | ignored | |
| 176 | + suggestedId: text('suggested_id'), | |
| 177 | + suggestedMatchType: text('suggested_match_type'), | |
| 178 | + suggestedScore: real('suggested_score'), | |
| 179 | + resolvedId: text('resolved_id'), | |
| 180 | + resolvedBy: text('resolved_by'), | |
| 181 | + createdAt: createdAt(), | |
| 182 | + updatedAt: updatedAt(), | |
| 183 | + }, | |
| 184 | + (t) => [uniqueIndex('unresolved_labels_uq').on(t.sourceId, t.entityKind, t.normalized), index('unresolved_labels_count_idx').on(t.status, t.count)], | |
| 185 | +); | |
| 186 | + | |
| 187 | +/** Entity change history (CLAUDE.md §96). */ | |
| 188 | +export const changeEvents = pgTable( | |
| 189 | + 'change_events', | |
| 190 | + { | |
| 191 | + id: bigserial('id', { mode: 'number' }).primaryKey(), | |
| 192 | + entityType: text('entity_type').notNull(), | |
| 193 | + entityId: text('entity_id').notNull(), | |
| 194 | + kind: text('kind').notNull(), // created | updated | trial_added | approval_added | ranking_changed | merged | deprecated … | |
| 195 | + summary: text('summary').notNull(), | |
| 196 | + before: jsonb('before'), | |
| 197 | + after: jsonb('after'), | |
| 198 | + ingestRunId: text('ingest_run_id'), | |
| 199 | + createdAt: createdAt(), | |
| 200 | + }, | |
| 201 | + (t) => [index('change_events_entity_idx').on(t.entityType, t.entityId, t.createdAt)], | |
| 202 | +); | |
| 203 | + | |
| 204 | +/** Curator/admin audit log (CLAUDE.md §348). */ | |
| 205 | +export const auditLog = pgTable('audit_log', { | |
| 206 | + id: bigserial('id', { mode: 'number' }).primaryKey(), | |
| 207 | + actor: text('actor').notNull(), | |
| 208 | + action: text('action').notNull(), | |
| 209 | + entityType: text('entity_type'), | |
| 210 | + entityId: text('entity_id'), | |
| 211 | + before: jsonb('before'), | |
| 212 | + after: jsonb('after'), | |
| 213 | + reason: text('reason'), | |
| 214 | + createdAt: createdAt(), | |
| 215 | +}); | |
| 216 | + | |
| 217 | +/** Entity merge queue + audit (CLAUDE.md §70). Reversible. */ | |
| 218 | +export const entityMerges = pgTable('entity_merges', { | |
| 219 | + id: bigserial('id', { mode: 'number' }).primaryKey(), | |
| 220 | + entityType: text('entity_type').notNull(), | |
| 221 | + keepId: text('keep_id').notNull(), | |
| 222 | + mergeId: text('merge_id').notNull(), | |
| 223 | + evidence: jsonb('evidence').$type<Record<string, unknown>>().notNull().default({}), | |
| 224 | + status: text('status').notNull().default('proposed'), // proposed | merged | kept_separate | reverted | |
| 225 | + decidedBy: text('decided_by'), | |
| 226 | + decidedAt: timestamp('decided_at', { withTimezone: true }), | |
| 227 | + createdAt: createdAt(), | |
| 228 | +}); | |
added
packages/database/src/schema/trials.ts
+115 −0
@@ -0,0 +1,115 @@ | ||
| 1 | +import { pgTable, text, integer, bigserial, index, uniqueIndex, boolean, jsonb, real, date } from 'drizzle-orm/pg-core'; | |
| 2 | +import { ciId, createdAt, updatedAt } from './_common.js'; | |
| 3 | + | |
| 4 | +/** ClinicalTrials.gov studies (CLAUDE.md §10.5). */ | |
| 5 | +export const clinicalTrials = pgTable( | |
| 6 | + 'clinical_trials', | |
| 7 | + { | |
| 8 | + id: ciId().primaryKey(), // CI-TRIAL-… | |
| 9 | + nctId: text('nct_id').notNull(), | |
| 10 | + briefTitle: text('brief_title').notNull(), | |
| 11 | + officialTitle: text('official_title'), | |
| 12 | + acronym: text('acronym'), | |
| 13 | + studyType: text('study_type'), // INTERVENTIONAL | OBSERVATIONAL | EXPANDED_ACCESS | |
| 14 | + phases: text('phases').array().notNull().default([]), // EARLY_PHASE1 | PHASE1 | PHASE2 | PHASE3 | PHASE4 | NA | |
| 15 | + overallStatus: text('overall_status'), // RECRUITING | ACTIVE_NOT_RECRUITING | COMPLETED | … | |
| 16 | + whyStopped: text('why_stopped'), | |
| 17 | + startDate: text('start_date'), | |
| 18 | + primaryCompletionDate: text('primary_completion_date'), | |
| 19 | + completionDate: text('completion_date'), | |
| 20 | + firstPostedDate: text('first_posted_date'), | |
| 21 | + lastUpdatePostedDate: text('last_update_posted_date'), | |
| 22 | + resultsFirstPostedDate: text('results_first_posted_date'), | |
| 23 | + hasResults: boolean('has_results').notNull().default(false), | |
| 24 | + enrollmentCount: integer('enrollment_count'), | |
| 25 | + enrollmentType: text('enrollment_type'), | |
| 26 | + leadSponsor: text('lead_sponsor'), | |
| 27 | + leadSponsorClass: text('lead_sponsor_class'), | |
| 28 | + collaborators: text('collaborators').array().notNull().default([]), | |
| 29 | + conditions: text('conditions').array().notNull().default([]), // raw free text | |
| 30 | + keywords: text('keywords').array().notNull().default([]), | |
| 31 | + interventions: jsonb('interventions').$type<Array<{ type: string; name: string; description?: string; otherNames?: string[] }>>().notNull().default([]), | |
| 32 | + arms: jsonb('arms').$type<Array<Record<string, unknown>>>().notNull().default([]), | |
| 33 | + primaryOutcomes: jsonb('primary_outcomes').$type<Array<Record<string, unknown>>>().notNull().default([]), | |
| 34 | + secondaryOutcomes: jsonb('secondary_outcomes').$type<Array<Record<string, unknown>>>().notNull().default([]), | |
| 35 | + eligibility: jsonb('eligibility').$type<Record<string, unknown>>().notNull().default({}), | |
| 36 | + sex: text('sex'), | |
| 37 | + minimumAge: text('minimum_age'), | |
| 38 | + maximumAge: text('maximum_age'), | |
| 39 | + countries: text('countries').array().notNull().default([]), | |
| 40 | + locationsCount: integer('locations_count').notNull().default(0), | |
| 41 | + references: jsonb('references').$type<Array<{ pmid?: string; type?: string; citation?: string }>>().notNull().default([]), | |
| 42 | + briefSummary: text('brief_summary'), | |
| 43 | + isOncology: boolean('is_oncology').notNull().default(true), | |
| 44 | + sourceRecordId: integer('source_record_id'), | |
| 45 | + ingestRunId: text('ingest_run_id'), | |
| 46 | + createdAt: createdAt(), | |
| 47 | + updatedAt: updatedAt(), | |
| 48 | + }, | |
| 49 | + (t) => [ | |
| 50 | + uniqueIndex('clinical_trials_nct_uq').on(t.nctId), | |
| 51 | + index('clinical_trials_status_idx').on(t.overallStatus), | |
| 52 | + index('clinical_trials_updated_idx').on(t.lastUpdatePostedDate), | |
| 53 | + index('clinical_trials_sponsor_idx').on(t.leadSponsor), | |
| 54 | + ], | |
| 55 | +); | |
| 56 | + | |
| 57 | +/** trial → cancer mapping from free-text conditions (CLAUDE.md §10.5, §221). */ | |
| 58 | +export const trialConditions = pgTable( | |
| 59 | + 'trial_conditions', | |
| 60 | + { | |
| 61 | + id: bigserial('id', { mode: 'number' }).primaryKey(), | |
| 62 | + trialId: ciId('trial_id').notNull(), | |
| 63 | + conditionText: text('condition_text').notNull(), | |
| 64 | + normalized: text('normalized').notNull(), | |
| 65 | + cancerId: ciId('cancer_id'), | |
| 66 | + matchType: text('match_type').notNull().default('UNRESOLVED'), | |
| 67 | + confidence: real('confidence'), | |
| 68 | + }, | |
| 69 | + (t) => [uniqueIndex('trial_conditions_uq').on(t.trialId, t.normalized), index('trial_conditions_cancer_idx').on(t.cancerId), index('trial_conditions_norm_idx').on(t.normalized)], | |
| 70 | +); | |
| 71 | + | |
| 72 | +export const trialInterventions = pgTable( | |
| 73 | + 'trial_interventions', | |
| 74 | + { | |
| 75 | + id: bigserial('id', { mode: 'number' }).primaryKey(), | |
| 76 | + trialId: ciId('trial_id').notNull(), | |
| 77 | + name: text('name').notNull(), | |
| 78 | + normalized: text('normalized').notNull(), | |
| 79 | + interventionType: text('intervention_type'), // DRUG | BIOLOGICAL | DEVICE | PROCEDURE | RADIATION | BEHAVIORAL | OTHER | |
| 80 | + drugId: ciId('drug_id'), | |
| 81 | + matchType: text('match_type').notNull().default('UNRESOLVED'), | |
| 82 | + }, | |
| 83 | + (t) => [uniqueIndex('trial_interventions_uq').on(t.trialId, t.normalized), index('trial_interventions_drug_idx').on(t.drugId)], | |
| 84 | +); | |
| 85 | + | |
| 86 | +export const trialLocations = pgTable( | |
| 87 | + 'trial_locations', | |
| 88 | + { | |
| 89 | + id: bigserial('id', { mode: 'number' }).primaryKey(), | |
| 90 | + trialId: ciId('trial_id').notNull(), | |
| 91 | + facility: text('facility'), | |
| 92 | + city: text('city'), | |
| 93 | + state: text('state'), | |
| 94 | + zip: text('zip'), | |
| 95 | + country: text('country'), | |
| 96 | + status: text('status'), | |
| 97 | + lat: real('lat'), | |
| 98 | + lng: real('lng'), | |
| 99 | + }, | |
| 100 | + (t) => [index('trial_locations_trial_idx').on(t.trialId), index('trial_locations_country_idx').on(t.country)], | |
| 101 | +); | |
| 102 | + | |
| 103 | +/** Daily trial-creation pulse (derived; CLAUDE.md §332). */ | |
| 104 | +export const trialPulse = pgTable( | |
| 105 | + 'trial_pulse', | |
| 106 | + { | |
| 107 | + id: bigserial('id', { mode: 'number' }).primaryKey(), | |
| 108 | + day: date('day').notNull(), | |
| 109 | + cancerId: ciId('cancer_id'), | |
| 110 | + phase: text('phase'), | |
| 111 | + newTrials: integer('new_trials').notNull(), | |
| 112 | + computedAt: updatedAt(), | |
| 113 | + }, | |
| 114 | + (t) => [uniqueIndex('trial_pulse_uq').on(t.day, t.cancerId, t.phase)], | |
| 115 | +); | |
added
packages/database/src/seed-data/geographies.ts
+101 −0
@@ -0,0 +1,101 @@ | ||
| 1 | +/** Canonical geography seed (CLAUDE.md §117). ISO 3166-1; WHO regions per WHO membership lists. */ | |
| 2 | +export interface GeographySeed { | |
| 3 | + slug: string; | |
| 4 | + name: string; | |
| 5 | + kind: 'world' | 'region' | 'who_region' | 'country' | 'subdivision'; | |
| 6 | + iso2?: string; | |
| 7 | + iso3?: string; | |
| 8 | + parentSlug?: string; | |
| 9 | + whoRegion?: string; | |
| 10 | +} | |
| 11 | + | |
| 12 | +const WHO_REGIONS: Array<[string, string]> = [ | |
| 13 | + ['who-afro', 'WHO African Region'], | |
| 14 | + ['who-amro', 'WHO Region of the Americas'], | |
| 15 | + ['who-searo', 'WHO South-East Asia Region'], | |
| 16 | + ['who-euro', 'WHO European Region'], | |
| 17 | + ['who-emro', 'WHO Eastern Mediterranean Region'], | |
| 18 | + ['who-wpro', 'WHO Western Pacific Region'], | |
| 19 | +]; | |
| 20 | + | |
| 21 | +// [iso2, iso3, name, whoRegion] | |
| 22 | +const COUNTRIES: Array<[string, string, string, string]> = [ | |
| 23 | + ['US', 'USA', 'United States', 'who-amro'], | |
| 24 | + ['CA', 'CAN', 'Canada', 'who-amro'], | |
| 25 | + ['MX', 'MEX', 'Mexico', 'who-amro'], | |
| 26 | + ['BR', 'BRA', 'Brazil', 'who-amro'], | |
| 27 | + ['AR', 'ARG', 'Argentina', 'who-amro'], | |
| 28 | + ['CL', 'CHL', 'Chile', 'who-amro'], | |
| 29 | + ['CO', 'COL', 'Colombia', 'who-amro'], | |
| 30 | + ['PE', 'PER', 'Peru', 'who-amro'], | |
| 31 | + ['CU', 'CUB', 'Cuba', 'who-amro'], | |
| 32 | + ['GB', 'GBR', 'United Kingdom', 'who-euro'], | |
| 33 | + ['IE', 'IRL', 'Ireland', 'who-euro'], | |
| 34 | + ['FR', 'FRA', 'France', 'who-euro'], | |
| 35 | + ['DE', 'DEU', 'Germany', 'who-euro'], | |
| 36 | + ['IT', 'ITA', 'Italy', 'who-euro'], | |
| 37 | + ['ES', 'ESP', 'Spain', 'who-euro'], | |
| 38 | + ['PT', 'PRT', 'Portugal', 'who-euro'], | |
| 39 | + ['NL', 'NLD', 'Netherlands', 'who-euro'], | |
| 40 | + ['BE', 'BEL', 'Belgium', 'who-euro'], | |
| 41 | + ['CH', 'CHE', 'Switzerland', 'who-euro'], | |
| 42 | + ['AT', 'AUT', 'Austria', 'who-euro'], | |
| 43 | + ['SE', 'SWE', 'Sweden', 'who-euro'], | |
| 44 | + ['NO', 'NOR', 'Norway', 'who-euro'], | |
| 45 | + ['DK', 'DNK', 'Denmark', 'who-euro'], | |
| 46 | + ['FI', 'FIN', 'Finland', 'who-euro'], | |
| 47 | + ['IS', 'ISL', 'Iceland', 'who-euro'], | |
| 48 | + ['PL', 'POL', 'Poland', 'who-euro'], | |
| 49 | + ['CZ', 'CZE', 'Czechia', 'who-euro'], | |
| 50 | + ['HU', 'HUN', 'Hungary', 'who-euro'], | |
| 51 | + ['GR', 'GRC', 'Greece', 'who-euro'], | |
| 52 | + ['RO', 'ROU', 'Romania', 'who-euro'], | |
| 53 | + ['UA', 'UKR', 'Ukraine', 'who-euro'], | |
| 54 | + ['RU', 'RUS', 'Russian Federation', 'who-euro'], | |
| 55 | + ['TR', 'TUR', 'Türkiye', 'who-euro'], | |
| 56 | + ['IL', 'ISR', 'Israel', 'who-euro'], | |
| 57 | + ['EG', 'EGY', 'Egypt', 'who-emro'], | |
| 58 | + ['SA', 'SAU', 'Saudi Arabia', 'who-emro'], | |
| 59 | + ['IR', 'IRN', 'Iran', 'who-emro'], | |
| 60 | + ['PK', 'PAK', 'Pakistan', 'who-emro'], | |
| 61 | + ['MA', 'MAR', 'Morocco', 'who-emro'], | |
| 62 | + ['NG', 'NGA', 'Nigeria', 'who-afro'], | |
| 63 | + ['ZA', 'ZAF', 'South Africa', 'who-afro'], | |
| 64 | + ['KE', 'KEN', 'Kenya', 'who-afro'], | |
| 65 | + ['ET', 'ETH', 'Ethiopia', 'who-afro'], | |
| 66 | + ['GH', 'GHA', 'Ghana', 'who-afro'], | |
| 67 | + ['UG', 'UGA', 'Uganda', 'who-afro'], | |
| 68 | + ['TZ', 'TZA', 'Tanzania', 'who-afro'], | |
| 69 | + ['DZ', 'DZA', 'Algeria', 'who-afro'], | |
| 70 | + ['IN', 'IND', 'India', 'who-searo'], | |
| 71 | + ['BD', 'BGD', 'Bangladesh', 'who-searo'], | |
| 72 | + ['ID', 'IDN', 'Indonesia', 'who-searo'], | |
| 73 | + ['TH', 'THA', 'Thailand', 'who-searo'], | |
| 74 | + ['NP', 'NPL', 'Nepal', 'who-searo'], | |
| 75 | + ['LK', 'LKA', 'Sri Lanka', 'who-searo'], | |
| 76 | + ['CN', 'CHN', 'China', 'who-wpro'], | |
| 77 | + ['JP', 'JPN', 'Japan', 'who-wpro'], | |
| 78 | + ['KR', 'KOR', 'Republic of Korea', 'who-wpro'], | |
| 79 | + ['AU', 'AUS', 'Australia', 'who-wpro'], | |
| 80 | + ['NZ', 'NZL', 'New Zealand', 'who-wpro'], | |
| 81 | + ['SG', 'SGP', 'Singapore', 'who-wpro'], | |
| 82 | + ['MY', 'MYS', 'Malaysia', 'who-wpro'], | |
| 83 | + ['PH', 'PHL', 'Philippines', 'who-wpro'], | |
| 84 | + ['VN', 'VNM', 'Viet Nam', 'who-wpro'], | |
| 85 | +]; | |
| 86 | + | |
| 87 | +const US_STATES: Array<[string, string]> = [ | |
| 88 | + ['AL', 'Alabama'], ['AK', 'Alaska'], ['AZ', 'Arizona'], ['AR', 'Arkansas'], ['CA', 'California'], ['CO', 'Colorado'], ['CT', 'Connecticut'], ['DE', 'Delaware'], ['DC', 'District of Columbia'], ['FL', 'Florida'], ['GA', 'Georgia'], ['HI', 'Hawaii'], ['ID', 'Idaho'], ['IL', 'Illinois'], ['IN', 'Indiana'], ['IA', 'Iowa'], ['KS', 'Kansas'], ['KY', 'Kentucky'], ['LA', 'Louisiana'], ['ME', 'Maine'], ['MD', 'Maryland'], ['MA', 'Massachusetts'], ['MI', 'Michigan'], ['MN', 'Minnesota'], ['MS', 'Mississippi'], ['MO', 'Missouri'], ['MT', 'Montana'], ['NE', 'Nebraska'], ['NV', 'Nevada'], ['NH', 'New Hampshire'], ['NJ', 'New Jersey'], ['NM', 'New Mexico'], ['NY', 'New York'], ['NC', 'North Carolina'], ['ND', 'North Dakota'], ['OH', 'Ohio'], ['OK', 'Oklahoma'], ['OR', 'Oregon'], ['PA', 'Pennsylvania'], ['RI', 'Rhode Island'], ['SC', 'South Carolina'], ['SD', 'South Dakota'], ['TN', 'Tennessee'], ['TX', 'Texas'], ['UT', 'Utah'], ['VT', 'Vermont'], ['VA', 'Virginia'], ['WA', 'Washington'], ['WV', 'West Virginia'], ['WI', 'Wisconsin'], ['WY', 'Wyoming'], | |
| 89 | +]; | |
| 90 | + | |
| 91 | +const CA_PROVINCES: Array<[string, string]> = [ | |
| 92 | + ['AB', 'Alberta'], ['BC', 'British Columbia'], ['MB', 'Manitoba'], ['NB', 'New Brunswick'], ['NL', 'Newfoundland and Labrador'], ['NS', 'Nova Scotia'], ['NT', 'Northwest Territories'], ['NU', 'Nunavut'], ['ON', 'Ontario'], ['PE', 'Prince Edward Island'], ['QC', 'Quebec'], ['SK', 'Saskatchewan'], ['YT', 'Yukon'], | |
| 93 | +]; | |
| 94 | + | |
| 95 | +export const GEOGRAPHY_SEED: GeographySeed[] = [ | |
| 96 | + { slug: 'world', name: 'World', kind: 'world' }, | |
| 97 | + ...WHO_REGIONS.map(([slug, name]) => ({ slug, name, kind: 'who_region' as const, parentSlug: 'world' })), | |
| 98 | + ...COUNTRIES.map(([iso2, iso3, name, who]) => ({ slug: name.toLowerCase().replace(/[^a-z0-9]+/g, '-'), name, kind: 'country' as const, iso2, iso3, parentSlug: who, whoRegion: who })), | |
| 99 | + ...US_STATES.map(([code, name]) => ({ slug: `us-${code.toLowerCase()}`, name, kind: 'subdivision' as const, iso2: `US-${code}`, parentSlug: 'united-states', whoRegion: 'who-amro' })), | |
| 100 | + ...CA_PROVINCES.map(([code, name]) => ({ slug: `ca-${code.toLowerCase()}`, name, kind: 'subdivision' as const, iso2: `CA-${code}`, parentSlug: 'canada', whoRegion: 'who-amro' })), | |
| 101 | +]; | |
added
packages/database/src/seed-data/metrics.ts
+285 −0
@@ -0,0 +1,285 @@ | ||
| 1 | +/** | |
| 2 | + * Metric catalog (CLAUDE.md §250-252, §352). Every derived number shown on the site references | |
| 3 | + * one of these definitions by slug; the formula text and version are displayed on /methodology. | |
| 4 | + * | |
| 5 | + * Composite scores (CI-IMPACT etc.) are intentionally absent from Phase 1 (§353). | |
| 6 | + */ | |
| 7 | +export interface MetricSeed { | |
| 8 | + slug: string; | |
| 9 | + name: string; | |
| 10 | + description: string; | |
| 11 | + formula: string; | |
| 12 | + formulaVersion: string; | |
| 13 | + unit: string; | |
| 14 | + higherIsWorse: boolean | null; | |
| 15 | + aggregation: string | null; | |
| 16 | + validDimensions: string[]; | |
| 17 | + sourceSlugs: string[]; | |
| 18 | + category: string; | |
| 19 | + eligibility: Record<string, unknown>; | |
| 20 | + experimental: boolean; | |
| 21 | +} | |
| 22 | + | |
| 23 | +export const METRIC_CATALOG: MetricSeed[] = [ | |
| 24 | + // --- Burden (epidemiology observations) --- | |
| 25 | + { | |
| 26 | + slug: 'incidence_count', | |
| 27 | + name: 'Annual new cases', | |
| 28 | + description: 'Number of new cases diagnosed in the reference year for the population scope. Registry counts are observed; national/global figures may be estimates and are labeled as such.', | |
| 29 | + formula: 'epidemiology_observations.value WHERE metric = incidence_count', | |
| 30 | + formulaVersion: 'ci-incidence-count-v1', | |
| 31 | + unit: 'count', | |
| 32 | + higherIsWorse: true, | |
| 33 | + aggregation: 'none', | |
| 34 | + validDimensions: ['geography', 'sex', 'age_group', 'year'], | |
| 35 | + sourceSlugs: ['cdc-wonder', 'seer', 'iarc-globocan'], | |
| 36 | + category: 'burden', | |
| 37 | + eligibility: { requires: ['incidence_count'], entityLevel: 'top' }, | |
| 38 | + experimental: false, | |
| 39 | + }, | |
| 40 | + { | |
| 41 | + slug: 'mortality_count', | |
| 42 | + name: 'Annual deaths', | |
| 43 | + description: 'Number of deaths with this cancer as underlying cause in the reference year for the population scope.', | |
| 44 | + formula: 'epidemiology_observations.value WHERE metric = mortality_count', | |
| 45 | + formulaVersion: 'ci-mortality-count-v1', | |
| 46 | + unit: 'count', | |
| 47 | + higherIsWorse: true, | |
| 48 | + aggregation: 'none', | |
| 49 | + validDimensions: ['geography', 'sex', 'age_group', 'year'], | |
| 50 | + sourceSlugs: ['cdc-wonder', 'seer', 'iarc-globocan'], | |
| 51 | + category: 'burden', | |
| 52 | + eligibility: { requires: ['mortality_count'], entityLevel: 'top' }, | |
| 53 | + experimental: false, | |
| 54 | + }, | |
| 55 | + { | |
| 56 | + slug: 'as_incidence_rate', | |
| 57 | + name: 'Age-standardized incidence rate', | |
| 58 | + description: 'Incidence per 100,000 person-years, age-standardized to the standard population declared by the source. Rates standardized to different standard populations are never compared in one ranking.', | |
| 59 | + formula: 'epidemiology_observations.value WHERE metric = as_incidence_rate (standard_population fixed per scope)', | |
| 60 | + formulaVersion: 'ci-asir-v1', | |
| 61 | + unit: 'per_100k', | |
| 62 | + higherIsWorse: true, | |
| 63 | + aggregation: 'none', | |
| 64 | + validDimensions: ['geography', 'sex', 'year'], | |
| 65 | + sourceSlugs: ['cdc-wonder', 'seer', 'iarc-globocan'], | |
| 66 | + category: 'burden', | |
| 67 | + eligibility: { requires: ['as_incidence_rate'], entityLevel: 'top' }, | |
| 68 | + experimental: false, | |
| 69 | + }, | |
| 70 | + { | |
| 71 | + slug: 'as_mortality_rate', | |
| 72 | + name: 'Age-standardized mortality rate', | |
| 73 | + description: 'Deaths per 100,000 person-years, age-standardized to the standard population declared by the source.', | |
| 74 | + formula: 'epidemiology_observations.value WHERE metric = as_mortality_rate (standard_population fixed per scope)', | |
| 75 | + formulaVersion: 'ci-asmr-v1', | |
| 76 | + unit: 'per_100k', | |
| 77 | + higherIsWorse: true, | |
| 78 | + aggregation: 'none', | |
| 79 | + validDimensions: ['geography', 'sex', 'year'], | |
| 80 | + sourceSlugs: ['cdc-wonder', 'seer', 'iarc-globocan'], | |
| 81 | + category: 'burden', | |
| 82 | + eligibility: { requires: ['as_mortality_rate'], entityLevel: 'top' }, | |
| 83 | + experimental: false, | |
| 84 | + }, | |
| 85 | + // --- Lethality --- | |
| 86 | + { | |
| 87 | + slug: 'mortality_incidence_ratio', | |
| 88 | + name: 'Mortality-to-incidence ratio', | |
| 89 | + description: 'Deaths divided by new cases in the same population, year and sex. A crude proxy of lethality; it is not a survival probability and is affected by incidence trends and registration completeness.', | |
| 90 | + formula: 'mortality_count / incidence_count (same geography, year, sex, source family)', | |
| 91 | + formulaVersion: 'ci-mir-v1', | |
| 92 | + unit: 'ratio', | |
| 93 | + higherIsWorse: true, | |
| 94 | + aggregation: 'none', | |
| 95 | + validDimensions: ['geography', 'sex', 'year'], | |
| 96 | + sourceSlugs: ['cdc-wonder', 'seer', 'iarc-globocan'], | |
| 97 | + category: 'lethality', | |
| 98 | + eligibility: { requires: ['incidence_count', 'mortality_count'], minIncidence: 100, entityLevel: 'top' }, | |
| 99 | + experimental: false, | |
| 100 | + }, | |
| 101 | + { | |
| 102 | + slug: 'five_year_survival', | |
| 103 | + name: '5-year relative survival', | |
| 104 | + description: '5-year relative survival for the diagnosis period and population declared by the source. Population survival statistics do not predict an individual outcome.', | |
| 105 | + formula: 'survival_observations.probability WHERE survival_type = relative AND duration_months = 60 AND stage IS NULL', | |
| 106 | + formulaVersion: 'ci-5ys-v1', | |
| 107 | + unit: 'probability', | |
| 108 | + higherIsWorse: false, | |
| 109 | + aggregation: 'none', | |
| 110 | + validDimensions: ['geography', 'sex', 'diagnosis_period'], | |
| 111 | + sourceSlugs: ['seer'], | |
| 112 | + category: 'lethality', | |
| 113 | + eligibility: { requires: ['five_year_relative_survival'], minCohort: 50, entityLevel: 'top' }, | |
| 114 | + experimental: false, | |
| 115 | + }, | |
| 116 | + // --- Clinical research (ClinicalTrials.gov) --- | |
| 117 | + { | |
| 118 | + slug: 'active_trials', | |
| 119 | + name: 'Active clinical trials', | |
| 120 | + description: 'Interventional studies registered on ClinicalTrials.gov whose overall status is RECRUITING, NOT_YET_RECRUITING, ENROLLING_BY_INVITATION or ACTIVE_NOT_RECRUITING and whose conditions map to this cancer or one of its descendants in the NCIt hierarchy.', | |
| 121 | + formula: 'COUNT(DISTINCT trial) FROM trial_conditions JOIN clinical_trials WHERE cancer_id IN descendants(cancer) AND study_type = INTERVENTIONAL AND overall_status IN (RECRUITING, NOT_YET_RECRUITING, ENROLLING_BY_INVITATION, ACTIVE_NOT_RECRUITING)', | |
| 122 | + formulaVersion: 'ci-active-trials-v1', | |
| 123 | + unit: 'count', | |
| 124 | + higherIsWorse: false, | |
| 125 | + aggregation: 'descendants', | |
| 126 | + validDimensions: ['entity_level'], | |
| 127 | + sourceSlugs: ['clinicaltrials'], | |
| 128 | + category: 'trials', | |
| 129 | + eligibility: { entityLevel: 'any' }, | |
| 130 | + experimental: false, | |
| 131 | + }, | |
| 132 | + { | |
| 133 | + slug: 'recruiting_trials', | |
| 134 | + name: 'Recruiting clinical trials', | |
| 135 | + description: 'Interventional studies currently RECRUITING whose conditions map to this cancer or its descendants.', | |
| 136 | + formula: 'COUNT(DISTINCT trial) … overall_status = RECRUITING AND study_type = INTERVENTIONAL', | |
| 137 | + formulaVersion: 'ci-recruiting-trials-v1', | |
| 138 | + unit: 'count', | |
| 139 | + higherIsWorse: false, | |
| 140 | + aggregation: 'descendants', | |
| 141 | + validDimensions: ['entity_level'], | |
| 142 | + sourceSlugs: ['clinicaltrials'], | |
| 143 | + category: 'trials', | |
| 144 | + eligibility: { entityLevel: 'any' }, | |
| 145 | + experimental: false, | |
| 146 | + }, | |
| 147 | + { | |
| 148 | + slug: 'phase3_trials', | |
| 149 | + name: 'Active Phase III trials', | |
| 150 | + description: 'Active interventional studies with PHASE3 among their phases, mapped to this cancer or its descendants.', | |
| 151 | + formula: 'COUNT(DISTINCT trial) … active statuses AND PHASE3 = ANY(phases)', | |
| 152 | + formulaVersion: 'ci-phase3-trials-v1', | |
| 153 | + unit: 'count', | |
| 154 | + higherIsWorse: false, | |
| 155 | + aggregation: 'descendants', | |
| 156 | + validDimensions: ['entity_level'], | |
| 157 | + sourceSlugs: ['clinicaltrials'], | |
| 158 | + category: 'trials', | |
| 159 | + eligibility: { entityLevel: 'any' }, | |
| 160 | + experimental: false, | |
| 161 | + }, | |
| 162 | + // --- Research activity (PubMed) --- | |
| 163 | + { | |
| 164 | + slug: 'publications_5y', | |
| 165 | + name: 'Publications, last 5 years', | |
| 166 | + description: 'PubMed records matching the cancer’s MeSH-anchored query (stored verbatim with the count) with a publication date in the last 5 full years plus the current year.', | |
| 167 | + formula: 'literature_counts.count WHERE window_key = 5y (query stored per row)', | |
| 168 | + formulaVersion: 'ci-pubs-5y-v1', | |
| 169 | + unit: 'count', | |
| 170 | + higherIsWorse: false, | |
| 171 | + aggregation: 'none', | |
| 172 | + validDimensions: ['entity_level'], | |
| 173 | + sourceSlugs: ['pubmed'], | |
| 174 | + category: 'research', | |
| 175 | + eligibility: { entityLevel: 'any', requiresQuery: true }, | |
| 176 | + experimental: false, | |
| 177 | + }, | |
| 178 | + { | |
| 179 | + slug: 'publications_12m', | |
| 180 | + name: 'Publications, last 12 months', | |
| 181 | + description: 'PubMed records matching the cancer’s query with a publication date in the last 12 months.', | |
| 182 | + formula: 'literature_counts.count WHERE window_key = 12m', | |
| 183 | + formulaVersion: 'ci-pubs-12m-v1', | |
| 184 | + unit: 'count', | |
| 185 | + higherIsWorse: false, | |
| 186 | + aggregation: 'none', | |
| 187 | + validDimensions: ['entity_level'], | |
| 188 | + sourceSlugs: ['pubmed'], | |
| 189 | + category: 'research', | |
| 190 | + eligibility: { entityLevel: 'any', requiresQuery: true }, | |
| 191 | + experimental: false, | |
| 192 | + }, | |
| 193 | + { | |
| 194 | + slug: 'publication_growth', | |
| 195 | + name: 'Publication growth', | |
| 196 | + description: 'Ratio of publications in the last 12 months to the average annual publications over the preceding 5-year window. Values above 1 indicate accelerating literature.', | |
| 197 | + formula: 'publications_12m / (publications_5y_prior / 5)', | |
| 198 | + formulaVersion: 'ci-pub-growth-v1', | |
| 199 | + unit: 'ratio', | |
| 200 | + higherIsWorse: null, | |
| 201 | + aggregation: 'none', | |
| 202 | + validDimensions: ['entity_level'], | |
| 203 | + sourceSlugs: ['pubmed'], | |
| 204 | + category: 'trend', | |
| 205 | + eligibility: { entityLevel: 'any', minPublications5y: 50 }, | |
| 206 | + experimental: false, | |
| 207 | + }, | |
| 208 | + // --- Molecular knowledge --- | |
| 209 | + { | |
| 210 | + slug: 'curated_evidence_items', | |
| 211 | + name: 'Curated clinical evidence items', | |
| 212 | + description: 'Accepted CIViC evidence items whose disease maps to this cancer or its descendants.', | |
| 213 | + formula: 'COUNT(civic_evidence_items) WHERE status = ACCEPTED AND cancer_id IN descendants(cancer)', | |
| 214 | + formulaVersion: 'ci-civic-evidence-v1', | |
| 215 | + unit: 'count', | |
| 216 | + higherIsWorse: false, | |
| 217 | + aggregation: 'descendants', | |
| 218 | + validDimensions: ['entity_level'], | |
| 219 | + sourceSlugs: ['civic'], | |
| 220 | + category: 'molecular', | |
| 221 | + eligibility: { entityLevel: 'any' }, | |
| 222 | + experimental: false, | |
| 223 | + }, | |
| 224 | + { | |
| 225 | + slug: 'associated_genes', | |
| 226 | + name: 'Genes with curated or cohort evidence', | |
| 227 | + description: 'Distinct genes linked to this cancer (or descendants) by an accepted CIViC evidence item or by a GDC cohort frequency of at least 5% with ≥ 20 affected cases.', | |
| 228 | + formula: 'COUNT(DISTINCT gene) FROM (civic accepted evidence ∪ gdc frequency ≥ 0.05 AND cases_affected ≥ 20)', | |
| 229 | + formulaVersion: 'ci-genes-v1', | |
| 230 | + unit: 'count', | |
| 231 | + higherIsWorse: false, | |
| 232 | + aggregation: 'descendants', | |
| 233 | + validDimensions: ['entity_level'], | |
| 234 | + sourceSlugs: ['civic', 'gdc'], | |
| 235 | + category: 'molecular', | |
| 236 | + eligibility: { entityLevel: 'any' }, | |
| 237 | + experimental: false, | |
| 238 | + }, | |
| 239 | + { | |
| 240 | + slug: 'genomic_cohorts', | |
| 241 | + name: 'Public genomic cohorts', | |
| 242 | + description: 'Open-access genomic studies (GDC projects) whose disease maps to this cancer or its descendants.', | |
| 243 | + formula: 'COUNT(genomic_cohorts) WHERE cancer_id IN descendants(cancer)', | |
| 244 | + formulaVersion: 'ci-cohorts-v1', | |
| 245 | + unit: 'count', | |
| 246 | + higherIsWorse: false, | |
| 247 | + aggregation: 'descendants', | |
| 248 | + validDimensions: ['entity_level'], | |
| 249 | + sourceSlugs: ['gdc'], | |
| 250 | + category: 'molecular', | |
| 251 | + eligibility: { entityLevel: 'any' }, | |
| 252 | + experimental: false, | |
| 253 | + }, | |
| 254 | + // --- Gap indexes (derived; require burden) --- | |
| 255 | + { | |
| 256 | + slug: 'trial_gap', | |
| 257 | + name: 'Trial Gap Index', | |
| 258 | + description: 'Burden percentile minus active-trial percentile within the same scope. Positive values flag cancers with high mortality burden but comparatively few active trials. A quantitative signal, not an accusation (CLAUDE.md §324).', | |
| 259 | + formula: 'percentile(mortality_count) − percentile(active_trials)', | |
| 260 | + formulaVersion: 'ci-trial-gap-v1', | |
| 261 | + unit: 'percentile_points', | |
| 262 | + higherIsWorse: true, | |
| 263 | + aggregation: 'none', | |
| 264 | + validDimensions: ['geography', 'year'], | |
| 265 | + sourceSlugs: ['clinicaltrials', 'cdc-wonder', 'seer', 'iarc-globocan'], | |
| 266 | + category: 'unmet_need', | |
| 267 | + eligibility: { requires: ['mortality_count', 'active_trials'], entityLevel: 'top' }, | |
| 268 | + experimental: false, | |
| 269 | + }, | |
| 270 | + { | |
| 271 | + slug: 'research_gap', | |
| 272 | + name: 'Research Gap Index', | |
| 273 | + description: 'Burden percentile minus research-activity percentile (publications, last 5 years) within the same scope. Positive values flag cancers with high mortality burden but comparatively little literature.', | |
| 274 | + formula: 'percentile(mortality_count) − percentile(publications_5y)', | |
| 275 | + formulaVersion: 'ci-research-gap-v1', | |
| 276 | + unit: 'percentile_points', | |
| 277 | + higherIsWorse: true, | |
| 278 | + aggregation: 'none', | |
| 279 | + validDimensions: ['geography', 'year'], | |
| 280 | + sourceSlugs: ['pubmed', 'cdc-wonder', 'seer', 'iarc-globocan'], | |
| 281 | + category: 'unmet_need', | |
| 282 | + eligibility: { requires: ['mortality_count', 'publications_5y'], entityLevel: 'top' }, | |
| 283 | + experimental: false, | |
| 284 | + }, | |
| 285 | +]; | |
added
packages/database/src/seed.ts
+49 −0
@@ -0,0 +1,49 @@ | ||
| 1 | +import { fileURLToPath } from 'node:url'; | |
| 2 | +import path from 'node:path'; | |
| 3 | +import { eq, sql } from 'drizzle-orm'; | |
| 4 | +import { getDb, closeDb } from './client.js'; | |
| 5 | +import { geographies, metricDefinitions } from './schema/index.js'; | |
| 6 | +import { mintId } from './ids.js'; | |
| 7 | +import { METRIC_CATALOG } from './seed-data/metrics.js'; | |
| 8 | +import { GEOGRAPHY_SEED } from './seed-data/geographies.js'; | |
| 9 | + | |
| 10 | +/** | |
| 11 | + * Seeds only system data (CLAUDE.md §358): metric definitions and canonical geographies. | |
| 12 | + * Scientific data is never seeded — it comes from connectors. Sources are synced from connector | |
| 13 | + * manifests by `pnpm cix sources:sync` (lives in the root CLI to avoid a package cycle). | |
| 14 | + */ | |
| 15 | +export async function seed(): Promise<void> { | |
| 16 | + const db = getDb({ max: 2 }); | |
| 17 | + for (const m of METRIC_CATALOG) { | |
| 18 | + const [existing] = await db.select({ id: metricDefinitions.id }).from(metricDefinitions).where(eq(metricDefinitions.slug, m.slug)).limit(1); | |
| 19 | + const values = { ...m, updatedAt: new Date() }; | |
| 20 | + if (existing) await db.update(metricDefinitions).set(values).where(eq(metricDefinitions.id, existing.id)); | |
| 21 | + else await db.insert(metricDefinitions).values({ id: await mintId(db, 'METRIC'), ...m }); | |
| 22 | + } | |
| 23 | + const bySlug = new Map<string, string>(); | |
| 24 | + for (const g of GEOGRAPHY_SEED) { | |
| 25 | + const [existing] = await db.select({ id: geographies.id }).from(geographies).where(eq(geographies.slug, g.slug)).limit(1); | |
| 26 | + const parentId = g.parentSlug ? bySlug.get(g.parentSlug) ?? null : null; | |
| 27 | + if (existing) { | |
| 28 | + await db.update(geographies).set({ name: g.name, kind: g.kind, iso2: g.iso2 ?? null, iso3: g.iso3 ?? null, parentId, whoRegion: g.whoRegion ?? null }).where(eq(geographies.id, existing.id)); | |
| 29 | + bySlug.set(g.slug, existing.id); | |
| 30 | + } else { | |
| 31 | + const id = await mintId(db, 'GEO'); | |
| 32 | + await db.insert(geographies).values({ id, slug: g.slug, name: g.name, kind: g.kind, iso2: g.iso2 ?? null, iso3: g.iso3 ?? null, parentId, whoRegion: g.whoRegion ?? null }); | |
| 33 | + bySlug.set(g.slug, id); | |
| 34 | + } | |
| 35 | + } | |
| 36 | + const [{ n }] = (await db.execute<{ n: string }>(sql`SELECT count(*)::text AS n FROM metric_definitions`)) as unknown as [{ n: string }]; | |
| 37 | + console.log(`[seed] metrics=${n} geographies=${GEOGRAPHY_SEED.length}`); | |
| 38 | +} | |
| 39 | + | |
| 40 | +const isMain = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url); | |
| 41 | +if (isMain) { | |
| 42 | + seed() | |
| 43 | + .then(() => closeDb()) | |
| 44 | + .catch(async (e) => { | |
| 45 | + console.error('[seed] failed', e); | |
| 46 | + await closeDb(); | |
| 47 | + process.exit(1); | |
| 48 | + }); | |
| 49 | +} | |
added
packages/database/tsconfig.json
+8 −0
@@ -0,0 +1,8 @@ | ||
| 1 | +{ | |
| 2 | + "extends": "../../tsconfig.base.json", | |
| 3 | + "compilerOptions": { | |
| 4 | + "rootDir": ".", | |
| 5 | + "noEmit": true | |
| 6 | + }, | |
| 7 | + "include": ["src", "drizzle.config.ts"] | |
| 8 | +} | |
added
packages/ontology/package.json
+27 −0
@@ -0,0 +1,27 @@ | ||
| 1 | +{ | |
| 2 | + "name": "@cancerindex/ontology", | |
| 3 | + "version": "0.1.0", | |
| 4 | + "private": true, | |
| 5 | + "type": "module", | |
| 6 | + "exports": { | |
| 7 | + ".": { | |
| 8 | + "types": "./src/index.ts", | |
| 9 | + "default": "./src/index.ts" | |
| 10 | + } | |
| 11 | + }, | |
| 12 | + "scripts": { | |
| 13 | + "build": "tsc -p tsconfig.json --noEmit", | |
| 14 | + "typecheck": "tsc -p tsconfig.json --noEmit", | |
| 15 | + "test": "vitest run --passWithNoTests" | |
| 16 | + }, | |
| 17 | + "dependencies": { | |
| 18 | + "@cancerindex/database": "workspace:*", | |
| 19 | + "@cancerindex/shared": "workspace:*", | |
| 20 | + "drizzle-orm": "^0.45.0" | |
| 21 | + }, | |
| 22 | + "devDependencies": { | |
| 23 | + "@types/node": "^24.0.0", | |
| 24 | + "typescript": "^5.9.3", | |
| 25 | + "vitest": "^3.2.0" | |
| 26 | + } | |
| 27 | +} | |
added
packages/ontology/src/index.ts
+3 −0
@@ -0,0 +1,3 @@ | ||
| 1 | +export * from './qualifiers.js'; | |
| 2 | +export * from './reconcile.js'; | |
| 3 | +export * from './top-level.js'; | |
added
packages/ontology/src/qualifiers.ts
+76 −0
@@ -0,0 +1,76 @@ | ||
| 1 | +/** | |
| 2 | + * NCIt neoplasm concepts that are *qualified disease states* rather than taxonomy nodes | |
| 3 | + * (CLAUDE.md §217-218: "Do not create a unique canonical cancer entity for every arbitrary | |
| 4 | + * combination"). Stage / recurrence / resectability / laterality / treatment-history qualified | |
| 5 | + * concepts are retained as source records and mapped to their base disease, but are not canonical | |
| 6 | + * cancers. | |
| 7 | + * | |
| 8 | + * Rules are deterministic and tested (test/qualifiers.test.ts). When in doubt a concept is KEPT | |
| 9 | + * (a false negative costs an extra node; a false positive loses a real disease entity). | |
| 10 | + */ | |
| 11 | + | |
| 12 | +/** Words that mark a clinical state, never a disease identity. */ | |
| 13 | +const STATE_WORDS = | |
| 14 | + /\b(stage\s?[0iv]+[abcs]?\d?|stages?\s[0iv]+|recurrent|refractory|relapsed|resectable|unresectable|borderline resectable|localized|locally advanced|advanced|metastatic|progressive|residual|persistent|newly diagnosed|untreated|previously treated|previously untreated|treatment[- ]naive|bilateral|unilateral|synchronous|metachronous|multifocal|multicentric|unifocal|occult|extensive[- ]stage|limited[- ]stage|in remission|in relapse|in complete remission|minimal residual disease|post[- ]?transplant(ation)?|aggravated|complicated|uncomplicated|with (unknown|known) primary site|of unknown primary origin)\b/i; | |
| 15 | + | |
| 16 | +/** Staging-system suffixes: "… AJCC v8", "… FIGO Stage IIIC". */ | |
| 17 | +const STAGING_SUFFIX = /\s(by\s)?(ajcc|figo|uicc|inrg|inss|cog|enneking|lugano|ann arbor|binet|rai|durie[- ]salmon|r?-?iss|tnm)(\sv\d+)?(\sstage\s?[0iv]+[abc]?)?$/i; | |
| 18 | + | |
| 19 | +/** Laterality prefixes only count when followed by an organ-level disease. */ | |
| 20 | +const LATERALITY_PREFIX = /^(left|right)\s/i; | |
| 21 | + | |
| 22 | +/** | |
| 23 | + * Recognized disease names that contain a state word as part of their identity. | |
| 24 | + * Kept deliberately short: the regexes above are the primary mechanism. | |
| 25 | + */ | |
| 26 | +const KEEP_EXACT = new Set( | |
| 27 | + [ | |
| 28 | + 'Progressive Multifocal Leukoencephalopathy', | |
| 29 | + 'Persistent Mullerian Duct Syndrome', | |
| 30 | + 'Advanced Sclerosing Systemic Mastocytosis', | |
| 31 | + 'Residual Neuroblastoma', | |
| 32 | + 'Cancer of Unknown Primary Origin', | |
| 33 | + 'Carcinoma of Unknown Primary Origin', | |
| 34 | + 'Neoplasm of Unknown Primary Origin', | |
| 35 | + 'Adenocarcinoma of Unknown Primary Origin', | |
| 36 | + 'Squamous Cell Carcinoma of Unknown Primary Origin', | |
| 37 | + 'Neuroendocrine Carcinoma of Unknown Primary Origin', | |
| 38 | + 'Melanoma of Unknown Primary Origin', | |
| 39 | + 'Metastatic Malignant Neoplasm of Unknown Primary Origin', | |
| 40 | + 'Multifocal Motor Neuropathy', | |
| 41 | + 'Multicentric Castleman Disease', | |
| 42 | + 'Idiopathic Multicentric Castleman Disease', | |
| 43 | + 'HHV8-Associated Multicentric Castleman Disease', | |
| 44 | + 'Multicentric Reticulohistiocytosis', | |
| 45 | + 'Multifocal Langerhans Cell Histiocytosis', | |
| 46 | + 'Unifocal Langerhans Cell Histiocytosis', | |
| 47 | + 'Multifocal Osteosarcoma', | |
| 48 | + 'Synchronous Multiple Primary Malignant Neoplasm', | |
| 49 | + 'Metachronous Multiple Primary Malignant Neoplasm', | |
| 50 | + 'Occult Breast Carcinoma', | |
| 51 | + 'Occult Primary Malignant Neoplasm', | |
| 52 | + ].map((s) => s.toLowerCase()), | |
| 53 | +); | |
| 54 | + | |
| 55 | +/** True when a preferred name denotes a qualified state rather than a disease taxonomy node. */ | |
| 56 | +export function isQualifiedState(preferredName: string): boolean { | |
| 57 | + const name = preferredName.trim().toLowerCase(); | |
| 58 | + if (KEEP_EXACT.has(name)) return false; | |
| 59 | + if (STAGING_SUFFIX.test(name)) return true; | |
| 60 | + if (STATE_WORDS.test(name)) return true; | |
| 61 | + if (LATERALITY_PREFIX.test(name)) return true; | |
| 62 | + return false; | |
| 63 | +} | |
| 64 | + | |
| 65 | +/** Strip qualifiers to find the base disease label (used to attach qualified states to their disease). */ | |
| 66 | +export function baseDiseaseLabel(preferredName: string): string { | |
| 67 | + let s = preferredName.trim(); | |
| 68 | + for (let i = 0; i < 5; i++) { | |
| 69 | + const before = s; | |
| 70 | + s = s.replace(STAGING_SUFFIX, ''); | |
| 71 | + s = s.replace(/^(stage\s?[0iv]+[abcs]?\d?|recurrent|refractory|relapsed|resectable|unresectable|borderline resectable|localized|locally advanced|advanced|metastatic|progressive|residual|persistent|newly diagnosed|untreated|previously treated|previously untreated|treatment[- ]naive|bilateral|unilateral|left|right|synchronous|metachronous|multifocal|multicentric|unifocal|occult|extensive[- ]stage|limited[- ]stage)\s+/i, ''); | |
| 72 | + s = s.replace(/\s+(stage\s?[0iv]+[abcs]?\d?|in remission|in relapse|in complete remission)$/i, ''); | |
| 73 | + if (s === before) break; | |
| 74 | + } | |
| 75 | + return s.trim(); | |
| 76 | +} | |
added
packages/ontology/src/reconcile.ts
+106 −0
@@ -0,0 +1,106 @@ | ||
| 1 | +import { and, eq, inArray, sql } from 'drizzle-orm'; | |
| 2 | +import { normalizeLabel, uninvertMeshTerm, type MatchType } from '@cancerindex/shared'; | |
| 3 | +import { type Database, cancerAliases, cancerCodes, cancers } from '@cancerindex/database'; | |
| 4 | + | |
| 5 | +export interface CancerMatch { | |
| 6 | + cancerId: string; | |
| 7 | + matchType: MatchType; | |
| 8 | + confidence: number; | |
| 9 | + via: string; // human-readable explanation | |
| 10 | +} | |
| 11 | + | |
| 12 | +/** | |
| 13 | + * Cancer label reconciliation (CLAUDE.md §69): identifiers → curated aliases → normalized strings. | |
| 14 | + * Never uses embeddings or an LLM to merge. Returns null (→ unresolved queue) when unsure. | |
| 15 | + */ | |
| 16 | +export class CancerResolver { | |
| 17 | + private aliasCache = new Map<string, { cancerId: string; aliasType: string }[]>(); | |
| 18 | + private codeCache = new Map<string, string>(); | |
| 19 | + | |
| 20 | + constructor(private readonly db: Database) {} | |
| 21 | + | |
| 22 | + /** Preload alias + code lookups in memory for bulk reconciliation. */ | |
| 23 | + async warm(): Promise<void> { | |
| 24 | + const rows = await this.db | |
| 25 | + .select({ cancerId: cancerAliases.cancerId, normalized: cancerAliases.normalized, aliasType: cancerAliases.aliasType }) | |
| 26 | + .from(cancerAliases) | |
| 27 | + .innerJoin(cancers, eq(cancers.id, cancerAliases.cancerId)) | |
| 28 | + .where(eq(cancers.status, 'active')); | |
| 29 | + this.aliasCache.clear(); | |
| 30 | + for (const r of rows) { | |
| 31 | + const arr = this.aliasCache.get(r.normalized) ?? []; | |
| 32 | + arr.push({ cancerId: r.cancerId, aliasType: r.aliasType }); | |
| 33 | + this.aliasCache.set(r.normalized, arr); | |
| 34 | + } | |
| 35 | + const codes = await this.db.select({ cancerId: cancerCodes.cancerId, system: cancerCodes.system, code: cancerCodes.code }).from(cancerCodes).where(inArray(cancerCodes.matchType, ['EXACT_IDENTIFIER', 'CURATED_EXACT', 'ONTOLOGY_EXACT'])); | |
| 36 | + this.codeCache.clear(); | |
| 37 | + for (const c of codes) this.codeCache.set(`${c.system}:${c.code}`, c.cancerId); | |
| 38 | + } | |
| 39 | + | |
| 40 | + get warmed(): boolean { | |
| 41 | + return this.aliasCache.size > 0; | |
| 42 | + } | |
| 43 | + | |
| 44 | + /** Resolve by external identifier (NCIt code, DOID, OncoTree code, UMLS CUI, MeSH UI…). */ | |
| 45 | + byCode(system: string, code: string): CancerMatch | null { | |
| 46 | + const id = this.codeCache.get(`${system}:${code}`); | |
| 47 | + return id ? { cancerId: id, matchType: 'EXACT_IDENTIFIER', confidence: 1, via: `${system}:${code}` } : null; | |
| 48 | + } | |
| 49 | + | |
| 50 | + /** Resolve a free-text label. Ambiguous alias hits (≠ 1 distinct cancer) are returned as null. */ | |
| 51 | + byLabel(label: string, opts: { allowMeshInversion?: boolean } = {}): CancerMatch | null { | |
| 52 | + const candidates = [label]; | |
| 53 | + if (opts.allowMeshInversion && label.includes(',')) candidates.push(uninvertMeshTerm(label)); | |
| 54 | + // Common registry phrasings: "Cancer of the X" → "X cancer"; "Carcinoma of X" → "X carcinoma" | |
| 55 | + const m = /^(cancer|carcinoma|neoplasm|tumou?r|sarcoma|lymphoma|leukemia|adenocarcinoma|melanoma) of (the )?(.+)$/i.exec(label); | |
| 56 | + if (m) candidates.push(`${m[3]} ${m[1]}`); | |
| 57 | + for (const c of candidates) { | |
| 58 | + const norm = normalizeLabel(c); | |
| 59 | + if (!norm) continue; | |
| 60 | + const hits = this.aliasCache.get(norm); | |
| 61 | + if (!hits || hits.length === 0) continue; | |
| 62 | + const distinct = [...new Set(hits.map((h) => h.cancerId))]; | |
| 63 | + if (distinct.length === 1) { | |
| 64 | + const preferred = hits.some((h) => h.aliasType === 'preferred'); | |
| 65 | + return { cancerId: distinct[0]!, matchType: preferred ? 'ONTOLOGY_EXACT' : 'ALIAS', confidence: preferred ? 0.98 : 0.9, via: `alias "${c}"` }; | |
| 66 | + } | |
| 67 | + // Ambiguous: prefer the cancer where this string is the preferred name. | |
| 68 | + const pref = hits.filter((h) => h.aliasType === 'preferred'); | |
| 69 | + const prefDistinct = [...new Set(pref.map((h) => h.cancerId))]; | |
| 70 | + if (prefDistinct.length === 1) return { cancerId: prefDistinct[0]!, matchType: 'ONTOLOGY_EXACT', confidence: 0.9, via: `preferred name "${c}" (alias also used by ${distinct.length - 1} other concept(s))` }; | |
| 71 | + return null; | |
| 72 | + } | |
| 73 | + return null; | |
| 74 | + } | |
| 75 | + | |
| 76 | + /** Try identifiers first, then labels (CLAUDE.md §143, §221). */ | |
| 77 | + resolve(input: { codes?: Array<{ system: string; code: string }>; labels?: string[]; allowMeshInversion?: boolean }): CancerMatch | null { | |
| 78 | + for (const c of input.codes ?? []) { | |
| 79 | + const hit = this.byCode(c.system, c.code); | |
| 80 | + if (hit) return hit; | |
| 81 | + } | |
| 82 | + for (const l of input.labels ?? []) { | |
| 83 | + const hit = this.byLabel(l, { allowMeshInversion: input.allowMeshInversion }); | |
| 84 | + if (hit) return hit; | |
| 85 | + } | |
| 86 | + return null; | |
| 87 | + } | |
| 88 | + | |
| 89 | + /** Fuzzy suggestion for the curation queue only (never auto-accepted). */ | |
| 90 | + async suggest(label: string): Promise<{ cancerId: string; name: string; score: number } | null> { | |
| 91 | + const norm = normalizeLabel(label); | |
| 92 | + if (norm.length < 4) return null; | |
| 93 | + const rows = await this.db.execute<{ cancer_id: string; canonical_name: string; score: number }>(sql` | |
| 94 | + SELECT a.cancer_id, c.canonical_name, similarity(a.normalized, ${norm}) AS score | |
| 95 | + FROM cancer_aliases a JOIN cancers c ON c.id = a.cancer_id | |
| 96 | + WHERE c.status = 'active' AND a.normalized % ${norm} | |
| 97 | + ORDER BY score DESC LIMIT 1`); | |
| 98 | + const r = rows[0]; | |
| 99 | + return r && r.score >= 0.6 ? { cancerId: r.cancer_id, name: r.canonical_name, score: Number(r.score) } : null; | |
| 100 | + } | |
| 101 | +} | |
| 102 | + | |
| 103 | +export async function loadCancerIdBySlug(db: Database, slug: string): Promise<string | null> { | |
| 104 | + const [row] = await db.select({ id: cancers.id }).from(cancers).where(and(eq(cancers.slug, slug), eq(cancers.status, 'active'))).limit(1); | |
| 105 | + return row?.id ?? null; | |
| 106 | +} | |
added
packages/ontology/src/top-level.ts
+75 −0
@@ -0,0 +1,75 @@ | ||
| 1 | +/** | |
| 2 | + * Explicitly curated metadata (CLAUDE.md §8 allows curated metadata; §246-247 require a | |
| 3 | + * mutually exclusive top-level set for the global master ranking). | |
| 4 | + * | |
| 5 | + * The top-level set follows the GLOBOCAN / ICD-10 site groups so that registry burden estimates | |
| 6 | + * can be attached without double counting. Each entry maps a site group to its NCIt anchor concept | |
| 7 | + * (codes verified against the EVS REST API on 2026-09-08, NCIt 26.08e) and to the ICD-10 range | |
| 8 | + * used by registries. Codes are recorded on `cancer_codes` with match type CURATED_EXACT. | |
| 9 | + * | |
| 10 | + * This list is *not* the taxonomy — it is a ranking scope definition (entity_level = "top"). | |
| 11 | + */ | |
| 12 | +export interface TopLevelCancer { | |
| 13 | + key: string; | |
| 14 | + name: string; // display name (registry phrasing); the canonical NCIt name is kept on the entity | |
| 15 | + ncit: string; | |
| 16 | + icd10: string[]; // ICD-10 code ranges, e.g. "C33-C34" | |
| 17 | + hematologic?: boolean; | |
| 18 | + globocanCode?: number; // GLOBOCAN cancer code (reference only; ingestion is gated by license review) | |
| 19 | + notes?: string; | |
| 20 | +} | |
| 21 | + | |
| 22 | +export const TOP_LEVEL_CANCERS: TopLevelCancer[] = [ | |
| 23 | + { key: 'lip-oral-cavity', name: 'Lip and Oral Cavity Cancer', ncit: 'C9316', icd10: ['C00-C06'], globocanCode: 1 }, | |
| 24 | + { key: 'salivary-gland', name: 'Salivary Gland Cancer', ncit: 'C3811', icd10: ['C07-C08'], globocanCode: 2 }, | |
| 25 | + { key: 'oropharynx', name: 'Oropharyngeal Cancer', ncit: 'C7398', icd10: ['C09-C10'], globocanCode: 3 }, | |
| 26 | + { key: 'nasopharynx', name: 'Nasopharyngeal Cancer', ncit: 'C9321', icd10: ['C11'], globocanCode: 4 }, | |
| 27 | + { key: 'hypopharynx', name: 'Hypopharyngeal Cancer', ncit: 'C7190', icd10: ['C12-C13'], globocanCode: 5 }, | |
| 28 | + { key: 'esophagus', name: 'Esophageal Cancer', ncit: 'C7478', icd10: ['C15'], globocanCode: 6 }, | |
| 29 | + { key: 'stomach', name: 'Stomach Cancer', ncit: 'C9331', icd10: ['C16'], globocanCode: 7 }, | |
| 30 | + { key: 'colon', name: 'Colon Cancer', ncit: 'C9242', icd10: ['C18'], globocanCode: 8 }, | |
| 31 | + { key: 'rectum', name: 'Rectal Cancer', ncit: 'C7418', icd10: ['C19-C20'], globocanCode: 9 }, | |
| 32 | + { key: 'anus', name: 'Anal Cancer', ncit: 'C7379', icd10: ['C21'], globocanCode: 10 }, | |
| 33 | + { key: 'liver', name: 'Liver Cancer', ncit: 'C190593', icd10: ['C22'], globocanCode: 11, notes: 'ICD-10 C22 includes intrahepatic bile ducts' }, | |
| 34 | + { key: 'gallbladder', name: 'Gallbladder Cancer', ncit: 'C7481', icd10: ['C23-C24'], globocanCode: 12, notes: 'ICD-10 C23-C24 includes extrahepatic biliary tract; NCIt anchor is gallbladder' }, | |
| 35 | + { key: 'pancreas', name: 'Pancreatic Cancer', ncit: 'C9005', icd10: ['C25'], globocanCode: 13 }, | |
| 36 | + { key: 'larynx', name: 'Laryngeal Cancer', ncit: 'C7484', icd10: ['C32'], globocanCode: 14 }, | |
| 37 | + { key: 'lung', name: 'Lung Cancer', ncit: 'C7377', icd10: ['C33-C34'], globocanCode: 15 }, | |
| 38 | + { key: 'melanoma-skin', name: 'Melanoma of Skin', ncit: 'C3510', icd10: ['C43'], globocanCode: 16 }, | |
| 39 | + { key: 'non-melanoma-skin', name: 'Non-Melanoma Skin Cancer', ncit: 'C4914', icd10: ['C44'], globocanCode: 17, notes: 'registries differ on inclusion (CLAUDE.md §210)' }, | |
| 40 | + { key: 'mesothelioma', name: 'Mesothelioma', ncit: 'C4456', icd10: ['C45'], globocanCode: 18 }, | |
| 41 | + { key: 'kaposi-sarcoma', name: 'Kaposi Sarcoma', ncit: 'C9087', icd10: ['C46'], globocanCode: 19 }, | |
| 42 | + { key: 'breast', name: 'Breast Cancer', ncit: 'C9335', icd10: ['C50'], globocanCode: 20 }, | |
| 43 | + { key: 'vulva', name: 'Vulvar Cancer', ncit: 'C7502', icd10: ['C51'], globocanCode: 21 }, | |
| 44 | + { key: 'vagina', name: 'Vaginal Cancer', ncit: 'C7410', icd10: ['C52'], globocanCode: 22 }, | |
| 45 | + { key: 'cervix', name: 'Cervical Cancer', ncit: 'C9311', icd10: ['C53'], globocanCode: 23 }, | |
| 46 | + { key: 'corpus-uteri', name: 'Uterine Corpus Cancer', ncit: 'C3556', icd10: ['C54'], globocanCode: 24 }, | |
| 47 | + { key: 'ovary', name: 'Ovarian Cancer', ncit: 'C7431', icd10: ['C56'], globocanCode: 25 }, | |
| 48 | + { key: 'penis', name: 'Penile Cancer', ncit: 'C7547', icd10: ['C60'], globocanCode: 26 }, | |
| 49 | + { key: 'prostate', name: 'Prostate Cancer', ncit: 'C7378', icd10: ['C61'], globocanCode: 27 }, | |
| 50 | + { key: 'testis', name: 'Testicular Cancer', ncit: 'C7251', icd10: ['C62'], globocanCode: 28 }, | |
| 51 | + { key: 'kidney', name: 'Kidney Cancer', ncit: 'C7548', icd10: ['C64-C65'], globocanCode: 29 }, | |
| 52 | + { key: 'bladder', name: 'Bladder Cancer', ncit: 'C9334', icd10: ['C67'], globocanCode: 30 }, | |
| 53 | + { key: 'brain-cns', name: 'Brain and Central Nervous System Cancer', ncit: 'C4627', icd10: ['C70-C72'], globocanCode: 31 }, | |
| 54 | + { key: 'thyroid', name: 'Thyroid Cancer', ncit: 'C7510', icd10: ['C73'], globocanCode: 32 }, | |
| 55 | + { key: 'hodgkin-lymphoma', name: 'Hodgkin Lymphoma', ncit: 'C9357', icd10: ['C81'], hematologic: true, globocanCode: 33 }, | |
| 56 | + { key: 'non-hodgkin-lymphoma', name: 'Non-Hodgkin Lymphoma', ncit: 'C3211', icd10: ['C82-C86', 'C96'], hematologic: true, globocanCode: 34 }, | |
| 57 | + { key: 'multiple-myeloma', name: 'Multiple Myeloma', ncit: 'C3242', icd10: ['C88', 'C90'], hematologic: true, globocanCode: 35 }, | |
| 58 | + { key: 'leukemia', name: 'Leukemia', ncit: 'C3161', icd10: ['C91-C95'], hematologic: true, globocanCode: 36 }, | |
| 59 | +]; | |
| 60 | + | |
| 61 | +export const TOP_LEVEL_BY_NCIT = new Map(TOP_LEVEL_CANCERS.map((t) => [t.ncit, t])); | |
| 62 | + | |
| 63 | +/** NCIt roots used to build the multi-dimensional hierarchy (verified 2026-09-08). */ | |
| 64 | +export const NCIT_ROOTS = { | |
| 65 | + neoplasm: 'C3262', | |
| 66 | + malignantNeoplasm: 'C9305', | |
| 67 | + neoplasmBySite: 'C3263', | |
| 68 | + neoplasmByMorphology: 'C4741', | |
| 69 | + carcinoma: 'C2916', | |
| 70 | + malignantSolidNeoplasm: 'C132146', | |
| 71 | + hematopoieticLymphoidCellNeoplasm: 'C27134', | |
| 72 | + hematopoieticLymphaticSystemNeoplasm: 'C35813', | |
| 73 | + childhoodMalignantNeoplasm: 'C4005', | |
| 74 | + nervousSystemNeoplasm: 'C3268', | |
| 75 | +} as const; | |
added
packages/ontology/test/qualifiers.test.ts
+55 −0
@@ -0,0 +1,55 @@ | ||
| 1 | +import { describe, expect, it } from 'vitest'; | |
| 2 | +import { isQualifiedState, baseDiseaseLabel } from '../src/qualifiers.js'; | |
| 3 | + | |
| 4 | +describe('isQualifiedState', () => { | |
| 5 | + it('flags stage / recurrence / resectability concepts', () => { | |
| 6 | + for (const n of [ | |
| 7 | + 'Stage IV Pancreatic Cancer AJCC v8', | |
| 8 | + 'Stage IIIA Lung Cancer AJCC v7', | |
| 9 | + 'Recurrent Glioblastoma', | |
| 10 | + 'Refractory Acute Myeloid Leukemia', | |
| 11 | + 'Relapsed Multiple Myeloma', | |
| 12 | + 'Unresectable Hepatocellular Carcinoma', | |
| 13 | + 'Locally Advanced Rectal Carcinoma', | |
| 14 | + 'Metastatic Breast Carcinoma', | |
| 15 | + 'Newly Diagnosed Glioblastoma', | |
| 16 | + 'Pancreatic Adenocarcinoma Stage II', | |
| 17 | + 'Bilateral Breast Carcinoma', | |
| 18 | + 'Recurrent Childhood Acute Lymphoblastic Leukemia', | |
| 19 | + ]) | |
| 20 | + expect(isQualifiedState(n), n).toBe(true); | |
| 21 | + }); | |
| 22 | + it('keeps recognized diseases whose names start with a qualifier-like word', () => { | |
| 23 | + for (const n of [ | |
| 24 | + 'Anaplastic Astrocytoma', | |
| 25 | + 'Anaplastic Large Cell Lymphoma', | |
| 26 | + 'Undifferentiated Pleomorphic Sarcoma', | |
| 27 | + 'Invasive Breast Carcinoma', | |
| 28 | + 'Childhood Acute Lymphoblastic Leukemia', | |
| 29 | + 'Adult T-Cell Leukemia/Lymphoma', | |
| 30 | + 'Primary Myelofibrosis', | |
| 31 | + 'Primary Central Nervous System Lymphoma', | |
| 32 | + 'Therapy-Related Acute Myeloid Leukemia', | |
| 33 | + 'Triple-Negative Breast Carcinoma', | |
| 34 | + 'EGFR-Mutant Lung Adenocarcinoma', | |
| 35 | + 'KRAS G12C-Mutant Non-Small Cell Lung Carcinoma', | |
| 36 | + 'High Grade Serous Ovarian Carcinoma', | |
| 37 | + 'Low Grade Glioma', | |
| 38 | + 'Castration-Resistant Prostate Carcinoma', | |
| 39 | + 'Pancreatic Ductal Adenocarcinoma', | |
| 40 | + 'Glioblastoma', | |
| 41 | + 'Diffuse Large B-Cell Lymphoma', | |
| 42 | + 'Acute Myeloid Leukemia with NPM1 Mutation', | |
| 43 | + 'Secondary Acute Myeloid Leukemia', | |
| 44 | + ]) | |
| 45 | + expect(isQualifiedState(n), n).toBe(false); | |
| 46 | + }); | |
| 47 | +}); | |
| 48 | + | |
| 49 | +describe('baseDiseaseLabel', () => { | |
| 50 | + it('strips stage and recurrence qualifiers', () => { | |
| 51 | + expect(baseDiseaseLabel('Stage IV Pancreatic Cancer AJCC v8')).toBe('Pancreatic Cancer'); | |
| 52 | + expect(baseDiseaseLabel('Recurrent Metastatic Breast Carcinoma')).toBe('Breast Carcinoma'); | |
| 53 | + expect(baseDiseaseLabel('Glioblastoma')).toBe('Glioblastoma'); | |
| 54 | + }); | |
| 55 | +}); | |
added
packages/ontology/tsconfig.json
+8 −0
@@ -0,0 +1,8 @@ | ||
| 1 | +{ | |
| 2 | + "extends": "../../tsconfig.base.json", | |
| 3 | + "compilerOptions": { | |
| 4 | + "rootDir": ".", | |
| 5 | + "noEmit": true | |
| 6 | + }, | |
| 7 | + "include": ["src", "test"] | |
| 8 | +} | |
added
packages/ontology/vitest.config.ts
+3 −0
@@ -0,0 +1,3 @@ | ||
| 1 | +import { defineConfig } from 'vitest/config'; | |
| 2 | + | |
| 3 | +export default defineConfig({ test: { include: ['src/**/*.test.ts', 'test/**/*.test.ts'] } }); | |
added
packages/ranking/package.json
+28 −0
@@ -0,0 +1,28 @@ | ||
| 1 | +{ | |
| 2 | + "name": "@cancerindex/ranking", | |
| 3 | + "version": "0.1.0", | |
| 4 | + "private": true, | |
| 5 | + "type": "module", | |
| 6 | + "exports": { | |
| 7 | + ".": { | |
| 8 | + "types": "./src/index.ts", | |
| 9 | + "default": "./src/index.ts" | |
| 10 | + } | |
| 11 | + }, | |
| 12 | + "scripts": { | |
| 13 | + "build": "tsc -p tsconfig.json --noEmit", | |
| 14 | + "typecheck": "tsc -p tsconfig.json --noEmit", | |
| 15 | + "test": "vitest run --passWithNoTests" | |
| 16 | + }, | |
| 17 | + "dependencies": { | |
| 18 | + "@cancerindex/database": "workspace:*", | |
| 19 | + "@cancerindex/ontology": "workspace:*", | |
| 20 | + "@cancerindex/shared": "workspace:*", | |
| 21 | + "drizzle-orm": "^0.45.0" | |
| 22 | + }, | |
| 23 | + "devDependencies": { | |
| 24 | + "@types/node": "^24.0.0", | |
| 25 | + "typescript": "^5.9.3", | |
| 26 | + "vitest": "^3.2.0" | |
| 27 | + } | |
| 28 | +} | |
added
packages/ranking/src/counters.ts
+140 −0
@@ -0,0 +1,140 @@ | ||
| 1 | +import { sql } from 'drizzle-orm'; | |
| 2 | +import type { Database } from '@cancerindex/database'; | |
| 3 | + | |
| 4 | +const ACTIVE_STATUSES = ['RECRUITING', 'NOT_YET_RECRUITING', 'ENROLLING_BY_INVITATION', 'ACTIVE_NOT_RECRUITING']; | |
| 5 | + | |
| 6 | +/** | |
| 7 | + * Recompute entity_counters for every active cancer from canonical relations (CLAUDE.md §287). | |
| 8 | + * Trial / evidence / cohort counts aggregate over the NCIt-hierarchy descendants of each cancer | |
| 9 | + * (a lung-cancer trial mapped to "Lung Adenocarcinoma" counts for "Lung Cancer" too). | |
| 10 | + * Deterministic: a full rebuild in one SQL transaction. | |
| 11 | + */ | |
| 12 | +export async function refreshCounters(db: Database): Promise<number> { | |
| 13 | + await db.execute(sql` | |
| 14 | + CREATE TEMP TABLE IF NOT EXISTS _desc (ancestor varchar(32), descendant varchar(32)) ON COMMIT DROP; | |
| 15 | + `); | |
| 16 | + return db.transaction(async (tx) => { | |
| 17 | + await tx.execute(sql`CREATE TEMP TABLE _desc (ancestor varchar(32), descendant varchar(32)) ON COMMIT DROP`); | |
| 18 | + await tx.execute(sql` | |
| 19 | + INSERT INTO _desc | |
| 20 | + WITH RECURSIVE d AS ( | |
| 21 | + SELECT id AS ancestor, id AS descendant, 0 AS depth FROM cancers WHERE status = 'active' | |
| 22 | + UNION | |
| 23 | + SELECT d.ancestor, h.child_id, d.depth + 1 FROM d JOIN cancer_hierarchy h ON h.parent_id = d.descendant | |
| 24 | + WHERE d.depth < 12 | |
| 25 | + ) | |
| 26 | + SELECT DISTINCT ancestor, descendant FROM d | |
| 27 | + `); | |
| 28 | + await tx.execute(sql`CREATE INDEX ON _desc (descendant)`); | |
| 29 | + await tx.execute(sql`CREATE INDEX ON _desc (ancestor)`); | |
| 30 | + const rows = await tx.execute<{ n: string }>(sql` | |
| 31 | + WITH trial_map AS ( | |
| 32 | + SELECT DISTINCT d.ancestor AS cancer_id, t.id AS trial_id, t.overall_status, t.phases, t.study_type | |
| 33 | + FROM trial_conditions tc JOIN _desc d ON d.descendant = tc.cancer_id JOIN clinical_trials t ON t.id = tc.trial_id | |
| 34 | + WHERE tc.cancer_id IS NOT NULL | |
| 35 | + ), | |
| 36 | + trials AS ( | |
| 37 | + SELECT cancer_id, | |
| 38 | + count(*) AS trial_count, | |
| 39 | + count(*) FILTER (WHERE study_type = 'INTERVENTIONAL' AND overall_status = ANY(${ACTIVE_STATUSES}::text[])) AS active_trial_count, | |
| 40 | + count(*) FILTER (WHERE study_type = 'INTERVENTIONAL' AND overall_status = 'RECRUITING') AS recruiting_trial_count, | |
| 41 | + count(*) FILTER (WHERE study_type = 'INTERVENTIONAL' AND overall_status = ANY(${ACTIVE_STATUSES}::text[]) AND 'PHASE3' = ANY(phases)) AS phase3_trial_count | |
| 42 | + FROM trial_map GROUP BY cancer_id | |
| 43 | + ), | |
| 44 | + pubs AS ( | |
| 45 | + SELECT cancer_id, | |
| 46 | + max(count) FILTER (WHERE window_key = 'all') AS publication_count, | |
| 47 | + max(count) FILTER (WHERE window_key = '5y') AS publication_count_5y, | |
| 48 | + max(count) FILTER (WHERE window_key = '12m') AS publication_count_12m | |
| 49 | + FROM literature_counts GROUP BY cancer_id | |
| 50 | + ), | |
| 51 | + evid AS ( | |
| 52 | + SELECT d.ancestor AS cancer_id, count(DISTINCT e.civic_id) AS evidence_count, | |
| 53 | + count(DISTINCT g) FILTER (WHERE g IS NOT NULL) AS civic_gene_count, | |
| 54 | + count(DISTINCT v) FILTER (WHERE v IS NOT NULL) AS variant_count, | |
| 55 | + count(DISTINCT t) FILTER (WHERE t IS NOT NULL) AS drug_count | |
| 56 | + FROM civic_evidence_items e JOIN _desc d ON d.descendant = e.cancer_id | |
| 57 | + LEFT JOIN LATERAL unnest(e.gene_ids) g ON true | |
| 58 | + LEFT JOIN LATERAL unnest(e.variant_ids) v ON true | |
| 59 | + LEFT JOIN LATERAL unnest(e.therapy_ids) t ON true | |
| 60 | + WHERE e.status = 'ACCEPTED' GROUP BY d.ancestor | |
| 61 | + ), | |
| 62 | + cohorts AS ( | |
| 63 | + SELECT d.ancestor AS cancer_id, count(DISTINCT c.id) AS cohort_count, | |
| 64 | + count(DISTINCT f.gene_symbol) FILTER (WHERE f.frequency >= 0.05 AND f.cases_affected >= 20) AS gdc_gene_count | |
| 65 | + FROM genomic_cohorts c JOIN _desc d ON d.descendant = c.cancer_id | |
| 66 | + LEFT JOIN cancer_gene_frequencies f ON f.cohort_id = c.id | |
| 67 | + GROUP BY d.ancestor | |
| 68 | + ), | |
| 69 | + appr AS ( | |
| 70 | + SELECT d.ancestor AS cancer_id, count(DISTINCT a.drug_id) AS approved_drug_count | |
| 71 | + FROM drug_approvals a JOIN _desc d ON d.descendant = a.cancer_id WHERE a.status IN ('approved','accelerated','conditional') GROUP BY d.ancestor | |
| 72 | + ), | |
| 73 | + epi AS ( | |
| 74 | + SELECT cancer_id, count(*) AS n FROM epidemiology_observations GROUP BY cancer_id | |
| 75 | + ), | |
| 76 | + surv AS ( | |
| 77 | + SELECT cancer_id, count(*) AS n FROM survival_observations GROUP BY cancer_id | |
| 78 | + ), | |
| 79 | + tree AS ( | |
| 80 | + SELECT ancestor AS cancer_id, count(*) - 1 AS descendant_count FROM _desc GROUP BY ancestor | |
| 81 | + ), | |
| 82 | + kids AS ( | |
| 83 | + SELECT parent_id AS cancer_id, count(DISTINCT child_id) AS subtype_count FROM cancer_hierarchy GROUP BY parent_id | |
| 84 | + ), | |
| 85 | + ins AS ( | |
| 86 | + INSERT INTO entity_counters (entity_type, entity_id, trial_count, active_trial_count, recruiting_trial_count, phase3_trial_count, | |
| 87 | + publication_count, publication_count_5y, publication_count_12m, gene_count, variant_count, drug_count, approved_drug_count, | |
| 88 | + evidence_count, cohort_count, subtype_count, descendant_count, epidemiology_obs_count, survival_obs_count, completeness, computed_at) | |
| 89 | + SELECT 'cancer', c.id, | |
| 90 | + COALESCE(t.trial_count,0), COALESCE(t.active_trial_count,0), COALESCE(t.recruiting_trial_count,0), COALESCE(t.phase3_trial_count,0), | |
| 91 | + COALESCE(p.publication_count,0), COALESCE(p.publication_count_5y,0), COALESCE(p.publication_count_12m,0), | |
| 92 | + GREATEST(COALESCE(e.civic_gene_count,0), COALESCE(g.gdc_gene_count,0)), COALESCE(e.variant_count,0), COALESCE(e.drug_count,0), COALESCE(a.approved_drug_count,0), | |
| 93 | + COALESCE(e.evidence_count,0), COALESCE(g.cohort_count,0), COALESCE(k.subtype_count,0), COALESCE(tr.descendant_count,0), COALESCE(ep.n,0), COALESCE(sv.n,0), | |
| 94 | + jsonb_build_object( | |
| 95 | + 'epidemiology', CASE WHEN COALESCE(ep.n,0) > 0 THEN 1 ELSE 0 END, | |
| 96 | + 'survival', CASE WHEN COALESCE(sv.n,0) > 0 THEN 1 ELSE 0 END, | |
| 97 | + 'trials', CASE WHEN COALESCE(t.trial_count,0) > 0 THEN 1 ELSE 0 END, | |
| 98 | + 'literature', CASE WHEN COALESCE(p.publication_count,0) > 0 THEN 1 ELSE 0 END, | |
| 99 | + 'genomics', CASE WHEN COALESCE(g.cohort_count,0) > 0 THEN 1 ELSE 0 END, | |
| 100 | + 'evidence', CASE WHEN COALESCE(e.evidence_count,0) > 0 THEN 1 ELSE 0 END, | |
| 101 | + 'therapies', CASE WHEN COALESCE(a.approved_drug_count,0) > 0 THEN 1 ELSE 0 END | |
| 102 | + ), now() | |
| 103 | + FROM cancers c | |
| 104 | + LEFT JOIN trials t ON t.cancer_id = c.id | |
| 105 | + LEFT JOIN pubs p ON p.cancer_id = c.id | |
| 106 | + LEFT JOIN evid e ON e.cancer_id = c.id | |
| 107 | + LEFT JOIN cohorts g ON g.cancer_id = c.id | |
| 108 | + LEFT JOIN appr a ON a.cancer_id = c.id | |
| 109 | + LEFT JOIN epi ep ON ep.cancer_id = c.id | |
| 110 | + LEFT JOIN surv sv ON sv.cancer_id = c.id | |
| 111 | + LEFT JOIN tree tr ON tr.cancer_id = c.id | |
| 112 | + LEFT JOIN kids k ON k.cancer_id = c.id | |
| 113 | + WHERE c.status = 'active' | |
| 114 | + ON CONFLICT (entity_type, entity_id) DO UPDATE SET | |
| 115 | + trial_count = EXCLUDED.trial_count, active_trial_count = EXCLUDED.active_trial_count, recruiting_trial_count = EXCLUDED.recruiting_trial_count, | |
| 116 | + phase3_trial_count = EXCLUDED.phase3_trial_count, publication_count = EXCLUDED.publication_count, publication_count_5y = EXCLUDED.publication_count_5y, | |
| 117 | + publication_count_12m = EXCLUDED.publication_count_12m, gene_count = EXCLUDED.gene_count, variant_count = EXCLUDED.variant_count, drug_count = EXCLUDED.drug_count, | |
| 118 | + approved_drug_count = EXCLUDED.approved_drug_count, evidence_count = EXCLUDED.evidence_count, cohort_count = EXCLUDED.cohort_count, subtype_count = EXCLUDED.subtype_count, | |
| 119 | + descendant_count = EXCLUDED.descendant_count, epidemiology_obs_count = EXCLUDED.epidemiology_obs_count, survival_obs_count = EXCLUDED.survival_obs_count, | |
| 120 | + completeness = EXCLUDED.completeness, computed_at = now() | |
| 121 | + RETURNING 1 | |
| 122 | + ) | |
| 123 | + SELECT count(*)::text AS n FROM ins | |
| 124 | + `); | |
| 125 | + // Gene-level counters (cancers linked, evidence items) for gene pages. | |
| 126 | + await tx.execute(sql` | |
| 127 | + INSERT INTO entity_counters (entity_type, entity_id, evidence_count, variant_count, drug_count, trial_count, computed_at) | |
| 128 | + SELECT 'gene', g.id, | |
| 129 | + (SELECT count(*) FROM civic_evidence_items e WHERE e.status = 'ACCEPTED' AND g.id = ANY(e.gene_ids)), | |
| 130 | + (SELECT count(*) FROM variants v WHERE v.gene_id = g.id), | |
| 131 | + (SELECT count(DISTINCT t) FROM civic_evidence_items e, unnest(e.therapy_ids) t WHERE e.status = 'ACCEPTED' AND g.id = ANY(e.gene_ids)), | |
| 132 | + 0, now() | |
| 133 | + FROM genes g | |
| 134 | + WHERE EXISTS (SELECT 1 FROM civic_evidence_items e WHERE g.id = ANY(e.gene_ids)) OR EXISTS (SELECT 1 FROM variants v WHERE v.gene_id = g.id) OR EXISTS (SELECT 1 FROM cancer_gene_frequencies f WHERE f.gene_id = g.id) | |
| 135 | + ON CONFLICT (entity_type, entity_id) DO UPDATE SET evidence_count = EXCLUDED.evidence_count, variant_count = EXCLUDED.variant_count, drug_count = EXCLUDED.drug_count, computed_at = now() | |
| 136 | + `); | |
| 137 | + await tx.execute(sql`UPDATE genes g SET is_cancer_gene = EXISTS (SELECT 1 FROM civic_evidence_items e WHERE e.status = 'ACCEPTED' AND g.id = ANY(e.gene_ids)) OR EXISTS (SELECT 1 FROM cancer_gene_frequencies f WHERE f.gene_id = g.id AND f.frequency >= 0.05 AND f.cases_affected >= 20)`); | |
| 138 | + return Number(rows[0]?.n ?? 0); | |
| 139 | + }); | |
| 140 | +} | |
added
packages/ranking/src/engine.ts
+173 −0
@@ -0,0 +1,173 @@ | ||
| 1 | +import { and, eq, sql } from 'drizzle-orm'; | |
| 2 | +import { type Database, metricDefinitions, rankingSnapshots, rankings, entityCounters, cancers } from '@cancerindex/database'; | |
| 3 | +import { rankEntities, inputsHash, percentiles, type RankInput } from './rank.js'; | |
| 4 | + | |
| 5 | +export interface Scope { | |
| 6 | + geography: string; // WORLD | ISO3 | slug | |
| 7 | + sex: 'all' | 'male' | 'female'; | |
| 8 | + ageGroup: string; | |
| 9 | + year: number | null; | |
| 10 | + entityLevel: 'top' | 'all'; | |
| 11 | +} | |
| 12 | + | |
| 13 | +export function scopeKey(s: Scope): string { | |
| 14 | + return `geo=${s.geography}|sex=${s.sex}|age=${s.ageGroup}|year=${s.year ?? 'latest'}|level=${s.entityLevel}`; | |
| 15 | +} | |
| 16 | + | |
| 17 | +export interface RankingResult { | |
| 18 | + metricSlug: string; | |
| 19 | + scopeKey: string; | |
| 20 | + eligible: number; | |
| 21 | + snapshotId: number; | |
| 22 | +} | |
| 23 | + | |
| 24 | +/** | |
| 25 | + * Ranking engine (CLAUDE.md §30-35, §245-247, §288). Every snapshot stores scope, formula version, | |
| 26 | + * an inputs hash and per-row lineage (`inputs`) so "Why #4?" and TRACE can be answered. | |
| 27 | + * | |
| 28 | + * Phase 1 ships count-based metrics (trials, literature, molecular) for both entity levels and the | |
| 29 | + * burden/lethality/gap metrics only where epidemiology observations exist for the scope. | |
| 30 | + */ | |
| 31 | +export async function computeAllRankings(db: Database): Promise<RankingResult[]> { | |
| 32 | + const results: RankingResult[] = []; | |
| 33 | + const defs = await db.select().from(metricDefinitions); | |
| 34 | + const byslug = new Map(defs.map((d) => [d.slug, d])); | |
| 35 | + | |
| 36 | + const countMetrics: Array<[string, keyof typeof entityCounters.$inferSelect]> = [ | |
| 37 | + ['active_trials', 'activeTrialCount'], | |
| 38 | + ['recruiting_trials', 'recruitingTrialCount'], | |
| 39 | + ['phase3_trials', 'phase3TrialCount'], | |
| 40 | + ['publications_5y', 'publicationCount5y'], | |
| 41 | + ['publications_12m', 'publicationCount12m'], | |
| 42 | + ['curated_evidence_items', 'evidenceCount'], | |
| 43 | + ['associated_genes', 'geneCount'], | |
| 44 | + ['genomic_cohorts', 'cohortCount'], | |
| 45 | + ]; | |
| 46 | + | |
| 47 | + for (const level of ['top', 'all'] as const) { | |
| 48 | + const scope: Scope = { geography: 'WORLD', sex: 'all', ageGroup: 'all', year: null, entityLevel: level }; | |
| 49 | + const counters = await db | |
| 50 | + .select({ id: cancers.id, c: entityCounters }) | |
| 51 | + .from(cancers) | |
| 52 | + .innerJoin(entityCounters, and(eq(entityCounters.entityType, 'cancer'), eq(entityCounters.entityId, cancers.id))) | |
| 53 | + .where(level === 'top' ? and(eq(cancers.status, 'active'), eq(cancers.topLevel, true)) : and(eq(cancers.status, 'active'), eq(cancers.malignant, true))); | |
| 54 | + for (const [slug, field] of countMetrics) { | |
| 55 | + const def = byslug.get(slug); | |
| 56 | + if (!def) continue; | |
| 57 | + const items: RankInput[] = counters | |
| 58 | + .map((r) => ({ id: r.id, value: Number(r.c[field] ?? 0), inputs: { counter: field, computedAt: r.c.computedAt } })) | |
| 59 | + .filter((i) => (slug.startsWith('publications') ? i.value > 0 : true)); // publications: only cancers with a stored PubMed query | |
| 60 | + if (items.length === 0) continue; | |
| 61 | + const snap = await persistSnapshot(db, def, scope, items, { descending: true, sourceIds: def.sourceSlugs }); | |
| 62 | + results.push(snap); | |
| 63 | + } | |
| 64 | + // Publication growth (derived from two windows). | |
| 65 | + const growthDef = byslug.get('publication_growth'); | |
| 66 | + if (growthDef) { | |
| 67 | + const rows = await db.execute<{ cancer_id: string; m12: number; y5: number }>(sql` | |
| 68 | + SELECT cancer_id, max(count) FILTER (WHERE window_key = '12m') AS m12, max(count) FILTER (WHERE window_key = '5y_prior') AS y5 | |
| 69 | + FROM literature_counts GROUP BY cancer_id`); | |
| 70 | + const ids = new Set(counters.map((c) => c.id)); | |
| 71 | + const items: RankInput[] = rows | |
| 72 | + .filter((r) => ids.has(r.cancer_id) && r.y5 != null && r.m12 != null && Number(r.y5) >= 50) | |
| 73 | + .map((r) => ({ id: r.cancer_id, value: Number(r.m12) / (Number(r.y5) / 5), inputs: { publications_12m: Number(r.m12), publications_5y_prior: Number(r.y5), formula: growthDef.formula } })); | |
| 74 | + if (items.length) results.push(await persistSnapshot(db, growthDef, scope, items, { descending: true, sourceIds: ['pubmed'] })); | |
| 75 | + } | |
| 76 | + } | |
| 77 | + | |
| 78 | + // Burden / lethality / gaps per geography-year where observations exist (top level only, §246-247). | |
| 79 | + const scopes = await db.execute<{ geography_id: string; slug: string; iso3: string | null; year: number; sex: string; source_id: string; n: number }>(sql` | |
| 80 | + SELECT o.geography_id, g.slug, g.iso3, o.year, o.sex, o.source_id, count(*) AS n | |
| 81 | + FROM epidemiology_observations o JOIN geographies g ON g.id = o.geography_id JOIN cancers c ON c.id = o.cancer_id | |
| 82 | + WHERE c.top_level AND o.age_group = 'all' AND o.metric IN ('incidence_count','mortality_count','as_incidence_rate','as_mortality_rate') | |
| 83 | + GROUP BY o.geography_id, g.slug, g.iso3, o.year, o.sex, o.source_id HAVING count(*) >= 10`); | |
| 84 | + for (const s of scopes) { | |
| 85 | + const scope: Scope = { geography: s.iso3 ?? s.slug.toUpperCase(), sex: s.sex as Scope['sex'], ageGroup: 'all', year: Number(s.year), entityLevel: 'top' }; | |
| 86 | + const obs = await db.execute<{ cancer_id: string; metric: string; value: number; id: number; estimate_type: string; site_definition: string | null }>(sql` | |
| 87 | + SELECT o.cancer_id, o.metric, o.value, o.id, o.estimate_type, o.site_definition FROM epidemiology_observations o JOIN cancers c ON c.id = o.cancer_id | |
| 88 | + WHERE c.top_level AND o.geography_id = ${s.geography_id} AND o.year = ${s.year} AND o.sex = ${s.sex} AND o.age_group = 'all' AND o.source_id = ${s.source_id}`); | |
| 89 | + const byMetric = new Map<string, Map<string, { value: number; id: number; estimate_type: string; site: string | null }>>(); | |
| 90 | + for (const o of obs) { | |
| 91 | + if (!byMetric.has(o.metric)) byMetric.set(o.metric, new Map()); | |
| 92 | + byMetric.get(o.metric)!.set(o.cancer_id, { value: Number(o.value), id: o.id, estimate_type: o.estimate_type, site: o.site_definition }); | |
| 93 | + } | |
| 94 | + for (const metric of ['incidence_count', 'mortality_count', 'as_incidence_rate', 'as_mortality_rate']) { | |
| 95 | + const def = byslug.get(metric); | |
| 96 | + const m = byMetric.get(metric); | |
| 97 | + if (!def || !m) continue; | |
| 98 | + const items: RankInput[] = [...m.entries()].map(([id, v]) => ({ id, value: v.value, confidence: v.estimate_type === 'observed' ? 'HIGH' : 'MEDIUM', inputs: { observationId: v.id, estimateType: v.estimate_type, siteDefinition: v.site, sourceId: s.source_id } })); | |
| 99 | + results.push(await persistSnapshot(db, def, scope, items, { descending: true, sourceIds: [s.source_id] })); | |
| 100 | + } | |
| 101 | + // Mortality-to-incidence ratio | |
| 102 | + const mirDef = byslug.get('mortality_incidence_ratio'); | |
| 103 | + const inc = byMetric.get('incidence_count'); | |
| 104 | + const mort = byMetric.get('mortality_count'); | |
| 105 | + if (mirDef && inc && mort) { | |
| 106 | + const items: RankInput[] = []; | |
| 107 | + for (const [id, i] of inc) { | |
| 108 | + const d = mort.get(id); | |
| 109 | + if (!d || i.value < 100) continue; | |
| 110 | + items.push({ id, value: d.value / i.value, confidence: i.value >= 1000 ? 'HIGH' : 'MEDIUM', inputs: { incidenceObservationId: i.id, mortalityObservationId: d.id, incidence: i.value, deaths: d.value, formula: mirDef.formula } }); | |
| 111 | + } | |
| 112 | + if (items.length) results.push(await persistSnapshot(db, mirDef, scope, items, { descending: true, sourceIds: [s.source_id] })); | |
| 113 | + } | |
| 114 | + // Gap indexes: burden percentile − activity percentile | |
| 115 | + if (mort) { | |
| 116 | + const burdenPct = percentiles([...mort.entries()].map(([id, v]) => ({ id, value: v.value }))); | |
| 117 | + const counters = await db.select({ id: entityCounters.entityId, trials: entityCounters.activeTrialCount, pubs: entityCounters.publicationCount5y }).from(entityCounters).where(eq(entityCounters.entityType, 'cancer')); | |
| 118 | + const cmap = new Map(counters.map((c) => [c.id, c])); | |
| 119 | + for (const [slug, field] of [ | |
| 120 | + ['trial_gap', 'trials'], | |
| 121 | + ['research_gap', 'pubs'], | |
| 122 | + ] as const) { | |
| 123 | + const def = byslug.get(slug); | |
| 124 | + if (!def) continue; | |
| 125 | + const ids = [...mort.keys()].filter((id) => cmap.has(id) && (field === 'trials' || (cmap.get(id)!.pubs ?? 0) > 0)); | |
| 126 | + if (ids.length < 5) continue; | |
| 127 | + const actPct = percentiles(ids.map((id) => ({ id, value: Number(cmap.get(id)![field] ?? 0) }))); | |
| 128 | + const items: RankInput[] = ids.map((id) => ({ id, value: (burdenPct.get(id) ?? 0) - (actPct.get(id) ?? 0), inputs: { burdenPercentile: burdenPct.get(id), activityPercentile: actPct.get(id), deaths: mort.get(id)!.value, activity: Number(cmap.get(id)![field] ?? 0), formula: def.formula } })); | |
| 129 | + results.push(await persistSnapshot(db, def, scope, items, { descending: true, sourceIds: [s.source_id, ...def.sourceSlugs] })); | |
| 130 | + } | |
| 131 | + } | |
| 132 | + } | |
| 133 | + return results; | |
| 134 | +} | |
| 135 | + | |
| 136 | +async function persistSnapshot(db: Database, def: typeof metricDefinitions.$inferSelect, scope: Scope, items: RankInput[], opts: { descending: boolean; sourceIds: string[] }): Promise<RankingResult> { | |
| 137 | + const key = scopeKey(scope); | |
| 138 | + const ranked = rankEntities(items, { descending: def.higherIsWorse === false ? opts.descending : opts.descending }); | |
| 139 | + const hash = await inputsHash(items); | |
| 140 | + return db.transaction(async (tx) => { | |
| 141 | + // Previous rank for change explanation (§311) | |
| 142 | + const prev = await tx.execute<{ cancer_id: string; rank: number }>(sql` | |
| 143 | + SELECT r.cancer_id, r.rank FROM rankings r JOIN ranking_snapshots s ON s.id = r.snapshot_id | |
| 144 | + WHERE s.metric_slug = ${def.slug} AND s.scope_key = ${key} AND s.is_current`); | |
| 145 | + const prevMap = new Map(prev.map((p) => [p.cancer_id, Number(p.rank)])); | |
| 146 | + await tx.update(rankingSnapshots).set({ isCurrent: false }).where(and(eq(rankingSnapshots.metricSlug, def.slug), eq(rankingSnapshots.scopeKey, key), eq(rankingSnapshots.isCurrent, true))); | |
| 147 | + const [snap] = await tx | |
| 148 | + .insert(rankingSnapshots) | |
| 149 | + .values({ metricId: def.id, metricSlug: def.slug, scopeKey: key, geography: scope.geography, sex: scope.sex, ageGroup: scope.ageGroup, year: scope.year, entityLevel: scope.entityLevel, formulaVersion: def.formulaVersion, eligibleEntities: ranked.length, inputsHash: hash, sourceIds: opts.sourceIds, isCurrent: true }) | |
| 150 | + .returning({ id: rankingSnapshots.id }); | |
| 151 | + const snapshotId = snap!.id; | |
| 152 | + const chunk = 500; | |
| 153 | + for (let i = 0; i < ranked.length; i += chunk) { | |
| 154 | + await tx.insert(rankings).values( | |
| 155 | + ranked.slice(i, i + chunk).map((r) => ({ | |
| 156 | + snapshotId, | |
| 157 | + metricSlug: def.slug, | |
| 158 | + scopeKey: key, | |
| 159 | + cancerId: r.id, | |
| 160 | + rank: r.rank, | |
| 161 | + eligibleEntities: r.eligible, | |
| 162 | + percentile: r.percentile, | |
| 163 | + value: r.value, | |
| 164 | + unit: def.unit, | |
| 165 | + confidence: r.confidence ?? 'MEDIUM', | |
| 166 | + inputs: r.inputs ?? {}, | |
| 167 | + previousRank: prevMap.get(r.id) ?? null, | |
| 168 | + })), | |
| 169 | + ); | |
| 170 | + } | |
| 171 | + return { metricSlug: def.slug, scopeKey: key, eligible: ranked.length, snapshotId }; | |
| 172 | + }); | |
| 173 | +} | |
added
packages/ranking/src/index.ts
+4 −0
@@ -0,0 +1,4 @@ | ||
| 1 | +export * from './rank.js'; | |
| 2 | +export * from './counters.js'; | |
| 3 | +export * from './engine.js'; | |
| 4 | +export * from './trace.js'; | |
added
packages/ranking/src/rank.ts
+50 −0
@@ -0,0 +1,50 @@ | ||
| 1 | +/** | |
| 2 | + * Pure ranking primitives (CLAUDE.md §155: deterministic given fixed inputs). | |
| 3 | + */ | |
| 4 | +export interface RankInput { | |
| 5 | + id: string; | |
| 6 | + value: number; | |
| 7 | + /** Optional confidence label carried through to the output. */ | |
| 8 | + confidence?: 'HIGH' | 'MEDIUM' | 'LOW' | 'INSUFFICIENT_DATA'; | |
| 9 | + inputs?: Record<string, unknown>; | |
| 10 | +} | |
| 11 | + | |
| 12 | +export interface RankOutput extends RankInput { | |
| 13 | + rank: number; | |
| 14 | + percentile: number; // 0..100, 100 = most extreme in the ranking direction | |
| 15 | + eligible: number; | |
| 16 | +} | |
| 17 | + | |
| 18 | +/** | |
| 19 | + * Rank entities. `higherIsWorse === null` means "higher is first" (descending) by convention. | |
| 20 | + * Ties share the same rank (competition ranking: 1,2,2,4). Percentile = share of eligible entities | |
| 21 | + * ranked at or below this entity in the ranking direction. | |
| 22 | + */ | |
| 23 | +export function rankEntities(items: RankInput[], opts: { descending?: boolean } = {}): RankOutput[] { | |
| 24 | + const desc = opts.descending ?? true; | |
| 25 | + const valid = items.filter((i) => Number.isFinite(i.value)); | |
| 26 | + const sorted = [...valid].sort((a, b) => (desc ? b.value - a.value : a.value - b.value) || a.id.localeCompare(b.id)); | |
| 27 | + const n = sorted.length; | |
| 28 | + const out: RankOutput[] = []; | |
| 29 | + let rank = 0; | |
| 30 | + for (let i = 0; i < n; i++) { | |
| 31 | + const cur = sorted[i]!; | |
| 32 | + if (i === 0 || cur.value !== sorted[i - 1]!.value) rank = i + 1; | |
| 33 | + out.push({ ...cur, rank, eligible: n, percentile: n === 1 ? 100 : Math.round(((n - rank) / (n - 1)) * 1000) / 10 }); | |
| 34 | + } | |
| 35 | + return out; | |
| 36 | +} | |
| 37 | + | |
| 38 | +/** Percentile of each entity for a metric (0..100, higher value → higher percentile). */ | |
| 39 | +export function percentiles(items: RankInput[]): Map<string, number> { | |
| 40 | + const ranked = rankEntities(items, { descending: true }); | |
| 41 | + return new Map(ranked.map((r) => [r.id, r.percentile])); | |
| 42 | +} | |
| 43 | + | |
| 44 | +/** Stable hash of ranking inputs so a snapshot is reproducible/auditable. */ | |
| 45 | +export async function inputsHash(items: RankInput[]): Promise<string> { | |
| 46 | + const { createHash } = await import('node:crypto'); | |
| 47 | + const h = createHash('sha256'); | |
| 48 | + for (const i of [...items].sort((a, b) => a.id.localeCompare(b.id))) h.update(`${i.id}=${i.value};`); | |
| 49 | + return h.digest('hex').slice(0, 24); | |
| 50 | +} | |
added
packages/ranking/src/trace.ts
+51 −0
@@ -0,0 +1,51 @@ | ||
| 1 | +import { sql } from 'drizzle-orm'; | |
| 2 | +import type { Database } from '@cancerindex/database'; | |
| 3 | + | |
| 4 | +/** | |
| 5 | + * Lineage trace (CLAUDE.md §252-253): ranked value → inputs → observation → provenance → raw record. | |
| 6 | + * Supported: rankings/<id>, epidemiology_observations/<id>, cancer_gene_frequencies/<id>, literature_counts/<id>. | |
| 7 | + */ | |
| 8 | +export async function traceValue(db: Database, table: string, id: string): Promise<Record<string, unknown>> { | |
| 9 | + const out: Record<string, unknown> = { table, id }; | |
| 10 | + const prov = async (pid: number | null | undefined) => { | |
| 11 | + if (!pid) return null; | |
| 12 | + const [p] = await db.execute<Record<string, unknown>>(sql`SELECT p.*, s.name AS source_name, s.slug AS source_slug, s.license FROM provenance p JOIN sources s ON s.id = p.source_id WHERE p.id = ${pid}`); | |
| 13 | + if (!p) return null; | |
| 14 | + const [rec] = await db.execute<Record<string, unknown>>(sql`SELECT id, entity_kind, source_record_id, raw_path, payload_hash, retrieved_at, status FROM source_records WHERE source_id = ${p.source_id as string} AND source_record_id = ${(p.source_record_id as string) ?? ''} LIMIT 1`); | |
| 15 | + return { provenance: p, sourceRecord: rec ?? null }; | |
| 16 | + }; | |
| 17 | + switch (table) { | |
| 18 | + case 'rankings': { | |
| 19 | + const [r] = await db.execute<Record<string, unknown>>(sql`SELECT r.*, s.formula_version, s.inputs_hash, s.generated_at AS snapshot_generated_at, m.formula, m.name AS metric_name FROM rankings r JOIN ranking_snapshots s ON s.id = r.snapshot_id JOIN metric_definitions m ON m.slug = r.metric_slug WHERE r.id = ${Number(id)}`); | |
| 20 | + if (!r) return { ...out, error: 'not found' }; | |
| 21 | + out.ranking = r; | |
| 22 | + const inputs = (r.inputs ?? {}) as Record<string, unknown>; | |
| 23 | + for (const k of ['observationId', 'incidenceObservationId', 'mortalityObservationId']) { | |
| 24 | + if (inputs[k]) out[k] = await traceValue(db, 'epidemiology_observations', String(inputs[k])); | |
| 25 | + } | |
| 26 | + return out; | |
| 27 | + } | |
| 28 | + case 'epidemiology_observations': { | |
| 29 | + const [o] = await db.execute<Record<string, unknown>>(sql`SELECT * FROM epidemiology_observations WHERE id = ${Number(id)}`); | |
| 30 | + if (!o) return { ...out, error: 'not found' }; | |
| 31 | + return { ...out, observation: o, lineage: await prov(o.provenance_id as number) }; | |
| 32 | + } | |
| 33 | + case 'cancer_gene_frequencies': { | |
| 34 | + const [o] = await db.execute<Record<string, unknown>>(sql`SELECT * FROM cancer_gene_frequencies WHERE id = ${Number(id)}`); | |
| 35 | + if (!o) return { ...out, error: 'not found' }; | |
| 36 | + return { ...out, frequency: o, lineage: await prov(o.provenance_id as number) }; | |
| 37 | + } | |
| 38 | + case 'literature_counts': { | |
| 39 | + const [o] = await db.execute<Record<string, unknown>>(sql`SELECT * FROM literature_counts WHERE id = ${Number(id)}`); | |
| 40 | + if (!o) return { ...out, error: 'not found' }; | |
| 41 | + return { ...out, literatureCount: o, lineage: await prov(o.provenance_id as number) }; | |
| 42 | + } | |
| 43 | + case 'survival_observations': { | |
| 44 | + const [o] = await db.execute<Record<string, unknown>>(sql`SELECT * FROM survival_observations WHERE id = ${Number(id)}`); | |
| 45 | + if (!o) return { ...out, error: 'not found' }; | |
| 46 | + return { ...out, observation: o, lineage: await prov(o.provenance_id as number) }; | |
| 47 | + } | |
| 48 | + default: | |
| 49 | + return { ...out, error: 'unsupported table' }; | |
| 50 | + } | |
| 51 | +} | |
added
packages/ranking/test/rank.test.ts
+33 −0
@@ -0,0 +1,33 @@ | ||
| 1 | +import { describe, expect, it } from 'vitest'; | |
| 2 | +import { rankEntities, percentiles, inputsHash } from '../src/rank.js'; | |
| 3 | + | |
| 4 | +describe('rankEntities', () => { | |
| 5 | + it('is deterministic and handles ties with competition ranking', () => { | |
| 6 | + const items = [ | |
| 7 | + { id: 'b', value: 10 }, | |
| 8 | + { id: 'a', value: 10 }, | |
| 9 | + { id: 'c', value: 5 }, | |
| 10 | + { id: 'd', value: 1 }, | |
| 11 | + ]; | |
| 12 | + const r = rankEntities(items); | |
| 13 | + expect(r.map((x) => [x.id, x.rank])).toEqual([ | |
| 14 | + ['a', 1], | |
| 15 | + ['b', 1], | |
| 16 | + ['c', 3], | |
| 17 | + ['d', 4], | |
| 18 | + ]); | |
| 19 | + expect(r[0]!.percentile).toBe(100); | |
| 20 | + expect(r[3]!.percentile).toBe(0); | |
| 21 | + expect(rankEntities([...items].reverse())).toEqual(r); | |
| 22 | + }); | |
| 23 | + it('drops non-finite values and ranks ascending when requested', () => { | |
| 24 | + const r = rankEntities([{ id: 'x', value: NaN }, { id: 'y', value: 2 }, { id: 'z', value: 1 }], { descending: false }); | |
| 25 | + expect(r.map((x) => x.id)).toEqual(['z', 'y']); | |
| 26 | + expect(r[0]!.eligible).toBe(2); | |
| 27 | + }); | |
| 28 | + it('percentiles and hash are stable', async () => { | |
| 29 | + const items = [{ id: 'a', value: 3 }, { id: 'b', value: 1 }]; | |
| 30 | + expect(percentiles(items).get('a')).toBe(100); | |
| 31 | + expect(await inputsHash(items)).toBe(await inputsHash([...items].reverse())); | |
| 32 | + }); | |
| 33 | +}); | |
added
packages/ranking/tsconfig.json
+8 −0
@@ -0,0 +1,8 @@ | ||
| 1 | +{ | |
| 2 | + "extends": "../../tsconfig.base.json", | |
| 3 | + "compilerOptions": { | |
| 4 | + "rootDir": ".", | |
| 5 | + "noEmit": true | |
| 6 | + }, | |
| 7 | + "include": ["src", "test"] | |
| 8 | +} | |
added
packages/ranking/vitest.config.ts
+3 −0
@@ -0,0 +1,3 @@ | ||
| 1 | +import { defineConfig } from 'vitest/config'; | |
| 2 | + | |
| 3 | +export default defineConfig({ test: { include: ['src/**/*.test.ts', 'test/**/*.test.ts'] } }); | |
added
packages/shared/package.json
+26 −0
@@ -0,0 +1,26 @@ | ||
| 1 | +{ | |
| 2 | + "name": "@cancerindex/shared", | |
| 3 | + "version": "0.1.0", | |
| 4 | + "private": true, | |
| 5 | + "type": "module", | |
| 6 | + "exports": { | |
| 7 | + ".": { | |
| 8 | + "types": "./src/index.ts", | |
| 9 | + "default": "./src/index.ts" | |
| 10 | + } | |
| 11 | + }, | |
| 12 | + "scripts": { | |
| 13 | + "build": "tsc -p tsconfig.json --noEmit", | |
| 14 | + "typecheck": "tsc -p tsconfig.json --noEmit", | |
| 15 | + "test": "vitest run --passWithNoTests" | |
| 16 | + }, | |
| 17 | + "dependencies": { | |
| 18 | + "pino": "^9.7.0", | |
| 19 | + "zod": "^4.0.0" | |
| 20 | + }, | |
| 21 | + "devDependencies": { | |
| 22 | + "@types/node": "^24.0.0", | |
| 23 | + "typescript": "^5.9.3", | |
| 24 | + "vitest": "^3.2.0" | |
| 25 | + } | |
| 26 | +} | |
added
packages/shared/src/env.ts
+16 −0
@@ -0,0 +1,16 @@ | ||
| 1 | +import path from 'node:path'; | |
| 2 | + | |
| 3 | +export function dataDir(): string { | |
| 4 | + return path.resolve(process.env.CI_DATA_DIR ?? path.join(process.cwd(), 'data')); | |
| 5 | +} | |
| 6 | + | |
| 7 | +export function isProduction(): boolean { | |
| 8 | + return process.env.NODE_ENV === 'production'; | |
| 9 | +} | |
| 10 | + | |
| 11 | +export function envInt(name: string, fallback: number): number { | |
| 12 | + const v = process.env[name]; | |
| 13 | + if (!v) return fallback; | |
| 14 | + const n = Number(v); | |
| 15 | + return Number.isFinite(n) ? n : fallback; | |
| 16 | +} | |
added
packages/shared/src/ids.ts
+70 −0
@@ -0,0 +1,70 @@ | ||
| 1 | +/** | |
| 2 | + * CancerIndex stable public identifiers (CLAUDE.md §6). | |
| 3 | + * | |
| 4 | + * CI-CAN-00000001 cancer entity | |
| 5 | + * CI-GENE-00000001 gene | |
| 6 | + * CI-VAR-00000001 variant | |
| 7 | + * CI-DRUG-00000001 drug | |
| 8 | + * CI-TRIAL-00000001 clinical trial | |
| 9 | + * CI-PUB-00000001 publication | |
| 10 | + * CI-BIO-00000001 biomarker | |
| 11 | + * CI-STUDY-00000001 genomic study / cohort | |
| 12 | + * CI-METRIC-00000001 metric definition | |
| 13 | + * CI-SOURCE-00000001 source | |
| 14 | + * CI-ORG-00000001 organization | |
| 15 | + * CI-TRT-00000001 treatment / regimen | |
| 16 | + * CI-ANAT-00000001 anatomical site | |
| 17 | + * CI-GEO-00000001 geography | |
| 18 | + * CI-PROV-00000001 provenance record | |
| 19 | + * | |
| 20 | + * IDs are minted from a database sequence per namespace (see @cancerindex/database `mintId`) and | |
| 21 | + * are never reused. Database auto-increment integers are never exposed publicly. | |
| 22 | + */ | |
| 23 | +export const ID_NAMESPACES = [ | |
| 24 | + 'CAN', | |
| 25 | + 'GENE', | |
| 26 | + 'VAR', | |
| 27 | + 'DRUG', | |
| 28 | + 'TRIAL', | |
| 29 | + 'PUB', | |
| 30 | + 'BIO', | |
| 31 | + 'STUDY', | |
| 32 | + 'METRIC', | |
| 33 | + 'SOURCE', | |
| 34 | + 'ORG', | |
| 35 | + 'TRT', | |
| 36 | + 'ANAT', | |
| 37 | + 'GEO', | |
| 38 | + 'PROV', | |
| 39 | + 'EDGE', | |
| 40 | + 'RANK', | |
| 41 | +] as const; | |
| 42 | +export type IdNamespace = (typeof ID_NAMESPACES)[number]; | |
| 43 | + | |
| 44 | +export const ID_WIDTH = 8; | |
| 45 | + | |
| 46 | +export function formatId(ns: IdNamespace, n: number): string { | |
| 47 | + if (!Number.isInteger(n) || n < 1) throw new Error(`invalid id number ${n}`); | |
| 48 | + return `CI-${ns}-${String(n).padStart(ID_WIDTH, '0')}`; | |
| 49 | +} | |
| 50 | + | |
| 51 | +const ID_RE = /^CI-([A-Z]+)-(\d{8,})$/; | |
| 52 | + | |
| 53 | +export function parseId(id: string): { ns: IdNamespace; n: number } | null { | |
| 54 | + const m = ID_RE.exec(id); | |
| 55 | + if (!m) return null; | |
| 56 | + const ns = m[1] as IdNamespace; | |
| 57 | + if (!ID_NAMESPACES.includes(ns)) return null; | |
| 58 | + return { ns, n: Number(m[2]) }; | |
| 59 | +} | |
| 60 | + | |
| 61 | +export function isCiId(id: string, ns?: IdNamespace): boolean { | |
| 62 | + const p = parseId(id); | |
| 63 | + return !!p && (ns ? p.ns === ns : true); | |
| 64 | +} | |
| 65 | + | |
| 66 | +/** Ingest run identifiers: ING-CLINICALTRIALS-20260908-000019 (CLAUDE.md §176). */ | |
| 67 | +export function formatRunId(connectorId: string, date: Date, seq: number): string { | |
| 68 | + const d = date.toISOString().slice(0, 10).replace(/-/g, ''); | |
| 69 | + return `ING-${connectorId.toUpperCase().replace(/[^A-Z0-9]/g, '')}-${d}-${String(seq).padStart(6, '0')}`; | |
| 70 | +} | |
added
packages/shared/src/index.ts
+5 −0
@@ -0,0 +1,5 @@ | ||
| 1 | +export * from './ids.js'; | |
| 2 | +export * from './provenance.js'; | |
| 3 | +export * from './normalize.js'; | |
| 4 | +export * from './logger.js'; | |
| 5 | +export * from './env.js'; | |
added
packages/shared/src/logger.ts
+9 −0
@@ -0,0 +1,9 @@ | ||
| 1 | +import pino from 'pino'; | |
| 2 | + | |
| 3 | +export const logger = pino({ | |
| 4 | + level: process.env.LOG_LEVEL ?? 'info', | |
| 5 | + base: { service: process.env.CI_SERVICE ?? 'cancerindex' }, | |
| 6 | + timestamp: pino.stdTimeFunctions.isoTime, | |
| 7 | +}); | |
| 8 | + | |
| 9 | +export type Logger = typeof logger; | |
added
packages/shared/src/normalize.ts
+40 −0
@@ -0,0 +1,40 @@ | ||
| 1 | +/** | |
| 2 | + * String normalization used by reconciliation (CLAUDE.md §69). Deterministic, no LLM. | |
| 3 | + */ | |
| 4 | + | |
| 5 | +const GREEK: Record<string, string> = { α: 'alpha', β: 'beta', γ: 'gamma', δ: 'delta', κ: 'kappa', λ: 'lambda', μ: 'mu' }; | |
| 6 | + | |
| 7 | +export function stripDiacritics(s: string): string { | |
| 8 | + return s.normalize('NFKD').replace(/[̀-ͯ]/g, ''); | |
| 9 | +} | |
| 10 | + | |
| 11 | +/** Lowercase, ASCII, punctuation → space, collapse whitespace. Keeps digits (G12C, HER2). */ | |
| 12 | +export function normalizeLabel(input: string): string { | |
| 13 | + let s = stripDiacritics(input).toLowerCase(); | |
| 14 | + s = s.replace(/[αβγδκλμ]/g, (c) => GREEK[c] ?? c); | |
| 15 | + s = s.replace(/['’`"]/g, ''); | |
| 16 | + s = s.replace(/[^a-z0-9]+/g, ' '); | |
| 17 | + s = s.replace(/\b(nos|unspecified|not otherwise specified)\b/g, ' '); | |
| 18 | + s = s.replace(/\bcarcinomas\b/g, 'carcinoma').replace(/\bcancers\b/g, 'cancer').replace(/\btumou?rs\b/g, 'tumor').replace(/\btumour\b/g, 'tumor').replace(/\bneoplasms\b/g, 'neoplasm').replace(/\bsarcomas\b/g, 'sarcoma').replace(/\blymphomas\b/g, 'lymphoma').replace(/\bleukaemia\b/g, 'leukemia').replace(/\bleukemias\b/g, 'leukemia'); | |
| 19 | + return s.replace(/\s+/g, ' ').trim(); | |
| 20 | +} | |
| 21 | + | |
| 22 | +/** "Carcinoma, Non-Small-Cell Lung" (MeSH inverted form) → "Non-Small-Cell Lung Carcinoma". */ | |
| 23 | +export function uninvertMeshTerm(term: string): string { | |
| 24 | + const parts = term.split(',').map((p) => p.trim()); | |
| 25 | + if (parts.length < 2) return term; | |
| 26 | + return [...parts.slice(1), parts[0]].join(' '); | |
| 27 | +} | |
| 28 | + | |
| 29 | +/** URL slug for entity routes. */ | |
| 30 | +export function slugify(input: string): string { | |
| 31 | + return stripDiacritics(input) | |
| 32 | + .toLowerCase() | |
| 33 | + .replace(/[^a-z0-9]+/g, '-') | |
| 34 | + .replace(/^-+|-+$/g, '') | |
| 35 | + .slice(0, 120); | |
| 36 | +} | |
| 37 | + | |
| 38 | +export function sha256Hex(data: string | Uint8Array): Promise<string> { | |
| 39 | + return import('node:crypto').then(({ createHash }) => createHash('sha256').update(data).digest('hex')); | |
| 40 | +} | |
added
packages/shared/src/provenance.ts
+85 −0
@@ -0,0 +1,85 @@ | ||
| 1 | +import { z } from 'zod'; | |
| 2 | + | |
| 3 | +/** CLAUDE.md §2 — every imported fact carries provenance; every derived value is reproducible. */ | |
| 4 | +export const EvidenceType = z.enum([ | |
| 5 | + 'registry', | |
| 6 | + 'clinical_trial', | |
| 7 | + 'meta_analysis', | |
| 8 | + 'systematic_review', | |
| 9 | + 'cohort', | |
| 10 | + 'case_control', | |
| 11 | + 'case_series', | |
| 12 | + 'case_report', | |
| 13 | + 'preclinical', | |
| 14 | + 'regulatory', | |
| 15 | + 'guideline', | |
| 16 | + 'expert_curation', | |
| 17 | + 'database', | |
| 18 | + 'computed', | |
| 19 | +]); | |
| 20 | +export type EvidenceType = z.infer<typeof EvidenceType>; | |
| 21 | + | |
| 22 | +export const AccessLevel = z.enum(['open', 'registration_required', 'controlled', 'licensed']); | |
| 23 | +export type AccessLevel = z.infer<typeof AccessLevel>; | |
| 24 | + | |
| 25 | +export const ProvenanceInput = z.object({ | |
| 26 | + sourceId: z.string(), | |
| 27 | + sourceRecordId: z.string().optional(), | |
| 28 | + sourceUrl: z.string().url().optional(), | |
| 29 | + dataset: z.string().optional(), | |
| 30 | + datasetVersion: z.string().optional(), | |
| 31 | + publicationId: z.string().optional(), | |
| 32 | + pmid: z.string().optional(), | |
| 33 | + doi: z.string().optional(), | |
| 34 | + retrievedAt: z.string().datetime(), | |
| 35 | + publishedAt: z.string().optional(), | |
| 36 | + updatedAt: z.string().optional(), | |
| 37 | + geography: z.string().optional(), | |
| 38 | + population: z.string().optional(), | |
| 39 | + cohortSize: z.number().int().nonnegative().optional(), | |
| 40 | + methodology: z.string().optional(), | |
| 41 | + evidenceType: EvidenceType, | |
| 42 | + accessLevel: AccessLevel, | |
| 43 | + confidence: z.number().min(0).max(1).optional(), | |
| 44 | + license: z.string().optional(), | |
| 45 | + ingestRunId: z.string().optional(), | |
| 46 | +}); | |
| 47 | +export type ProvenanceInput = z.infer<typeof ProvenanceInput>; | |
| 48 | + | |
| 49 | +/** Data layer a value belongs to (CLAUDE.md §2 — layers stay separable). */ | |
| 50 | +export const DataLayer = z.enum(['raw', 'normalized', 'canonical', 'derived', 'ranked', 'ai_synthesis']); | |
| 51 | +export type DataLayer = z.infer<typeof DataLayer>; | |
| 52 | + | |
| 53 | +/** Display category for scientific safety (CLAUDE.md §3). */ | |
| 54 | +export const ClaimCategory = z.enum([ | |
| 55 | + 'observed_data', | |
| 56 | + 'published_evidence', | |
| 57 | + 'curated_evidence', | |
| 58 | + 'regulatory_status', | |
| 59 | + 'clinical_guideline', | |
| 60 | + 'computed_metric', | |
| 61 | + 'ai_generated_synthesis', | |
| 62 | +]); | |
| 63 | +export type ClaimCategory = z.infer<typeof ClaimCategory>; | |
| 64 | + | |
| 65 | +/** Entity mapping confidence (CLAUDE.md §221). */ | |
| 66 | +export const MatchType = z.enum([ | |
| 67 | + 'EXACT_IDENTIFIER', | |
| 68 | + 'CURATED_EXACT', | |
| 69 | + 'ONTOLOGY_EXACT', | |
| 70 | + 'CURATED_BROADER', | |
| 71 | + 'CURATED_NARROWER', | |
| 72 | + 'ALIAS', | |
| 73 | + 'PROBABILISTIC', | |
| 74 | + 'UNRESOLVED', | |
| 75 | +]); | |
| 76 | +export type MatchType = z.infer<typeof MatchType>; | |
| 77 | + | |
| 78 | +/** Derived statistic envelope (CLAUDE.md §2). */ | |
| 79 | +export interface DerivedValue { | |
| 80 | + metric: string; | |
| 81 | + value: number; | |
| 82 | + computed: true; | |
| 83 | + formulaVersion: string; | |
| 84 | + inputs: string[]; | |
| 85 | +} | |
added
packages/shared/tsconfig.json
+9 −0
@@ -0,0 +1,9 @@ | ||
| 1 | +{ | |
| 2 | + "extends": "../../tsconfig.base.json", | |
| 3 | + "compilerOptions": { | |
| 4 | + "rootDir": "src", | |
| 5 | + "outDir": "dist", | |
| 6 | + "noEmit": true | |
| 7 | + }, | |
| 8 | + "include": ["src"] | |
| 9 | +} | |
added
pnpm-lock.yaml
+2023 −0
@@ -0,0 +1,2023 @@ | ||
| 1 | +lockfileVersion: '9.0' | |
| 2 | + | |
| 3 | +settings: | |
| 4 | + autoInstallPeers: true | |
| 5 | + excludeLinksFromLockfile: false | |
| 6 | + | |
| 7 | +importers: | |
| 8 | + | |
| 9 | + .: | |
| 10 | + dependencies: | |
| 11 | + '@cancerindex/connectors': | |
| 12 | + specifier: workspace:* | |
| 13 | + version: link:packages/connectors | |
| 14 | + '@cancerindex/database': | |
| 15 | + specifier: workspace:* | |
| 16 | + version: link:packages/database | |
| 17 | + '@cancerindex/ontology': | |
| 18 | + specifier: workspace:* | |
| 19 | + version: link:packages/ontology | |
| 20 | + '@cancerindex/ranking': | |
| 21 | + specifier: workspace:* | |
| 22 | + version: link:packages/ranking | |
| 23 | + '@cancerindex/shared': | |
| 24 | + specifier: workspace:* | |
| 25 | + version: link:packages/shared | |
| 26 | + drizzle-orm: | |
| 27 | + specifier: ^0.45.0 | |
| 28 | + version: 0.45.2(postgres@3.4.9) | |
| 29 | + postgres: | |
| 30 | + specifier: ^3.4.7 | |
| 31 | + version: 3.4.9 | |
| 32 | + devDependencies: | |
| 33 | + '@types/node': | |
| 34 | + specifier: ^24.0.0 | |
| 35 | + version: 24.13.3 | |
| 36 | + tsx: | |
| 37 | + specifier: ^4.20.0 | |
| 38 | + version: 4.23.13 | |
| 39 | + typescript: | |
| 40 | + specifier: ^5.9.3 | |
| 41 | + version: 5.9.3 | |
| 42 | + vitest: | |
| 43 | + specifier: ^3.2.0 | |
| 44 | + version: 3.2.7(@types/node@24.13.3)(tsx@4.23.13)(yaml@2.9.0) | |
| 45 | + | |
| 46 | + packages/connectors: | |
| 47 | + dependencies: | |
| 48 | + '@cancerindex/database': | |
| 49 | + specifier: workspace:* | |
| 50 | + version: link:../database | |
| 51 | + '@cancerindex/ontology': | |
| 52 | + specifier: workspace:* | |
| 53 | + version: link:../ontology | |
| 54 | + '@cancerindex/shared': | |
| 55 | + specifier: workspace:* | |
| 56 | + version: link:../shared | |
| 57 | + drizzle-orm: | |
| 58 | + specifier: ^0.45.0 | |
| 59 | + version: 0.45.2(postgres@3.4.9) | |
| 60 | + fast-xml-parser: | |
| 61 | + specifier: ^5.2.0 | |
| 62 | + version: 5.11.1 | |
| 63 | + postgres: | |
| 64 | + specifier: ^3.4.7 | |
| 65 | + version: 3.4.9 | |
| 66 | + yaml: | |
| 67 | + specifier: ^2.8.0 | |
| 68 | + version: 2.9.0 | |
| 69 | + zod: | |
| 70 | + specifier: ^4.0.0 | |
| 71 | + version: 4.5.4 | |
| 72 | + devDependencies: | |
| 73 | + '@types/node': | |
| 74 | + specifier: ^24.0.0 | |
| 75 | + version: 24.13.3 | |
| 76 | + typescript: | |
| 77 | + specifier: ^5.9.3 | |
| 78 | + version: 5.9.3 | |
| 79 | + vitest: | |
| 80 | + specifier: ^3.2.0 | |
| 81 | + version: 3.2.7(@types/node@24.13.3)(tsx@4.23.13)(yaml@2.9.0) | |
| 82 | + | |
| 83 | + packages/database: | |
| 84 | + dependencies: | |
| 85 | + '@cancerindex/shared': | |
| 86 | + specifier: workspace:* | |
| 87 | + version: link:../shared | |
| 88 | + drizzle-orm: | |
| 89 | + specifier: ^0.45.0 | |
| 90 | + version: 0.45.2(postgres@3.4.9) | |
| 91 | + postgres: | |
| 92 | + specifier: ^3.4.7 | |
| 93 | + version: 3.4.9 | |
| 94 | + devDependencies: | |
| 95 | + '@types/node': | |
| 96 | + specifier: ^24.0.0 | |
| 97 | + version: 24.13.3 | |
| 98 | + drizzle-kit: | |
| 99 | + specifier: ^0.31.0 | |
| 100 | + version: 0.31.10 | |
| 101 | + tsx: | |
| 102 | + specifier: ^4.20.0 | |
| 103 | + version: 4.23.13 | |
| 104 | + typescript: | |
| 105 | + specifier: ^5.9.3 | |
| 106 | + version: 5.9.3 | |
| 107 | + vitest: | |
| 108 | + specifier: ^3.2.0 | |
| 109 | + version: 3.2.7(@types/node@24.13.3)(tsx@4.23.13)(yaml@2.9.0) | |
| 110 | + | |
| 111 | + packages/ontology: | |
| 112 | + dependencies: | |
| 113 | + '@cancerindex/database': | |
| 114 | + specifier: workspace:* | |
| 115 | + version: link:../database | |
| 116 | + '@cancerindex/shared': | |
| 117 | + specifier: workspace:* | |
| 118 | + version: link:../shared | |
| 119 | + drizzle-orm: | |
| 120 | + specifier: ^0.45.0 | |
| 121 | + version: 0.45.2(postgres@3.4.9) | |
| 122 | + devDependencies: | |
| 123 | + '@types/node': | |
| 124 | + specifier: ^24.0.0 | |
| 125 | + version: 24.13.3 | |
| 126 | + typescript: | |
| 127 | + specifier: ^5.9.3 | |
| 128 | + version: 5.9.3 | |
| 129 | + vitest: | |
| 130 | + specifier: ^3.2.0 | |
| 131 | + version: 3.2.7(@types/node@24.13.3)(tsx@4.23.13)(yaml@2.9.0) | |
| 132 | + | |
| 133 | + packages/ranking: | |
| 134 | + dependencies: | |
| 135 | + '@cancerindex/database': | |
| 136 | + specifier: workspace:* | |
| 137 | + version: link:../database | |
| 138 | + '@cancerindex/ontology': | |
| 139 | + specifier: workspace:* | |
| 140 | + version: link:../ontology | |
| 141 | + '@cancerindex/shared': | |
| 142 | + specifier: workspace:* | |
| 143 | + version: link:../shared | |
| 144 | + drizzle-orm: | |
| 145 | + specifier: ^0.45.0 | |
| 146 | + version: 0.45.2(postgres@3.4.9) | |
| 147 | + devDependencies: | |
| 148 | + '@types/node': | |
| 149 | + specifier: ^24.0.0 | |
| 150 | + version: 24.13.3 | |
| 151 | + typescript: | |
| 152 | + specifier: ^5.9.3 | |
| 153 | + version: 5.9.3 | |
| 154 | + vitest: | |
| 155 | + specifier: ^3.2.0 | |
| 156 | + version: 3.2.7(@types/node@24.13.3)(tsx@4.23.13)(yaml@2.9.0) | |
| 157 | + | |
| 158 | + packages/shared: | |
| 159 | + dependencies: | |
| 160 | + pino: | |
| 161 | + specifier: ^9.7.0 | |
| 162 | + version: 9.14.0 | |
| 163 | + zod: | |
| 164 | + specifier: ^4.0.0 | |
| 165 | + version: 4.5.4 | |
| 166 | + devDependencies: | |
| 167 | + '@types/node': | |
| 168 | + specifier: ^24.0.0 | |
| 169 | + version: 24.13.3 | |
| 170 | + typescript: | |
| 171 | + specifier: ^5.9.3 | |
| 172 | + version: 5.9.3 | |
| 173 | + vitest: | |
| 174 | + specifier: ^3.2.0 | |
| 175 | + version: 3.2.7(@types/node@24.13.3)(tsx@4.23.13)(yaml@2.9.0) | |
| 176 | + | |
| 177 | +packages: | |
| 178 | + | |
| 179 | + '@drizzle-team/brocli@0.10.2': | |
| 180 | + resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==} | |
| 181 | + | |
| 182 | + '@esbuild-kit/core-utils@3.3.2': | |
| 183 | + resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==} | |
| 184 | + deprecated: 'Merged into tsx: https://tsx.hirok.io' | |
| 185 | + | |
| 186 | + '@esbuild-kit/esm-loader@2.6.5': | |
| 187 | + resolution: {integrity: sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==} | |
| 188 | + deprecated: 'Merged into tsx: https://tsx.hirok.io' | |
| 189 | + | |
| 190 | + '@esbuild/aix-ppc64@0.25.12': | |
| 191 | + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} | |
| 192 | + engines: {node: '>=18'} | |
| 193 | + cpu: [ppc64] | |
| 194 | + os: [aix] | |
| 195 | + | |
| 196 | + '@esbuild/aix-ppc64@0.28.2': | |
| 197 | + resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==} | |
| 198 | + engines: {node: '>=18'} | |
| 199 | + cpu: [ppc64] | |
| 200 | + os: [aix] | |
| 201 | + | |
| 202 | + '@esbuild/android-arm64@0.18.20': | |
| 203 | + resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} | |
| 204 | + engines: {node: '>=12'} | |
| 205 | + cpu: [arm64] | |
| 206 | + os: [android] | |
| 207 | + | |
| 208 | + '@esbuild/android-arm64@0.25.12': | |
| 209 | + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} | |
| 210 | + engines: {node: '>=18'} | |
| 211 | + cpu: [arm64] | |
| 212 | + os: [android] | |
| 213 | + | |
| 214 | + '@esbuild/android-arm64@0.28.2': | |
| 215 | + resolution: {integrity: sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==} | |
| 216 | + engines: {node: '>=18'} | |
| 217 | + cpu: [arm64] | |
| 218 | + os: [android] | |
| 219 | + | |
| 220 | + '@esbuild/android-arm@0.18.20': | |
| 221 | + resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} | |
| 222 | + engines: {node: '>=12'} | |
| 223 | + cpu: [arm] | |
| 224 | + os: [android] | |
| 225 | + | |
| 226 | + '@esbuild/android-arm@0.25.12': | |
| 227 | + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} | |
| 228 | + engines: {node: '>=18'} | |
| 229 | + cpu: [arm] | |
| 230 | + os: [android] | |
| 231 | + | |
| 232 | + '@esbuild/android-arm@0.28.2': | |
| 233 | + resolution: {integrity: sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==} | |
| 234 | + engines: {node: '>=18'} | |
| 235 | + cpu: [arm] | |
| 236 | + os: [android] | |
| 237 | + | |
| 238 | + '@esbuild/android-x64@0.18.20': | |
| 239 | + resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} | |
| 240 | + engines: {node: '>=12'} | |
| 241 | + cpu: [x64] | |
| 242 | + os: [android] | |
| 243 | + | |
| 244 | + '@esbuild/android-x64@0.25.12': | |
| 245 | + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} | |
| 246 | + engines: {node: '>=18'} | |
| 247 | + cpu: [x64] | |
| 248 | + os: [android] | |
| 249 | + | |
| 250 | + '@esbuild/android-x64@0.28.2': | |
| 251 | + resolution: {integrity: sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==} | |
| 252 | + engines: {node: '>=18'} | |
| 253 | + cpu: [x64] | |
| 254 | + os: [android] | |
| 255 | + | |
| 256 | + '@esbuild/darwin-arm64@0.18.20': | |
| 257 | + resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} | |
| 258 | + engines: {node: '>=12'} | |
| 259 | + cpu: [arm64] | |
| 260 | + os: [darwin] | |
| 261 | + | |
| 262 | + '@esbuild/darwin-arm64@0.25.12': | |
| 263 | + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} | |
| 264 | + engines: {node: '>=18'} | |
| 265 | + cpu: [arm64] | |
| 266 | + os: [darwin] | |
| 267 | + | |
| 268 | + '@esbuild/darwin-arm64@0.28.2': | |
| 269 | + resolution: {integrity: sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==} | |
| 270 | + engines: {node: '>=18'} | |
| 271 | + cpu: [arm64] | |
| 272 | + os: [darwin] | |
| 273 | + | |
| 274 | + '@esbuild/darwin-x64@0.18.20': | |
| 275 | + resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} | |
| 276 | + engines: {node: '>=12'} | |
| 277 | + cpu: [x64] | |
| 278 | + os: [darwin] | |
| 279 | + | |
| 280 | + '@esbuild/darwin-x64@0.25.12': | |
| 281 | + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} | |
| 282 | + engines: {node: '>=18'} | |
| 283 | + cpu: [x64] | |
| 284 | + os: [darwin] | |
| 285 | + | |
| 286 | + '@esbuild/darwin-x64@0.28.2': | |
| 287 | + resolution: {integrity: sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==} | |
| 288 | + engines: {node: '>=18'} | |
| 289 | + cpu: [x64] | |
| 290 | + os: [darwin] | |
| 291 | + | |
| 292 | + '@esbuild/freebsd-arm64@0.18.20': | |
| 293 | + resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} | |
| 294 | + engines: {node: '>=12'} | |
| 295 | + cpu: [arm64] | |
| 296 | + os: [freebsd] | |
| 297 | + | |
| 298 | + '@esbuild/freebsd-arm64@0.25.12': | |
| 299 | + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} | |
| 300 | + engines: {node: '>=18'} | |
| 301 | + cpu: [arm64] | |
| 302 | + os: [freebsd] | |
| 303 | + | |
| 304 | + '@esbuild/freebsd-arm64@0.28.2': | |
| 305 | + resolution: {integrity: sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==} | |
| 306 | + engines: {node: '>=18'} | |
| 307 | + cpu: [arm64] | |
| 308 | + os: [freebsd] | |
| 309 | + | |
| 310 | + '@esbuild/freebsd-x64@0.18.20': | |
| 311 | + resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} | |
| 312 | + engines: {node: '>=12'} | |
| 313 | + cpu: [x64] | |
| 314 | + os: [freebsd] | |
| 315 | + | |
| 316 | + '@esbuild/freebsd-x64@0.25.12': | |
| 317 | + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} | |
| 318 | + engines: {node: '>=18'} | |
| 319 | + cpu: [x64] | |
| 320 | + os: [freebsd] | |
| 321 | + | |
| 322 | + '@esbuild/freebsd-x64@0.28.2': | |
| 323 | + resolution: {integrity: sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==} | |
| 324 | + engines: {node: '>=18'} | |
| 325 | + cpu: [x64] | |
| 326 | + os: [freebsd] | |
| 327 | + | |
| 328 | + '@esbuild/linux-arm64@0.18.20': | |
| 329 | + resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} | |
| 330 | + engines: {node: '>=12'} | |
| 331 | + cpu: [arm64] | |
| 332 | + os: [linux] | |
| 333 | + | |
| 334 | + '@esbuild/linux-arm64@0.25.12': | |
| 335 | + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} | |
| 336 | + engines: {node: '>=18'} | |
| 337 | + cpu: [arm64] | |
| 338 | + os: [linux] | |
| 339 | + | |
| 340 | + '@esbuild/linux-arm64@0.28.2': | |
| 341 | + resolution: {integrity: sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==} | |
| 342 | + engines: {node: '>=18'} | |
| 343 | + cpu: [arm64] | |
| 344 | + os: [linux] | |
| 345 | + | |
| 346 | + '@esbuild/linux-arm@0.18.20': | |
| 347 | + resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} | |
| 348 | + engines: {node: '>=12'} | |
| 349 | + cpu: [arm] | |
| 350 | + os: [linux] | |
| 351 | + | |
| 352 | + '@esbuild/linux-arm@0.25.12': | |
| 353 | + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} | |
| 354 | + engines: {node: '>=18'} | |
| 355 | + cpu: [arm] | |
| 356 | + os: [linux] | |
| 357 | + | |
| 358 | + '@esbuild/linux-arm@0.28.2': | |
| 359 | + resolution: {integrity: sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==} | |
| 360 | + engines: {node: '>=18'} | |
| 361 | + cpu: [arm] | |
| 362 | + os: [linux] | |
| 363 | + | |
| 364 | + '@esbuild/linux-ia32@0.18.20': | |
| 365 | + resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} | |
| 366 | + engines: {node: '>=12'} | |
| 367 | + cpu: [ia32] | |
| 368 | + os: [linux] | |
| 369 | + | |
| 370 | + '@esbuild/linux-ia32@0.25.12': | |
| 371 | + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} | |
| 372 | + engines: {node: '>=18'} | |
| 373 | + cpu: [ia32] | |
| 374 | + os: [linux] | |
| 375 | + | |
| 376 | + '@esbuild/linux-ia32@0.28.2': | |
| 377 | + resolution: {integrity: sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==} | |
| 378 | + engines: {node: '>=18'} | |
| 379 | + cpu: [ia32] | |
| 380 | + os: [linux] | |
| 381 | + | |
| 382 | + '@esbuild/linux-loong64@0.18.20': | |
| 383 | + resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} | |
| 384 | + engines: {node: '>=12'} | |
| 385 | + cpu: [loong64] | |
| 386 | + os: [linux] | |
| 387 | + | |
| 388 | + '@esbuild/linux-loong64@0.25.12': | |
| 389 | + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} | |
| 390 | + engines: {node: '>=18'} | |
| 391 | + cpu: [loong64] | |
| 392 | + os: [linux] | |
| 393 | + | |
| 394 | + '@esbuild/linux-loong64@0.28.2': | |
| 395 | + resolution: {integrity: sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==} | |
| 396 | + engines: {node: '>=18'} | |
| 397 | + cpu: [loong64] | |
| 398 | + os: [linux] | |
| 399 | + | |
| 400 | + '@esbuild/linux-mips64el@0.18.20': | |
| 401 | + resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} | |
| 402 | + engines: {node: '>=12'} | |
| 403 | + cpu: [mips64el] | |
| 404 | + os: [linux] | |
| 405 | + | |
| 406 | + '@esbuild/linux-mips64el@0.25.12': | |
| 407 | + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} | |
| 408 | + engines: {node: '>=18'} | |
| 409 | + cpu: [mips64el] | |
| 410 | + os: [linux] | |
| 411 | + | |
| 412 | + '@esbuild/linux-mips64el@0.28.2': | |
| 413 | + resolution: {integrity: sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==} | |
| 414 | + engines: {node: '>=18'} | |
| 415 | + cpu: [mips64el] | |
| 416 | + os: [linux] | |
| 417 | + | |
| 418 | + '@esbuild/linux-ppc64@0.18.20': | |
| 419 | + resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} | |
| 420 | + engines: {node: '>=12'} | |
| 421 | + cpu: [ppc64] | |
| 422 | + os: [linux] | |
| 423 | + | |
| 424 | + '@esbuild/linux-ppc64@0.25.12': | |
| 425 | + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} | |
| 426 | + engines: {node: '>=18'} | |
| 427 | + cpu: [ppc64] | |
| 428 | + os: [linux] | |
| 429 | + | |
| 430 | + '@esbuild/linux-ppc64@0.28.2': | |
| 431 | + resolution: {integrity: sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==} | |
| 432 | + engines: {node: '>=18'} | |
| 433 | + cpu: [ppc64] | |
| 434 | + os: [linux] | |
| 435 | + | |
| 436 | + '@esbuild/linux-riscv64@0.18.20': | |
| 437 | + resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} | |
| 438 | + engines: {node: '>=12'} | |
| 439 | + cpu: [riscv64] | |
| 440 | + os: [linux] | |
| 441 | + | |
| 442 | + '@esbuild/linux-riscv64@0.25.12': | |
| 443 | + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} | |
| 444 | + engines: {node: '>=18'} | |
| 445 | + cpu: [riscv64] | |
| 446 | + os: [linux] | |
| 447 | + | |
| 448 | + '@esbuild/linux-riscv64@0.28.2': | |
| 449 | + resolution: {integrity: sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==} | |
| 450 | + engines: {node: '>=18'} | |
| 451 | + cpu: [riscv64] | |
| 452 | + os: [linux] | |
| 453 | + | |
| 454 | + '@esbuild/linux-s390x@0.18.20': | |
| 455 | + resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} | |
| 456 | + engines: {node: '>=12'} | |
| 457 | + cpu: [s390x] | |
| 458 | + os: [linux] | |
| 459 | + | |
| 460 | + '@esbuild/linux-s390x@0.25.12': | |
| 461 | + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} | |
| 462 | + engines: {node: '>=18'} | |
| 463 | + cpu: [s390x] | |
| 464 | + os: [linux] | |
| 465 | + | |
| 466 | + '@esbuild/linux-s390x@0.28.2': | |
| 467 | + resolution: {integrity: sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==} | |
| 468 | + engines: {node: '>=18'} | |
| 469 | + cpu: [s390x] | |
| 470 | + os: [linux] | |
| 471 | + | |
| 472 | + '@esbuild/linux-x64@0.18.20': | |
| 473 | + resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} | |
| 474 | + engines: {node: '>=12'} | |
| 475 | + cpu: [x64] | |
| 476 | + os: [linux] | |
| 477 | + | |
| 478 | + '@esbuild/linux-x64@0.25.12': | |
| 479 | + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} | |
| 480 | + engines: {node: '>=18'} | |
| 481 | + cpu: [x64] | |
| 482 | + os: [linux] | |
| 483 | + | |
| 484 | + '@esbuild/linux-x64@0.28.2': | |
| 485 | + resolution: {integrity: sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==} | |
| 486 | + engines: {node: '>=18'} | |
| 487 | + cpu: [x64] | |
| 488 | + os: [linux] | |
| 489 | + | |
| 490 | + '@esbuild/netbsd-arm64@0.25.12': | |
| 491 | + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} | |
| 492 | + engines: {node: '>=18'} | |
| 493 | + cpu: [arm64] | |
| 494 | + os: [netbsd] | |
| 495 | + | |
| 496 | + '@esbuild/netbsd-arm64@0.28.2': | |
| 497 | + resolution: {integrity: sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==} | |
| 498 | + engines: {node: '>=18'} | |
| 499 | + cpu: [arm64] | |
| 500 | + os: [netbsd] | |
| 501 | + | |
| 502 | + '@esbuild/netbsd-x64@0.18.20': | |
| 503 | + resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} | |
| 504 | + engines: {node: '>=12'} | |
| 505 | + cpu: [x64] | |
| 506 | + os: [netbsd] | |
| 507 | + | |
| 508 | + '@esbuild/netbsd-x64@0.25.12': | |
| 509 | + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} | |
| 510 | + engines: {node: '>=18'} | |
| 511 | + cpu: [x64] | |
| 512 | + os: [netbsd] | |
| 513 | + | |
| 514 | + '@esbuild/netbsd-x64@0.28.2': | |
| 515 | + resolution: {integrity: sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==} | |
| 516 | + engines: {node: '>=18'} | |
| 517 | + cpu: [x64] | |
| 518 | + os: [netbsd] | |
| 519 | + | |
| 520 | + '@esbuild/openbsd-arm64@0.25.12': | |
| 521 | + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} | |
| 522 | + engines: {node: '>=18'} | |
| 523 | + cpu: [arm64] | |
| 524 | + os: [openbsd] | |
| 525 | + | |
| 526 | + '@esbuild/openbsd-arm64@0.28.2': | |
| 527 | + resolution: {integrity: sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==} | |
| 528 | + engines: {node: '>=18'} | |
| 529 | + cpu: [arm64] | |
| 530 | + os: [openbsd] | |
| 531 | + | |
| 532 | + '@esbuild/openbsd-x64@0.18.20': | |
| 533 | + resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} | |
| 534 | + engines: {node: '>=12'} | |
| 535 | + cpu: [x64] | |
| 536 | + os: [openbsd] | |
| 537 | + | |
| 538 | + '@esbuild/openbsd-x64@0.25.12': | |
| 539 | + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} | |
| 540 | + engines: {node: '>=18'} | |
| 541 | + cpu: [x64] | |
| 542 | + os: [openbsd] | |
| 543 | + | |
| 544 | + '@esbuild/openbsd-x64@0.28.2': | |
| 545 | + resolution: {integrity: sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==} | |
| 546 | + engines: {node: '>=18'} | |
| 547 | + cpu: [x64] | |
| 548 | + os: [openbsd] | |
| 549 | + | |
| 550 | + '@esbuild/openharmony-arm64@0.25.12': | |
| 551 | + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} | |
| 552 | + engines: {node: '>=18'} | |
| 553 | + cpu: [arm64] | |
| 554 | + os: [openharmony] | |
| 555 | + | |
| 556 | + '@esbuild/openharmony-arm64@0.28.2': | |
| 557 | + resolution: {integrity: sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==} | |
| 558 | + engines: {node: '>=18'} | |
| 559 | + cpu: [arm64] | |
| 560 | + os: [openharmony] | |
| 561 | + | |
| 562 | + '@esbuild/sunos-x64@0.18.20': | |
| 563 | + resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} | |
| 564 | + engines: {node: '>=12'} | |
| 565 | + cpu: [x64] | |
| 566 | + os: [sunos] | |
| 567 | + | |
| 568 | + '@esbuild/sunos-x64@0.25.12': | |
| 569 | + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} | |
| 570 | + engines: {node: '>=18'} | |
| 571 | + cpu: [x64] | |
| 572 | + os: [sunos] | |
| 573 | + | |
| 574 | + '@esbuild/sunos-x64@0.28.2': | |
| 575 | + resolution: {integrity: sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==} | |
| 576 | + engines: {node: '>=18'} | |
| 577 | + cpu: [x64] | |
| 578 | + os: [sunos] | |
| 579 | + | |
| 580 | + '@esbuild/win32-arm64@0.18.20': | |
| 581 | + resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} | |
| 582 | + engines: {node: '>=12'} | |
| 583 | + cpu: [arm64] | |
| 584 | + os: [win32] | |
| 585 | + | |
| 586 | + '@esbuild/win32-arm64@0.25.12': | |
| 587 | + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} | |
| 588 | + engines: {node: '>=18'} | |
| 589 | + cpu: [arm64] | |
| 590 | + os: [win32] | |
| 591 | + | |
| 592 | + '@esbuild/win32-arm64@0.28.2': | |
| 593 | + resolution: {integrity: sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==} | |
| 594 | + engines: {node: '>=18'} | |
| 595 | + cpu: [arm64] | |
| 596 | + os: [win32] | |
| 597 | + | |
| 598 | + '@esbuild/win32-ia32@0.18.20': | |
| 599 | + resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} | |
| 600 | + engines: {node: '>=12'} | |
| 601 | + cpu: [ia32] | |
| 602 | + os: [win32] | |
| 603 | + | |
| 604 | + '@esbuild/win32-ia32@0.25.12': | |
| 605 | + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} | |
| 606 | + engines: {node: '>=18'} | |
| 607 | + cpu: [ia32] | |
| 608 | + os: [win32] | |
| 609 | + | |
| 610 | + '@esbuild/win32-ia32@0.28.2': | |
| 611 | + resolution: {integrity: sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==} | |
| 612 | + engines: {node: '>=18'} | |
| 613 | + cpu: [ia32] | |
| 614 | + os: [win32] | |
| 615 | + | |
| 616 | + '@esbuild/win32-x64@0.18.20': | |
| 617 | + resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} | |
| 618 | + engines: {node: '>=12'} | |
| 619 | + cpu: [x64] | |
| 620 | + os: [win32] | |
| 621 | + | |
| 622 | + '@esbuild/win32-x64@0.25.12': | |
| 623 | + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} | |
| 624 | + engines: {node: '>=18'} | |
| 625 | + cpu: [x64] | |
| 626 | + os: [win32] | |
| 627 | + | |
| 628 | + '@esbuild/win32-x64@0.28.2': | |
| 629 | + resolution: {integrity: sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==} | |
| 630 | + engines: {node: '>=18'} | |
| 631 | + cpu: [x64] | |
| 632 | + os: [win32] | |
| 633 | + | |
| 634 | + '@jridgewell/sourcemap-codec@1.6.0': | |
| 635 | + resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} | |
| 636 | + | |
| 637 | + '@napi-rs/lzma-linux-x64-gnu@1.5.1': | |
| 638 | + resolution: {integrity: sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==} | |
| 639 | + engines: {node: ^22.20 || ^24.12 || >=25} | |
| 640 | + cpu: [x64] | |
| 641 | + os: [linux] | |
| 642 | + libc: [glibc] | |
| 643 | + | |
| 644 | + '@nodable/entities@3.0.0': | |
| 645 | + resolution: {integrity: sha512-8L9xFeTYKhm49xfIypoe2W5wV1m/3Z58kT+7kR9A8OyFxcPduI4VmxaUMQyKYrRjUoLLSXv6EKKID5Tvj9cUVw==} | |
| 646 | + | |
| 647 | + '@pinojs/redact@0.4.0': | |
| 648 | + resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} | |
| 649 | + | |
| 650 | + '@rollup/rollup-android-arm-eabi@4.63.1': | |
| 651 | + resolution: {integrity: sha512-UZ8sUxPTiHWYX9QNdJedb1kDZSpS1t/VPWBWGSgqHNi9w3Cu6IXvu2mzbhiTiPvtrqgTQJ+zqiAq2iPIPilpaQ==} | |
| 652 | + cpu: [arm] | |
| 653 | + os: [android] | |
| 654 | + | |
| 655 | + '@rollup/rollup-android-arm64@4.63.1': | |
| 656 | + resolution: {integrity: sha512-cQ4nFQABN5cDvDpbvJ7bMStCpnaVxynZrRMfUJYgxcIk9Sh54FIO1vtfkg0B69REjER77ioZ/ov+eAApx/KmLQ==} | |
| 657 | + cpu: [arm64] | |
| 658 | + os: [android] | |
| 659 | + | |
| 660 | + '@rollup/rollup-darwin-arm64@4.63.1': | |
| 661 | + resolution: {integrity: sha512-FQNqd1lRy/0QhDk3xeRIkSBiCpXCiDnZO3YLVdcDKN1UBiKToNftCzcXYNLshmPDUMlu2TdeS8tGcsU6f3YF1Q==} | |
| 662 | + cpu: [arm64] | |
| 663 | + os: [darwin] | |
| 664 | + | |
| 665 | + '@rollup/rollup-darwin-x64@4.63.1': | |
| 666 | + resolution: {integrity: sha512-pvD16V939D3CloK0+qikpGaxiPrDUXTe7Y5cWOMkMSy7m1cawa8EGy/kXYi/G/cKAC4HDAbSnzCIk1WmsoOKXg==} | |
| 667 | + cpu: [x64] | |
| 668 | + os: [darwin] | |
| 669 | + | |
| 670 | + '@rollup/rollup-freebsd-arm64@4.63.1': | |
| 671 | + resolution: {integrity: sha512-pcFGeL2345VwdTnJhA6zLbew+YgWB0qBG2+dMtXjCicf6+rm6kO6cOoh5VnTe0ZMrMRgRyuHmCJxZWrIdzYuOw==} | |
| 672 | + cpu: [arm64] | |
| 673 | + os: [freebsd] | |
| 674 | + | |
| 675 | + '@rollup/rollup-freebsd-x64@4.63.1': | |
| 676 | + resolution: {integrity: sha512-mRJlqSRulVzcKq/LKA6ICSIc3K/l4fzlVn/gePn2nXIHy8seRi5z/eeRE0d/XMBxcMldiXtQTSpRj0tkkC3g8Q==} | |
| 677 | + cpu: [x64] | |
| 678 | + os: [freebsd] | |
| 679 | + | |
| 680 | + '@rollup/rollup-linux-arm-gnueabihf@4.63.1': | |
| 681 | + resolution: {integrity: sha512-YDUNvVM85TI3g/1OpnqKP1h4NeW/j64DfWMf+G3M809xNk1bJSnpFp4sh83NpmVE5DXnkh8ULor4LTVZKoYLHw==} | |
| 682 | + cpu: [arm] | |
| 683 | + os: [linux] | |
| 684 | + libc: [glibc] | |
| 685 | + | |
| 686 | + '@rollup/rollup-linux-arm-musleabihf@4.63.1': | |
| 687 | + resolution: {integrity: sha512-7Mcn71p9ZuQFAj+h+dhQXy/yeLePRS2yKRnmW1DijA9thKO5qap0GNOIQK4yQ6iP3SU0Mrb/yWo8h8vgRba8lw==} | |
| 688 | + cpu: [arm] | |
| 689 | + os: [linux] | |
| 690 | + libc: [musl] | |
| 691 | + | |
| 692 | + '@rollup/rollup-linux-arm64-gnu@4.63.1': | |
| 693 | + resolution: {integrity: sha512-4YiLQTX6U4CSl0L9cluep9A9W6UmTfqBDc2/CH6wlu54pl4E7Jn3cOD8oxzvBDEGk/JMKgJ47C8g+radF7mwvg==} | |
| 694 | + cpu: [arm64] | |
| 695 | + os: [linux] | |
| 696 | + libc: [glibc] | |
| 697 | + | |
| 698 | + '@rollup/rollup-linux-arm64-musl@4.63.1': | |
| 699 | + resolution: {integrity: sha512-2ra8F7w8OquwZN9z2/fKFnli69wa8PLwaVzRMIPGb13ByMJwC28Fbp8YcVGoUhlYMTt7j5j9bNgpysrN2UM+vw==} | |
| 700 | + cpu: [arm64] | |
| 701 | + os: [linux] | |
| 702 | + libc: [musl] | |
| 703 | + | |
| 704 | + '@rollup/rollup-linux-loong64-gnu@4.63.1': | |
| 705 | + resolution: {integrity: sha512-Sy20ncyhjmBP0Ml+UvQbimjlk6VFgjW5uNP+qqwHB00mTE8Bl2C1TuHTlRwK2YoXeZbee5lP2XevBWVkAQAtSQ==} | |
| 706 | + cpu: [loong64] | |
| 707 | + os: [linux] | |
| 708 | + libc: [glibc] | |
| 709 | + | |
| 710 | + '@rollup/rollup-linux-loong64-musl@4.63.1': | |
| 711 | + resolution: {integrity: sha512-noITLp8oNjYliPnGWmLyelIHwULGqbHloQHGw1rtxbWhTuWooRpnZarZQJ1y9EUC4szuCusCc+HEpUtxpIwYvA==} | |
| 712 | + cpu: [loong64] | |
| 713 | + os: [linux] | |
| 714 | + libc: [musl] | |
| 715 | + | |
| 716 | + '@rollup/rollup-linux-ppc64-gnu@4.63.1': | |
| 717 | + resolution: {integrity: sha512-hlxxXd+F1mWiAcaFR7Sv9ZQT6m6UfI8+Vy/kFJzztq2pDMU/0wZ9sish0iszNZvsQDo8Gc0i5yuFEOz5dDf6fA==} | |
| 718 | + cpu: [ppc64] | |
| 719 | + os: [linux] | |
| 720 | + libc: [glibc] | |
| 721 | + | |
| 722 | + '@rollup/rollup-linux-ppc64-musl@4.63.1': | |
| 723 | + resolution: {integrity: sha512-EF7OpqQTQ/BvGqLzUi4rEHuagCV9MugAUXSHemwPW5vxZ75RR+jxO/2j95Ph2dalMpFHSVECjRoioHZgA9zOYA==} | |
| 724 | + cpu: [ppc64] | |
| 725 | + os: [linux] | |
| 726 | + libc: [musl] | |
| 727 | + | |
| 728 | + '@rollup/rollup-linux-riscv64-gnu@4.63.1': | |
| 729 | + resolution: {integrity: sha512-wQO3JesW9PRkwlabQ27y7sPfVOOTLRG73I4F2UYHG5PXun3J9U3y+b7ezVKSYbsvSKGQ1k1cq8Qlun4C9kLt3w==} | |
| 730 | + cpu: [riscv64] | |
| 731 | + os: [linux] | |
| 732 | + libc: [glibc] | |
| 733 | + | |
| 734 | + '@rollup/rollup-linux-riscv64-musl@4.63.1': | |
| 735 | + resolution: {integrity: sha512-ouAGwhO6wHRXdnOVCOsB0tRFkA7nhNB2Nwax6oECXN0YiN8EYUTBAOudADOB1PI+yDL61TeNx/u7MVCzksNbkQ==} | |
| 736 | + cpu: [riscv64] | |
| 737 | + os: [linux] | |
| 738 | + libc: [musl] | |
| 739 | + | |
| 740 | + '@rollup/rollup-linux-s390x-gnu@4.63.1': | |
| 741 | + resolution: {integrity: sha512-q2R38Sn+1J8RxhfJ+T54wSWmyKXWec+9jgDfqO2AtArEqHO5R2aeayp5H5OYLr5UYDVGsVaZPEFUooMhYCdz5A==} | |
| 742 | + cpu: [s390x] | |
| 743 | + os: [linux] | |
| 744 | + libc: [glibc] | |
| 745 | + | |
| 746 | + '@rollup/rollup-linux-x64-gnu@4.63.1': | |
| 747 | + resolution: {integrity: sha512-gfI5T24WLLuFfSKw7Go/zDXjAAV0fny0swTaDv+WjK7vqcw4cRhFfdsyKL1n+ukI+ooBxn3bVQnyrn06WpI50w==} | |
| 748 | + cpu: [x64] | |
| 749 | + os: [linux] | |
| 750 | + libc: [glibc] | |
| 751 | + | |
| 752 | + '@rollup/rollup-linux-x64-musl@4.63.1': | |
| 753 | + resolution: {integrity: sha512-4h6XqthmB4Hspji84wvgk+ElodTsGj+dbZqHJHHtKxj4mYq0ANSEEPX9ys3moJueqsRjwpaJYH7874Itwnj2ow==} | |
| 754 | + cpu: [x64] | |
| 755 | + os: [linux] | |
| 756 | + libc: [musl] | |
| 757 | + | |
| 758 | + '@rollup/rollup-openbsd-x64@4.63.1': | |
| 759 | + resolution: {integrity: sha512-dlfCOa87o1VAYegLQ9EKilx2JCeRofiyPGhTCmqnuXZ6bMPiycO1rq1+sKoulAp7pGLIsTIw+1x5R+zgh5LhhA==} | |
| 760 | + cpu: [x64] | |
| 761 | + os: [openbsd] | |
| 762 | + | |
| 763 | + '@rollup/rollup-openharmony-arm64@4.63.1': | |
| 764 | + resolution: {integrity: sha512-cjkLbOlfcm3QGhMM1J5zaZjsw1GggbN6rw9UTSSRrPrR1KkcXnN7Uq9rPw34xImQ9VOY9GN+6u2Zj80B9ptkcw==} | |
| 765 | + cpu: [arm64] | |
| 766 | + os: [openharmony] | |
| 767 | + | |
| 768 | + '@rollup/rollup-win32-arm64-msvc@4.63.1': | |
| 769 | + resolution: {integrity: sha512-Li1KdUnWGE4N3e1F/B4RTB1ms+nG4WBgjByO46pkeBVX/2UBsY53xf5vK9WygVmnH3RwncIST7lkSdLSY6P9lg==} | |
| 770 | + cpu: [arm64] | |
| 771 | + os: [win32] | |
| 772 | + | |
| 773 | + '@rollup/rollup-win32-ia32-msvc@4.63.1': | |
| 774 | + resolution: {integrity: sha512-t4ZYOSoLTgwhuFMrmTMLx/+i1DQVK7HYqMc6kY46EApwi8X0nIVphzdNoThU3xt6n+N5urG1/gxBdCaKDLavfg==} | |
| 775 | + cpu: [ia32] | |
| 776 | + os: [win32] | |
| 777 | + | |
| 778 | + '@rollup/rollup-win32-x64-gnu@4.63.1': | |
| 779 | + resolution: {integrity: sha512-RgroPfMmKlD1RzSDxvwgcPiy2HNQKoYV7OmwIXDsk73uKW5t6B/V8KIy27SMv/FNXFo/oSBtWc9J0X7t91ezZg==} | |
| 780 | + cpu: [x64] | |
| 781 | + os: [win32] | |
| 782 | + | |
| 783 | + '@rollup/rollup-win32-x64-msvc@4.63.1': | |
| 784 | + resolution: {integrity: sha512-at8QVep6S3h5Y6gSbdGU06bRY5WJkf6WUduM9YtvYMbYhB1MOFfUgc6kehitQXzOtMSaT70q7f9ydPhpqu821w==} | |
| 785 | + cpu: [x64] | |
| 786 | + os: [win32] | |
| 787 | + | |
| 788 | + '@types/chai@5.2.3': | |
| 789 | + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} | |
| 790 | + | |
| 791 | + '@types/deep-eql@4.0.2': | |
| 792 | + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} | |
| 793 | + | |
| 794 | + '@types/estree@1.0.9': | |
| 795 | + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} | |
| 796 | + | |
| 797 | + '@types/node@24.13.3': | |
| 798 | + resolution: {integrity: sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==} | |
| 799 | + | |
| 800 | + '@vitest/expect@3.2.7': | |
| 801 | + resolution: {integrity: sha512-E8eBXaKibuvH2pSZErOjdVb5vF4PbKYcrnluBTYxEk1l/VhhwZg1kZQsdtjq+CsF5CFydf2Rdkz7jDHKSisi3w==} | |
| 802 | + | |
| 803 | + '@vitest/mocker@3.2.7': | |
| 804 | + resolution: {integrity: sha512-Trr0hYO9CM3Wj6ksWHRhK9IZpIY6wTMO5u/MqXurMxT57sWBaOPEtP3Oq60ihZuh5JsiagKfz95OcxdEP6dBrA==} | |
| 805 | + peerDependencies: | |
| 806 | + msw: ^2.4.9 | |
| 807 | + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 | |
| 808 | + peerDependenciesMeta: | |
| 809 | + msw: | |
| 810 | + optional: true | |
| 811 | + vite: | |
| 812 | + optional: true | |
| 813 | + | |
| 814 | + '@vitest/pretty-format@3.2.7': | |
| 815 | + resolution: {integrity: sha512-KUHlwqVu0sRlhCdyPdQ/wBoTfRahjUky1MubOmYw9fWfIZy1gNoHpuaaQBPAaMaVYdQYHJLurzj8ECCj5OwTqA==} | |
| 816 | + | |
| 817 | + '@vitest/runner@3.2.7': | |
| 818 | + resolution: {integrity: sha512-sB9y4ovltoQP+WaUPwmSxO9WIg9Ig694Di5PalVPsYHklAdE027mehpWF2SQSVq+k6sFgaivbTjTJwZLSHbedA==} | |
| 819 | + | |
| 820 | + '@vitest/snapshot@3.2.7': | |
| 821 | + resolution: {integrity: sha512-7C+MwShwtBSI5Buwoyg3s/iY1eHL9PKAf+O1wVh/TdnjXUtkoL/9YQtre90i4MtNXM6edP1wJ2zOBpfCyhIS7g==} | |
| 822 | + | |
| 823 | + '@vitest/spy@3.2.7': | |
| 824 | + resolution: {integrity: sha512-Q2eQGI6d2L/hBtZ0qNuKcAGid68XK6cv1xsoaIma6PaJhHPoqcEJhYpXZ/5myCMqkNgtP6UKuBhbc0nHKnrkuQ==} | |
| 825 | + | |
| 826 | + '@vitest/utils@3.2.7': | |
| 827 | + resolution: {integrity: sha512-x6BDOd7dyo3PFLY3I9/HJ25X/6OurhGXk2/B9gOZNPF7XDVjeBK4k01lQE5uvDpbuheErh91qYuE1E2OEjK3Rw==} | |
| 828 | + | |
| 829 | + anynum@1.0.1: | |
| 830 | + resolution: {integrity: sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==} | |
| 831 | + | |
| 832 | + assertion-error@2.0.1: | |
| 833 | + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} | |
| 834 | + engines: {node: '>=12'} | |
| 835 | + | |
| 836 | + atomic-sleep@1.0.0: | |
| 837 | + resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} | |
| 838 | + engines: {node: '>=8.0.0'} | |
| 839 | + | |
| 840 | + buffer-from@1.1.2: | |
| 841 | + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} | |
| 842 | + | |
| 843 | + cac@6.7.14: | |
| 844 | + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} | |
| 845 | + engines: {node: '>=8'} | |
| 846 | + | |
| 847 | + chai@5.3.3: | |
| 848 | + resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} | |
| 849 | + engines: {node: '>=18'} | |
| 850 | + | |
| 851 | + check-error@2.1.3: | |
| 852 | + resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} | |
| 853 | + engines: {node: '>= 16'} | |
| 854 | + | |
| 855 | + debug@4.4.3: | |
| 856 | + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} | |
| 857 | + engines: {node: '>=6.0'} | |
| 858 | + peerDependencies: | |
| 859 | + supports-color: '*' | |
| 860 | + peerDependenciesMeta: | |
| 861 | + supports-color: | |
| 862 | + optional: true | |
| 863 | + | |
| 864 | + deep-eql@5.0.2: | |
| 865 | + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} | |
| 866 | + engines: {node: '>=6'} | |
| 867 | + | |
| 868 | + drizzle-kit@0.31.10: | |
| 869 | + resolution: {integrity: sha512-7OZcmQUrdGI+DUNNsKBn1aW8qSoKuTH7d0mYgSP8bAzdFzKoovxEFnoGQp2dVs82EOJeYycqRtciopszwUf8bw==} | |
| 870 | + hasBin: true | |
| 871 | + | |
| 872 | + drizzle-orm@0.45.2: | |
| 873 | + resolution: {integrity: sha512-kY0BSaTNYWnoDMVoyY8uxmyHjpJW1geOmBMdSSicKo9CIIWkSxMIj2rkeSR51b8KAPB7m+qysjuHme5nKP+E5Q==} | |
| 874 | + peerDependencies: | |
| 875 | + '@aws-sdk/client-rds-data': '>=3' | |
| 876 | + '@cloudflare/workers-types': '>=4' | |
| 877 | + '@electric-sql/pglite': '>=0.2.0' | |
| 878 | + '@libsql/client': '>=0.10.0' | |
| 879 | + '@libsql/client-wasm': '>=0.10.0' | |
| 880 | + '@neondatabase/serverless': '>=0.10.0' | |
| 881 | + '@op-engineering/op-sqlite': '>=2' | |
| 882 | + '@opentelemetry/api': ^1.4.1 | |
| 883 | + '@planetscale/database': '>=1.13' | |
| 884 | + '@prisma/client': '*' | |
| 885 | + '@tidbcloud/serverless': '*' | |
| 886 | + '@types/better-sqlite3': '*' | |
| 887 | + '@types/pg': '*' | |
| 888 | + '@types/sql.js': '*' | |
| 889 | + '@upstash/redis': '>=1.34.7' | |
| 890 | + '@vercel/postgres': '>=0.8.0' | |
| 891 | + '@xata.io/client': '*' | |
| 892 | + better-sqlite3: '>=7' | |
| 893 | + bun-types: '*' | |
| 894 | + expo-sqlite: '>=14.0.0' | |
| 895 | + gel: '>=2' | |
| 896 | + knex: '*' | |
| 897 | + kysely: '*' | |
| 898 | + mysql2: '>=2' | |
| 899 | + pg: '>=8' | |
| 900 | + postgres: '>=3' | |
| 901 | + prisma: '*' | |
| 902 | + sql.js: '>=1' | |
| 903 | + sqlite3: '>=5' | |
| 904 | + peerDependenciesMeta: | |
| 905 | + '@aws-sdk/client-rds-data': | |
| 906 | + optional: true | |
| 907 | + '@cloudflare/workers-types': | |
| 908 | + optional: true | |
| 909 | + '@electric-sql/pglite': | |
| 910 | + optional: true | |
| 911 | + '@libsql/client': | |
| 912 | + optional: true | |
| 913 | + '@libsql/client-wasm': | |
| 914 | + optional: true | |
| 915 | + '@neondatabase/serverless': | |
| 916 | + optional: true | |
| 917 | + '@op-engineering/op-sqlite': | |
| 918 | + optional: true | |
| 919 | + '@opentelemetry/api': | |
| 920 | + optional: true | |
| 921 | + '@planetscale/database': | |
| 922 | + optional: true | |
| 923 | + '@prisma/client': | |
| 924 | + optional: true | |
| 925 | + '@tidbcloud/serverless': | |
| 926 | + optional: true | |
| 927 | + '@types/better-sqlite3': | |
| 928 | + optional: true | |
| 929 | + '@types/pg': | |
| 930 | + optional: true | |
| 931 | + '@types/sql.js': | |
| 932 | + optional: true | |
| 933 | + '@upstash/redis': | |
| 934 | + optional: true | |
| 935 | + '@vercel/postgres': | |
| 936 | + optional: true | |
| 937 | + '@xata.io/client': | |
| 938 | + optional: true | |
| 939 | + better-sqlite3: | |
| 940 | + optional: true | |
| 941 | + bun-types: | |
| 942 | + optional: true | |
| 943 | + expo-sqlite: | |
| 944 | + optional: true | |
| 945 | + gel: | |
| 946 | + optional: true | |
| 947 | + knex: | |
| 948 | + optional: true | |
| 949 | + kysely: | |
| 950 | + optional: true | |
| 951 | + mysql2: | |
| 952 | + optional: true | |
| 953 | + pg: | |
| 954 | + optional: true | |
| 955 | + postgres: | |
| 956 | + optional: true | |
| 957 | + prisma: | |
| 958 | + optional: true | |
| 959 | + sql.js: | |
| 960 | + optional: true | |
| 961 | + sqlite3: | |
| 962 | + optional: true | |
| 963 | + | |
| 964 | + es-module-lexer@1.7.0: | |
| 965 | + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} | |
| 966 | + | |
| 967 | + esbuild@0.18.20: | |
| 968 | + resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} | |
| 969 | + engines: {node: '>=12'} | |
| 970 | + hasBin: true | |
| 971 | + | |
| 972 | + esbuild@0.25.12: | |
| 973 | + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} | |
| 974 | + engines: {node: '>=18'} | |
| 975 | + hasBin: true | |
| 976 | + | |
| 977 | + esbuild@0.28.2: | |
| 978 | + resolution: {integrity: sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==} | |
| 979 | + engines: {node: '>=18'} | |
| 980 | + hasBin: true | |
| 981 | + | |
| 982 | + estree-walker@3.0.3: | |
| 983 | + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} | |
| 984 | + | |
| 985 | + expect-type@1.4.0: | |
| 986 | + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} | |
| 987 | + engines: {node: '>=12.0.0'} | |
| 988 | + | |
| 989 | + fast-xml-builder@1.3.1: | |
| 990 | + resolution: {integrity: sha512-pIM/1n3ntFXKYrUZwW7QCK0gAW7XY+wzj1YMIV3tLDvPj/V+zTGJK5e3/4WJfwj0qWw2ElNXiTixda/R+3YSug==} | |
| 991 | + | |
| 992 | + fast-xml-parser@5.11.1: | |
| 993 | + resolution: {integrity: sha512-TBw6K/fxoQGGjCmZDw9w/ZwP3uDcnTM4YH/g+PFRWr8sbe5idXtxNN6vITh4+1ruCZaho6uBFurElsA7F0zzgw==} | |
| 994 | + hasBin: true | |
| 995 | + | |
| 996 | + fdir@6.5.0: | |
| 997 | + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} | |
| 998 | + engines: {node: '>=12.0.0'} | |
| 999 | + peerDependencies: | |
| 1000 | + picomatch: ^3 || ^4 | |
| 1001 | + peerDependenciesMeta: | |
| 1002 | + picomatch: | |
| 1003 | + optional: true | |
| 1004 | + | |
| 1005 | + fsevents@2.3.3: | |
| 1006 | + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} | |
| 1007 | + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} | |
| 1008 | + os: [darwin] | |
| 1009 | + | |
| 1010 | + get-tsconfig@4.14.3: | |
| 1011 | + resolution: {integrity: sha512-++QEw4DIY7WGoukz+/+A/8dGYPT9l9yIadnmSgZ8Rjr3YVSVDipQSO9CdnJo9ePqFqUUqh+wk9uIaoiAwsiPkA==} | |
| 1012 | + | |
| 1013 | + is-unsafe@2.0.2: | |
| 1014 | + resolution: {integrity: sha512-HgbIHPBH0KHHCcjLfGsCvhtPTVxjaAZlXjwdz7/GQC40SjSe4sfQsar8J5VFo8JOSbarkpV0OLG95bbaNd9aAQ==} | |
| 1015 | + | |
| 1016 | + js-tokens@9.0.1: | |
| 1017 | + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} | |
| 1018 | + | |
| 1019 | + loupe@3.2.1: | |
| 1020 | + resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} | |
| 1021 | + | |
| 1022 | + magic-string@0.30.21: | |
| 1023 | + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} | |
| 1024 | + | |
| 1025 | + ms@2.1.3: | |
| 1026 | + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} | |
| 1027 | + | |
| 1028 | + nanoid@3.3.18: | |
| 1029 | + resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==} | |
| 1030 | + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} | |
| 1031 | + hasBin: true | |
| 1032 | + | |
| 1033 | + on-exit-leak-free@2.1.2: | |
| 1034 | + resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} | |
| 1035 | + engines: {node: '>=14.0.0'} | |
| 1036 | + | |
| 1037 | + path-expression-matcher@1.6.2: | |
| 1038 | + resolution: {integrity: sha512-enSlaiat05iasnzmgNxRj8reFdj3puY2QpNgP1aPIaVfT6nn9ICuPoFlKHk8EN22HcwewshO+mN2DGbkCEOtqQ==} | |
| 1039 | + engines: {node: '>=14.0.0'} | |
| 1040 | + | |
| 1041 | + pathe@2.0.3: | |
| 1042 | + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} | |
| 1043 | + | |
| 1044 | + pathval@2.0.1: | |
| 1045 | + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} | |
| 1046 | + engines: {node: '>= 14.16'} | |
| 1047 | + | |
| 1048 | + picocolors@1.1.1: | |
| 1049 | + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} | |
| 1050 | + | |
| 1051 | + picomatch@4.0.7: | |
| 1052 | + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} | |
| 1053 | + engines: {node: '>=12'} | |
| 1054 | + | |
| 1055 | + pino-abstract-transport@2.0.0: | |
| 1056 | + resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} | |
| 1057 | + | |
| 1058 | + pino-std-serializers@7.1.0: | |
| 1059 | + resolution: {integrity: sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==} | |
| 1060 | + | |
| 1061 | + pino@9.14.0: | |
| 1062 | + resolution: {integrity: sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==} | |
| 1063 | + hasBin: true | |
| 1064 | + | |
| 1065 | + postcss@8.5.28: | |
| 1066 | + resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==} | |
| 1067 | + engines: {node: ^10 || ^12 || >=14} | |
| 1068 | + | |
| 1069 | + postgres@3.4.9: | |
| 1070 | + resolution: {integrity: sha512-GD3qdB0x1z9xgFI6cdRD6xu2Sp2WCOEoe3mtnyB5Ee0XrrL5Pe+e4CCnJrRMnL1zYtRDZmQQVbvOttLnKDLnaw==} | |
| 1071 | + engines: {node: '>=12'} | |
| 1072 | + | |
| 1073 | + process-warning@5.1.0: | |
| 1074 | + resolution: {integrity: sha512-jQSaVHsPgtyw60e1rQ/A+/ArPEj/S8pS/vFnyGa/gYFXrKk/6RuDkoqVDQ5NI5MmS01698ltlAk0NoDBNLujRw==} | |
| 1075 | + | |
| 1076 | + quick-format-unescaped@4.0.4: | |
| 1077 | + resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} | |
| 1078 | + | |
| 1079 | + real-require@0.2.0: | |
| 1080 | + resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} | |
| 1081 | + engines: {node: '>= 12.13.0'} | |
| 1082 | + | |
| 1083 | + resolve-pkg-maps@1.0.0: | |
| 1084 | + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} | |
| 1085 | + | |
| 1086 | + rollup@4.63.1: | |
| 1087 | + resolution: {integrity: sha512-3Df9jsstwhccuEfmAMi9l8XUh/GOkVObmFTU7CCVBysEbcOZLl84jCtaAZMcPiMz2EGKsATzQcU+Xr3n/wU6cg==} | |
| 1088 | + engines: {node: '>=18.0.0', npm: '>=8.0.0'} | |
| 1089 | + hasBin: true | |
| 1090 | + | |
| 1091 | + safe-stable-stringify@2.5.0: | |
| 1092 | + resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} | |
| 1093 | + engines: {node: '>=10'} | |
| 1094 | + | |
| 1095 | + siginfo@2.0.0: | |
| 1096 | + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} | |
| 1097 | + | |
| 1098 | + sonic-boom@4.2.1: | |
| 1099 | + resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==} | |
| 1100 | + | |
| 1101 | + source-map-js@1.2.1: | |
| 1102 | + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} | |
| 1103 | + engines: {node: '>=0.10.0'} | |
| 1104 | + | |
| 1105 | + source-map-support@0.5.21: | |
| 1106 | + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} | |
| 1107 | + | |
| 1108 | + source-map@0.6.1: | |
| 1109 | + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} | |
| 1110 | + engines: {node: '>=0.10.0'} | |
| 1111 | + | |
| 1112 | + split2@4.2.0: | |
| 1113 | + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} | |
| 1114 | + engines: {node: '>= 10.x'} | |
| 1115 | + | |
| 1116 | + stackback@0.0.2: | |
| 1117 | + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} | |
| 1118 | + | |
| 1119 | + std-env@3.10.0: | |
| 1120 | + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} | |
| 1121 | + | |
| 1122 | + strip-literal@3.1.0: | |
| 1123 | + resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} | |
| 1124 | + | |
| 1125 | + strnum@2.4.2: | |
| 1126 | + resolution: {integrity: sha512-rDG3Ah4TV0k1hWvLSzkZtMmLN9+eS+h3knq4MP6A42Y3Yh5qGNnOUs1jJkoSr8FG5dsL28c7KgkIBzSEykqtuw==} | |
| 1127 | + | |
| 1128 | + thread-stream@3.2.0: | |
| 1129 | + resolution: {integrity: sha512-zLBvqpwr4Esa0kRjcrzGU6zL25lePWaCLMx0RQFrmteozIfeNdaMLpG5U7PeHzvlFkAWaRKA9/KVW4F60iB+qw==} | |
| 1130 | + | |
| 1131 | + tinybench@2.9.0: | |
| 1132 | + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} | |
| 1133 | + | |
| 1134 | + tinyexec@0.3.2: | |
| 1135 | + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} | |
| 1136 | + | |
| 1137 | + tinyglobby@0.2.17: | |
| 1138 | + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} | |
| 1139 | + engines: {node: '>=12.0.0'} | |
| 1140 | + | |
| 1141 | + tinypool@1.1.1: | |
| 1142 | + resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} | |
| 1143 | + engines: {node: ^18.0.0 || >=20.0.0} | |
| 1144 | + | |
| 1145 | + tinyrainbow@2.0.0: | |
| 1146 | + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} | |
| 1147 | + engines: {node: '>=14.0.0'} | |
| 1148 | + | |
| 1149 | + tinyspy@4.0.6: | |
| 1150 | + resolution: {integrity: sha512-u8KszXvGfU68hVcZpRHKG28T0krMuv2G5nDhiHaMLen/gIuFEgIJhaJuO69qjnXg5paSrbPMFfx3brNuN8eVSg==} | |
| 1151 | + engines: {node: '>=14.0.0'} | |
| 1152 | + | |
| 1153 | + tsx@4.23.13: | |
| 1154 | + resolution: {integrity: sha512-BL5MGkRln6aDYhb0xbQlEAGw743BaZYWdbWtdJOBriYJboKgUUYCadFp2/FpBBZquBC/ezNBn7wMMPx7FDZUDw==} | |
| 1155 | + engines: {node: '>=18.0.0'} | |
| 1156 | + hasBin: true | |
| 1157 | + | |
| 1158 | + typescript@5.9.3: | |
| 1159 | + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} | |
| 1160 | + engines: {node: '>=14.17'} | |
| 1161 | + hasBin: true | |
| 1162 | + | |
| 1163 | + undici-types@7.18.2: | |
| 1164 | + resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} | |
| 1165 | + | |
| 1166 | + vite-node@3.2.4: | |
| 1167 | + resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} | |
| 1168 | + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} | |
| 1169 | + hasBin: true | |
| 1170 | + | |
| 1171 | + vite@7.3.6: | |
| 1172 | + resolution: {integrity: sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==} | |
| 1173 | + engines: {node: ^20.19.0 || >=22.12.0} | |
| 1174 | + hasBin: true | |
| 1175 | + peerDependencies: | |
| 1176 | + '@types/node': ^20.19.0 || >=22.12.0 | |
| 1177 | + jiti: '>=1.21.0' | |
| 1178 | + less: ^4.0.0 | |
| 1179 | + lightningcss: ^1.21.0 | |
| 1180 | + sass: ^1.70.0 | |
| 1181 | + sass-embedded: ^1.70.0 | |
| 1182 | + stylus: '>=0.54.8' | |
| 1183 | + sugarss: ^5.0.0 | |
| 1184 | + terser: ^5.16.0 | |
| 1185 | + tsx: ^4.8.1 | |
| 1186 | + yaml: ^2.4.2 | |
| 1187 | + peerDependenciesMeta: | |
| 1188 | + '@types/node': | |
| 1189 | + optional: true | |
| 1190 | + jiti: | |
| 1191 | + optional: true | |
| 1192 | + less: | |
| 1193 | + optional: true | |
| 1194 | + lightningcss: | |
| 1195 | + optional: true | |
| 1196 | + sass: | |
| 1197 | + optional: true | |
| 1198 | + sass-embedded: | |
| 1199 | + optional: true | |
| 1200 | + stylus: | |
| 1201 | + optional: true | |
| 1202 | + sugarss: | |
| 1203 | + optional: true | |
| 1204 | + terser: | |
| 1205 | + optional: true | |
| 1206 | + tsx: | |
| 1207 | + optional: true | |
| 1208 | + yaml: | |
| 1209 | + optional: true | |
| 1210 | + | |
| 1211 | + vitest@3.2.7: | |
| 1212 | + resolution: {integrity: sha512-KrxIJ62Fd89gfysR4WotlgZABiz2dqFPgqGzX7s+CwsqLFomRH7777ZcrOD6+WVAh7khPQP41A+BKbpcJFrdEg==} | |
| 1213 | + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} | |
| 1214 | + hasBin: true | |
| 1215 | + peerDependencies: | |
| 1216 | + '@edge-runtime/vm': '*' | |
| 1217 | + '@types/debug': ^4.1.12 | |
| 1218 | + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 | |
| 1219 | + '@vitest/browser': 3.2.7 | |
| 1220 | + '@vitest/ui': 3.2.7 | |
| 1221 | + happy-dom: '*' | |
| 1222 | + jsdom: '*' | |
| 1223 | + peerDependenciesMeta: | |
| 1224 | + '@edge-runtime/vm': | |
| 1225 | + optional: true | |
| 1226 | + '@types/debug': | |
| 1227 | + optional: true | |
| 1228 | + '@types/node': | |
| 1229 | + optional: true | |
| 1230 | + '@vitest/browser': | |
| 1231 | + optional: true | |
| 1232 | + '@vitest/ui': | |
| 1233 | + optional: true | |
| 1234 | + happy-dom: | |
| 1235 | + optional: true | |
| 1236 | + jsdom: | |
| 1237 | + optional: true | |
| 1238 | + | |
| 1239 | + why-is-node-running@2.3.0: | |
| 1240 | + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} | |
| 1241 | + engines: {node: '>=8'} | |
| 1242 | + hasBin: true | |
| 1243 | + | |
| 1244 | + xml-naming@0.3.0: | |
| 1245 | + resolution: {integrity: sha512-ghig2TBE/H11aOVgmahA3MhimvkBr6JIYknH/Dhdk10nXwdbIqBJsbfMxpvFPG8bAw77gN29aQWvKpmVoPlvPQ==} | |
| 1246 | + engines: {node: '>=16.0.0'} | |
| 1247 | + | |
| 1248 | + yaml@2.9.0: | |
| 1249 | + resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} | |
| 1250 | + engines: {node: '>= 14.6'} | |
| 1251 | + hasBin: true | |
| 1252 | + | |
| 1253 | + zod@4.5.4: | |
| 1254 | + resolution: {integrity: sha512-sC95tT5iHHH9gtpj6A81kh+NEaRAUFN+qlUPDUbRfOMvNf5QCBqsb3WgvnpVtK5Y+4UfA6KqufotuTvMGiTlsA==} | |
| 1255 | + | |
| 1256 | +snapshots: | |
| 1257 | + | |
| 1258 | + '@drizzle-team/brocli@0.10.2': {} | |
| 1259 | + | |
| 1260 | + '@esbuild-kit/core-utils@3.3.2': | |
| 1261 | + dependencies: | |
| 1262 | + esbuild: 0.18.20 | |
| 1263 | + source-map-support: 0.5.21 | |
| 1264 | + | |
| 1265 | + '@esbuild-kit/esm-loader@2.6.5': | |
| 1266 | + dependencies: | |
| 1267 | + '@esbuild-kit/core-utils': 3.3.2 | |
| 1268 | + get-tsconfig: 4.14.3 | |
| 1269 | + | |
| 1270 | + '@esbuild/aix-ppc64@0.25.12': | |
| 1271 | + optional: true | |
| 1272 | + | |
| 1273 | + '@esbuild/aix-ppc64@0.28.2': | |
| 1274 | + optional: true | |
| 1275 | + | |
| 1276 | + '@esbuild/android-arm64@0.18.20': | |
| 1277 | + optional: true | |
| 1278 | + | |
| 1279 | + '@esbuild/android-arm64@0.25.12': | |
| 1280 | + optional: true | |
| 1281 | + | |
| 1282 | + '@esbuild/android-arm64@0.28.2': | |
| 1283 | + optional: true | |
| 1284 | + | |
| 1285 | + '@esbuild/android-arm@0.18.20': | |
| 1286 | + optional: true | |
| 1287 | + | |
| 1288 | + '@esbuild/android-arm@0.25.12': | |
| 1289 | + optional: true | |
| 1290 | + | |
| 1291 | + '@esbuild/android-arm@0.28.2': | |
| 1292 | + optional: true | |
| 1293 | + | |
| 1294 | + '@esbuild/android-x64@0.18.20': | |
| 1295 | + optional: true | |
| 1296 | + | |
| 1297 | + '@esbuild/android-x64@0.25.12': | |
| 1298 | + optional: true | |
| 1299 | + | |
| 1300 | + '@esbuild/android-x64@0.28.2': | |
| 1301 | + optional: true | |
| 1302 | + | |
| 1303 | + '@esbuild/darwin-arm64@0.18.20': | |
| 1304 | + optional: true | |
| 1305 | + | |
| 1306 | + '@esbuild/darwin-arm64@0.25.12': | |
| 1307 | + optional: true | |
| 1308 | + | |
| 1309 | + '@esbuild/darwin-arm64@0.28.2': | |
| 1310 | + optional: true | |
| 1311 | + | |
| 1312 | + '@esbuild/darwin-x64@0.18.20': | |
| 1313 | + optional: true | |
| 1314 | + | |
| 1315 | + '@esbuild/darwin-x64@0.25.12': | |
| 1316 | + optional: true | |
| 1317 | + | |
| 1318 | + '@esbuild/darwin-x64@0.28.2': | |
| 1319 | + optional: true | |
| 1320 | + | |
| 1321 | + '@esbuild/freebsd-arm64@0.18.20': | |
| 1322 | + optional: true | |
| 1323 | + | |
| 1324 | + '@esbuild/freebsd-arm64@0.25.12': | |
| 1325 | + optional: true | |
| 1326 | + | |
| 1327 | + '@esbuild/freebsd-arm64@0.28.2': | |
| 1328 | + optional: true | |
| 1329 | + | |
| 1330 | + '@esbuild/freebsd-x64@0.18.20': | |
| 1331 | + optional: true | |
| 1332 | + | |
| 1333 | + '@esbuild/freebsd-x64@0.25.12': | |
| 1334 | + optional: true | |
| 1335 | + | |
| 1336 | + '@esbuild/freebsd-x64@0.28.2': | |
| 1337 | + optional: true | |
| 1338 | + | |
| 1339 | + '@esbuild/linux-arm64@0.18.20': | |
| 1340 | + optional: true | |
| 1341 | + | |
| 1342 | + '@esbuild/linux-arm64@0.25.12': | |
| 1343 | + optional: true | |
| 1344 | + | |
| 1345 | + '@esbuild/linux-arm64@0.28.2': | |
| 1346 | + optional: true | |
| 1347 | + | |
| 1348 | + '@esbuild/linux-arm@0.18.20': | |
| 1349 | + optional: true | |
| 1350 | + | |
| 1351 | + '@esbuild/linux-arm@0.25.12': | |
| 1352 | + optional: true | |
| 1353 | + | |
| 1354 | + '@esbuild/linux-arm@0.28.2': | |
| 1355 | + optional: true | |
| 1356 | + | |
| 1357 | + '@esbuild/linux-ia32@0.18.20': | |
| 1358 | + optional: true | |
| 1359 | + | |
| 1360 | + '@esbuild/linux-ia32@0.25.12': | |
| 1361 | + optional: true | |
| 1362 | + | |
| 1363 | + '@esbuild/linux-ia32@0.28.2': | |
| 1364 | + optional: true | |
| 1365 | + | |
| 1366 | + '@esbuild/linux-loong64@0.18.20': | |
| 1367 | + optional: true | |
| 1368 | + | |
| 1369 | + '@esbuild/linux-loong64@0.25.12': | |
| 1370 | + optional: true | |
| 1371 | + | |
| 1372 | + '@esbuild/linux-loong64@0.28.2': | |
| 1373 | + optional: true | |
| 1374 | + | |
| 1375 | + '@esbuild/linux-mips64el@0.18.20': | |
| 1376 | + optional: true | |
| 1377 | + | |
| 1378 | + '@esbuild/linux-mips64el@0.25.12': | |
| 1379 | + optional: true | |
| 1380 | + | |
| 1381 | + '@esbuild/linux-mips64el@0.28.2': | |
| 1382 | + optional: true | |
| 1383 | + | |
| 1384 | + '@esbuild/linux-ppc64@0.18.20': | |
| 1385 | + optional: true | |
| 1386 | + | |
| 1387 | + '@esbuild/linux-ppc64@0.25.12': | |
| 1388 | + optional: true | |
| 1389 | + | |
| 1390 | + '@esbuild/linux-ppc64@0.28.2': | |
| 1391 | + optional: true | |
| 1392 | + | |
| 1393 | + '@esbuild/linux-riscv64@0.18.20': | |
| 1394 | + optional: true | |
| 1395 | + | |
| 1396 | + '@esbuild/linux-riscv64@0.25.12': | |
| 1397 | + optional: true | |
| 1398 | + | |
| 1399 | + '@esbuild/linux-riscv64@0.28.2': | |
| 1400 | + optional: true | |
| 1401 | + | |
| 1402 | + '@esbuild/linux-s390x@0.18.20': | |
| 1403 | + optional: true | |
| 1404 | + | |
| 1405 | + '@esbuild/linux-s390x@0.25.12': | |
| 1406 | + optional: true | |
| 1407 | + | |
| 1408 | + '@esbuild/linux-s390x@0.28.2': | |
| 1409 | + optional: true | |
| 1410 | + | |
| 1411 | + '@esbuild/linux-x64@0.18.20': | |
| 1412 | + optional: true | |
| 1413 | + | |
| 1414 | + '@esbuild/linux-x64@0.25.12': | |
| 1415 | + optional: true | |
| 1416 | + | |
| 1417 | + '@esbuild/linux-x64@0.28.2': | |
| 1418 | + optional: true | |
| 1419 | + | |
| 1420 | + '@esbuild/netbsd-arm64@0.25.12': | |
| 1421 | + optional: true | |
| 1422 | + | |
| 1423 | + '@esbuild/netbsd-arm64@0.28.2': | |
| 1424 | + optional: true | |
| 1425 | + | |
| 1426 | + '@esbuild/netbsd-x64@0.18.20': | |
| 1427 | + optional: true | |
| 1428 | + | |
| 1429 | + '@esbuild/netbsd-x64@0.25.12': | |
| 1430 | + optional: true | |
| 1431 | + | |
| 1432 | + '@esbuild/netbsd-x64@0.28.2': | |
| 1433 | + optional: true | |
| 1434 | + | |
| 1435 | + '@esbuild/openbsd-arm64@0.25.12': | |
| 1436 | + optional: true | |
| 1437 | + | |
| 1438 | + '@esbuild/openbsd-arm64@0.28.2': | |
| 1439 | + optional: true | |
| 1440 | + | |
| 1441 | + '@esbuild/openbsd-x64@0.18.20': | |
| 1442 | + optional: true | |
| 1443 | + | |
| 1444 | + '@esbuild/openbsd-x64@0.25.12': | |
| 1445 | + optional: true | |
| 1446 | + | |
| 1447 | + '@esbuild/openbsd-x64@0.28.2': | |
| 1448 | + optional: true | |
| 1449 | + | |
| 1450 | + '@esbuild/openharmony-arm64@0.25.12': | |
| 1451 | + optional: true | |
| 1452 | + | |
| 1453 | + '@esbuild/openharmony-arm64@0.28.2': | |
| 1454 | + optional: true | |
| 1455 | + | |
| 1456 | + '@esbuild/sunos-x64@0.18.20': | |
| 1457 | + optional: true | |
| 1458 | + | |
| 1459 | + '@esbuild/sunos-x64@0.25.12': | |
| 1460 | + optional: true | |
| 1461 | + | |
| 1462 | + '@esbuild/sunos-x64@0.28.2': | |
| 1463 | + optional: true | |
| 1464 | + | |
| 1465 | + '@esbuild/win32-arm64@0.18.20': | |
| 1466 | + optional: true | |
| 1467 | + | |
| 1468 | + '@esbuild/win32-arm64@0.25.12': | |
| 1469 | + optional: true | |
| 1470 | + | |
| 1471 | + '@esbuild/win32-arm64@0.28.2': | |
| 1472 | + optional: true | |
| 1473 | + | |
| 1474 | + '@esbuild/win32-ia32@0.18.20': | |
| 1475 | + optional: true | |
| 1476 | + | |
| 1477 | + '@esbuild/win32-ia32@0.25.12': | |
| 1478 | + optional: true | |
| 1479 | + | |
| 1480 | + '@esbuild/win32-ia32@0.28.2': | |
| 1481 | + optional: true | |
| 1482 | + | |
| 1483 | + '@esbuild/win32-x64@0.18.20': | |
| 1484 | + optional: true | |
| 1485 | + | |
| 1486 | + '@esbuild/win32-x64@0.25.12': | |
| 1487 | + optional: true | |
| 1488 | + | |
| 1489 | + '@esbuild/win32-x64@0.28.2': | |
| 1490 | + optional: true | |
| 1491 | + | |
| 1492 | + '@jridgewell/sourcemap-codec@1.6.0': {} | |
| 1493 | + | |
| 1494 | + '@napi-rs/lzma-linux-x64-gnu@1.5.1': | |
| 1495 | + optional: true | |
| 1496 | + | |
| 1497 | + '@nodable/entities@3.0.0': {} | |
| 1498 | + | |
| 1499 | + '@pinojs/redact@0.4.0': {} | |
| 1500 | + | |
| 1501 | + '@rollup/rollup-android-arm-eabi@4.63.1': | |
| 1502 | + optional: true | |
| 1503 | + | |
| 1504 | + '@rollup/rollup-android-arm64@4.63.1': | |
| 1505 | + optional: true | |
| 1506 | + | |
| 1507 | + '@rollup/rollup-darwin-arm64@4.63.1': | |
| 1508 | + optional: true | |
| 1509 | + | |
| 1510 | + '@rollup/rollup-darwin-x64@4.63.1': | |
| 1511 | + optional: true | |
| 1512 | + | |
| 1513 | + '@rollup/rollup-freebsd-arm64@4.63.1': | |
| 1514 | + optional: true | |
| 1515 | + | |
| 1516 | + '@rollup/rollup-freebsd-x64@4.63.1': | |
| 1517 | + optional: true | |
| 1518 | + | |
| 1519 | + '@rollup/rollup-linux-arm-gnueabihf@4.63.1': | |
| 1520 | + optional: true | |
| 1521 | + | |
| 1522 | + '@rollup/rollup-linux-arm-musleabihf@4.63.1': | |
| 1523 | + optional: true | |
| 1524 | + | |
| 1525 | + '@rollup/rollup-linux-arm64-gnu@4.63.1': | |
| 1526 | + optional: true | |
| 1527 | + | |
| 1528 | + '@rollup/rollup-linux-arm64-musl@4.63.1': | |
| 1529 | + optional: true | |
| 1530 | + | |
| 1531 | + '@rollup/rollup-linux-loong64-gnu@4.63.1': | |
| 1532 | + optional: true | |
| 1533 | + | |
| 1534 | + '@rollup/rollup-linux-loong64-musl@4.63.1': | |
| 1535 | + optional: true | |
| 1536 | + | |
| 1537 | + '@rollup/rollup-linux-ppc64-gnu@4.63.1': | |
| 1538 | + optional: true | |
| 1539 | + | |
| 1540 | + '@rollup/rollup-linux-ppc64-musl@4.63.1': | |
| 1541 | + optional: true | |
| 1542 | + | |
| 1543 | + '@rollup/rollup-linux-riscv64-gnu@4.63.1': | |
| 1544 | + optional: true | |
| 1545 | + | |
| 1546 | + '@rollup/rollup-linux-riscv64-musl@4.63.1': | |
| 1547 | + optional: true | |
| 1548 | + | |
| 1549 | + '@rollup/rollup-linux-s390x-gnu@4.63.1': | |
| 1550 | + optional: true | |
| 1551 | + | |
| 1552 | + '@rollup/rollup-linux-x64-gnu@4.63.1': | |
| 1553 | + optional: true | |
| 1554 | + | |
| 1555 | + '@rollup/rollup-linux-x64-musl@4.63.1': | |
| 1556 | + optional: true | |
| 1557 | + | |
| 1558 | + '@rollup/rollup-openbsd-x64@4.63.1': | |
| 1559 | + optional: true | |
| 1560 | + | |
| 1561 | + '@rollup/rollup-openharmony-arm64@4.63.1': | |
| 1562 | + optional: true | |
| 1563 | + | |
| 1564 | + '@rollup/rollup-win32-arm64-msvc@4.63.1': | |
| 1565 | + optional: true | |
| 1566 | + | |
| 1567 | + '@rollup/rollup-win32-ia32-msvc@4.63.1': | |
| 1568 | + optional: true | |
| 1569 | + | |
| 1570 | + '@rollup/rollup-win32-x64-gnu@4.63.1': | |
| 1571 | + optional: true | |
| 1572 | + | |
| 1573 | + '@rollup/rollup-win32-x64-msvc@4.63.1': | |
| 1574 | + optional: true | |
| 1575 | + | |
| 1576 | + '@types/chai@5.2.3': | |
| 1577 | + dependencies: | |
| 1578 | + '@types/deep-eql': 4.0.2 | |
| 1579 | + assertion-error: 2.0.1 | |
| 1580 | + | |
| 1581 | + '@types/deep-eql@4.0.2': {} | |
| 1582 | + | |
| 1583 | + '@types/estree@1.0.9': {} | |
| 1584 | + | |
| 1585 | + '@types/node@24.13.3': | |
| 1586 | + dependencies: | |
| 1587 | + undici-types: 7.18.2 | |
| 1588 | + | |
| 1589 | + '@vitest/expect@3.2.7': | |
| 1590 | + dependencies: | |
| 1591 | + '@types/chai': 5.2.3 | |
| 1592 | + '@vitest/spy': 3.2.7 | |
| 1593 | + '@vitest/utils': 3.2.7 | |
| 1594 | + chai: 5.3.3 | |
| 1595 | + tinyrainbow: 2.0.0 | |
| 1596 | + | |
| 1597 | + '@vitest/mocker@3.2.7(vite@7.3.6(@types/node@24.13.3)(tsx@4.23.13)(yaml@2.9.0))': | |
| 1598 | + dependencies: | |
| 1599 | + '@vitest/spy': 3.2.7 | |
| 1600 | + estree-walker: 3.0.3 | |
| 1601 | + magic-string: 0.30.21 | |
| 1602 | + optionalDependencies: | |
| 1603 | + vite: 7.3.6(@types/node@24.13.3)(tsx@4.23.13)(yaml@2.9.0) | |
| 1604 | + | |
| 1605 | + '@vitest/pretty-format@3.2.7': | |
| 1606 | + dependencies: | |
| 1607 | + tinyrainbow: 2.0.0 | |
| 1608 | + | |
| 1609 | + '@vitest/runner@3.2.7': | |
| 1610 | + dependencies: | |
| 1611 | + '@vitest/utils': 3.2.7 | |
| 1612 | + pathe: 2.0.3 | |
| 1613 | + strip-literal: 3.1.0 | |
| 1614 | + | |
| 1615 | + '@vitest/snapshot@3.2.7': | |
| 1616 | + dependencies: | |
| 1617 | + '@vitest/pretty-format': 3.2.7 | |
| 1618 | + magic-string: 0.30.21 | |
| 1619 | + pathe: 2.0.3 | |
| 1620 | + | |
| 1621 | + '@vitest/spy@3.2.7': | |
| 1622 | + dependencies: | |
| 1623 | + tinyspy: 4.0.6 | |
| 1624 | + | |
| 1625 | + '@vitest/utils@3.2.7': | |
| 1626 | + dependencies: | |
| 1627 | + '@vitest/pretty-format': 3.2.7 | |
| 1628 | + loupe: 3.2.1 | |
| 1629 | + tinyrainbow: 2.0.0 | |
| 1630 | + | |
| 1631 | + anynum@1.0.1: {} | |
| 1632 | + | |
| 1633 | + assertion-error@2.0.1: {} | |
| 1634 | + | |
| 1635 | + atomic-sleep@1.0.0: {} | |
| 1636 | + | |
| 1637 | + buffer-from@1.1.2: {} | |
| 1638 | + | |
| 1639 | + cac@6.7.14: {} | |
| 1640 | + | |
| 1641 | + chai@5.3.3: | |
| 1642 | + dependencies: | |
| 1643 | + assertion-error: 2.0.1 | |
| 1644 | + check-error: 2.1.3 | |
| 1645 | + deep-eql: 5.0.2 | |
| 1646 | + loupe: 3.2.1 | |
| 1647 | + pathval: 2.0.1 | |
| 1648 | + | |
| 1649 | + check-error@2.1.3: {} | |
| 1650 | + | |
| 1651 | + debug@4.4.3: | |
| 1652 | + dependencies: | |
| 1653 | + ms: 2.1.3 | |
| 1654 | + | |
| 1655 | + deep-eql@5.0.2: {} | |
| 1656 | + | |
| 1657 | + drizzle-kit@0.31.10: | |
| 1658 | + dependencies: | |
| 1659 | + '@drizzle-team/brocli': 0.10.2 | |
| 1660 | + '@esbuild-kit/esm-loader': 2.6.5 | |
| 1661 | + esbuild: 0.25.12 | |
| 1662 | + tsx: 4.23.13 | |
| 1663 | + | |
| 1664 | + drizzle-orm@0.45.2(postgres@3.4.9): | |
| 1665 | + optionalDependencies: | |
| 1666 | + postgres: 3.4.9 | |
| 1667 | + | |
| 1668 | + es-module-lexer@1.7.0: {} | |
| 1669 | + | |
| 1670 | + esbuild@0.18.20: | |
| 1671 | + optionalDependencies: | |
| 1672 | + '@esbuild/android-arm': 0.18.20 | |
| 1673 | + '@esbuild/android-arm64': 0.18.20 | |
| 1674 | + '@esbuild/android-x64': 0.18.20 | |
| 1675 | + '@esbuild/darwin-arm64': 0.18.20 | |
| 1676 | + '@esbuild/darwin-x64': 0.18.20 | |
| 1677 | + '@esbuild/freebsd-arm64': 0.18.20 | |
| 1678 | + '@esbuild/freebsd-x64': 0.18.20 | |
| 1679 | + '@esbuild/linux-arm': 0.18.20 | |
| 1680 | + '@esbuild/linux-arm64': 0.18.20 | |
| 1681 | + '@esbuild/linux-ia32': 0.18.20 | |
| 1682 | + '@esbuild/linux-loong64': 0.18.20 | |
| 1683 | + '@esbuild/linux-mips64el': 0.18.20 | |
| 1684 | + '@esbuild/linux-ppc64': 0.18.20 | |
| 1685 | + '@esbuild/linux-riscv64': 0.18.20 | |
| 1686 | + '@esbuild/linux-s390x': 0.18.20 | |
| 1687 | + '@esbuild/linux-x64': 0.18.20 | |
| 1688 | + '@esbuild/netbsd-x64': 0.18.20 | |
| 1689 | + '@esbuild/openbsd-x64': 0.18.20 | |
| 1690 | + '@esbuild/sunos-x64': 0.18.20 | |
| 1691 | + '@esbuild/win32-arm64': 0.18.20 | |
| 1692 | + '@esbuild/win32-ia32': 0.18.20 | |
| 1693 | + '@esbuild/win32-x64': 0.18.20 | |
| 1694 | + | |
| 1695 | + esbuild@0.25.12: | |
| 1696 | + optionalDependencies: | |
| 1697 | + '@esbuild/aix-ppc64': 0.25.12 | |
| 1698 | + '@esbuild/android-arm': 0.25.12 | |
| 1699 | + '@esbuild/android-arm64': 0.25.12 | |
| 1700 | + '@esbuild/android-x64': 0.25.12 | |
| 1701 | + '@esbuild/darwin-arm64': 0.25.12 | |
| 1702 | + '@esbuild/darwin-x64': 0.25.12 | |
| 1703 | + '@esbuild/freebsd-arm64': 0.25.12 | |
| 1704 | + '@esbuild/freebsd-x64': 0.25.12 | |
| 1705 | + '@esbuild/linux-arm': 0.25.12 | |
| 1706 | + '@esbuild/linux-arm64': 0.25.12 | |
| 1707 | + '@esbuild/linux-ia32': 0.25.12 | |
| 1708 | + '@esbuild/linux-loong64': 0.25.12 | |
| 1709 | + '@esbuild/linux-mips64el': 0.25.12 | |
| 1710 | + '@esbuild/linux-ppc64': 0.25.12 | |
| 1711 | + '@esbuild/linux-riscv64': 0.25.12 | |
| 1712 | + '@esbuild/linux-s390x': 0.25.12 | |
| 1713 | + '@esbuild/linux-x64': 0.25.12 | |
| 1714 | + '@esbuild/netbsd-arm64': 0.25.12 | |
| 1715 | + '@esbuild/netbsd-x64': 0.25.12 | |
| 1716 | + '@esbuild/openbsd-arm64': 0.25.12 | |
| 1717 | + '@esbuild/openbsd-x64': 0.25.12 | |
| 1718 | + '@esbuild/openharmony-arm64': 0.25.12 | |
| 1719 | + '@esbuild/sunos-x64': 0.25.12 | |
| 1720 | + '@esbuild/win32-arm64': 0.25.12 | |
| 1721 | + '@esbuild/win32-ia32': 0.25.12 | |
| 1722 | + '@esbuild/win32-x64': 0.25.12 | |
| 1723 | + | |
| 1724 | + esbuild@0.28.2: | |
| 1725 | + optionalDependencies: | |
| 1726 | + '@esbuild/aix-ppc64': 0.28.2 | |
| 1727 | + '@esbuild/android-arm': 0.28.2 | |
| 1728 | + '@esbuild/android-arm64': 0.28.2 | |
| 1729 | + '@esbuild/android-x64': 0.28.2 | |
| 1730 | + '@esbuild/darwin-arm64': 0.28.2 | |
| 1731 | + '@esbuild/darwin-x64': 0.28.2 | |
| 1732 | + '@esbuild/freebsd-arm64': 0.28.2 | |
| 1733 | + '@esbuild/freebsd-x64': 0.28.2 | |
| 1734 | + '@esbuild/linux-arm': 0.28.2 | |
| 1735 | + '@esbuild/linux-arm64': 0.28.2 | |
| 1736 | + '@esbuild/linux-ia32': 0.28.2 | |
| 1737 | + '@esbuild/linux-loong64': 0.28.2 | |
| 1738 | + '@esbuild/linux-mips64el': 0.28.2 | |
| 1739 | + '@esbuild/linux-ppc64': 0.28.2 | |
| 1740 | + '@esbuild/linux-riscv64': 0.28.2 | |
| 1741 | + '@esbuild/linux-s390x': 0.28.2 | |
| 1742 | + '@esbuild/linux-x64': 0.28.2 | |
| 1743 | + '@esbuild/netbsd-arm64': 0.28.2 | |
| 1744 | + '@esbuild/netbsd-x64': 0.28.2 | |
| 1745 | + '@esbuild/openbsd-arm64': 0.28.2 | |
| 1746 | + '@esbuild/openbsd-x64': 0.28.2 | |
| 1747 | + '@esbuild/openharmony-arm64': 0.28.2 | |
| 1748 | + '@esbuild/sunos-x64': 0.28.2 | |
| 1749 | + '@esbuild/win32-arm64': 0.28.2 | |
| 1750 | + '@esbuild/win32-ia32': 0.28.2 | |
| 1751 | + '@esbuild/win32-x64': 0.28.2 | |
| 1752 | + | |
| 1753 | + estree-walker@3.0.3: | |
| 1754 | + dependencies: | |
| 1755 | + '@types/estree': 1.0.9 | |
| 1756 | + | |
| 1757 | + expect-type@1.4.0: {} | |
| 1758 | + | |
| 1759 | + fast-xml-builder@1.3.1: | |
| 1760 | + dependencies: | |
| 1761 | + path-expression-matcher: 1.6.2 | |
| 1762 | + xml-naming: 0.3.0 | |
| 1763 | + | |
| 1764 | + fast-xml-parser@5.11.1: | |
| 1765 | + dependencies: | |
| 1766 | + '@nodable/entities': 3.0.0 | |
| 1767 | + fast-xml-builder: 1.3.1 | |
| 1768 | + is-unsafe: 2.0.2 | |
| 1769 | + path-expression-matcher: 1.6.2 | |
| 1770 | + strnum: 2.4.2 | |
| 1771 | + xml-naming: 0.3.0 | |
| 1772 | + | |
| 1773 | + fdir@6.5.0(picomatch@4.0.7): | |
| 1774 | + optionalDependencies: | |
| 1775 | + picomatch: 4.0.7 | |
| 1776 | + | |
| 1777 | + fsevents@2.3.3: | |
| 1778 | + optional: true | |
| 1779 | + | |
| 1780 | + get-tsconfig@4.14.3: | |
| 1781 | + dependencies: | |
| 1782 | + resolve-pkg-maps: 1.0.0 | |
| 1783 | + | |
| 1784 | + is-unsafe@2.0.2: {} | |
| 1785 | + | |
| 1786 | + js-tokens@9.0.1: {} | |
| 1787 | + | |
| 1788 | + loupe@3.2.1: {} | |
| 1789 | + | |
| 1790 | + magic-string@0.30.21: | |
| 1791 | + dependencies: | |
| 1792 | + '@jridgewell/sourcemap-codec': 1.6.0 | |
| 1793 | + | |
| 1794 | + ms@2.1.3: {} | |
| 1795 | + | |
| 1796 | + nanoid@3.3.18: {} | |
| 1797 | + | |
| 1798 | + on-exit-leak-free@2.1.2: {} | |
| 1799 | + | |
| 1800 | + path-expression-matcher@1.6.2: {} | |
| 1801 | + | |
| 1802 | + pathe@2.0.3: {} | |
| 1803 | + | |
| 1804 | + pathval@2.0.1: {} | |
| 1805 | + | |
| 1806 | + picocolors@1.1.1: {} | |
| 1807 | + | |
| 1808 | + picomatch@4.0.7: {} | |
| 1809 | + | |
| 1810 | + pino-abstract-transport@2.0.0: | |
| 1811 | + dependencies: | |
| 1812 | + split2: 4.2.0 | |
| 1813 | + | |
| 1814 | + pino-std-serializers@7.1.0: {} | |
| 1815 | + | |
| 1816 | + pino@9.14.0: | |
| 1817 | + dependencies: | |
| 1818 | + '@pinojs/redact': 0.4.0 | |
| 1819 | + atomic-sleep: 1.0.0 | |
| 1820 | + on-exit-leak-free: 2.1.2 | |
| 1821 | + pino-abstract-transport: 2.0.0 | |
| 1822 | + pino-std-serializers: 7.1.0 | |
| 1823 | + process-warning: 5.1.0 | |
| 1824 | + quick-format-unescaped: 4.0.4 | |
| 1825 | + real-require: 0.2.0 | |
| 1826 | + safe-stable-stringify: 2.5.0 | |
| 1827 | + sonic-boom: 4.2.1 | |
| 1828 | + thread-stream: 3.2.0 | |
| 1829 | + | |
| 1830 | + postcss@8.5.28: | |
| 1831 | + dependencies: | |
| 1832 | + nanoid: 3.3.18 | |
| 1833 | + picocolors: 1.1.1 | |
| 1834 | + source-map-js: 1.2.1 | |
| 1835 | + | |
| 1836 | + postgres@3.4.9: {} | |
| 1837 | + | |
| 1838 | + process-warning@5.1.0: {} | |
| 1839 | + | |
| 1840 | + quick-format-unescaped@4.0.4: {} | |
| 1841 | + | |
| 1842 | + real-require@0.2.0: {} | |
| 1843 | + | |
| 1844 | + resolve-pkg-maps@1.0.0: {} | |
| 1845 | + | |
| 1846 | + rollup@4.63.1: | |
| 1847 | + dependencies: | |
| 1848 | + '@types/estree': 1.0.9 | |
| 1849 | + optionalDependencies: | |
| 1850 | + '@napi-rs/lzma-linux-x64-gnu': 1.5.1 | |
| 1851 | + '@rollup/rollup-android-arm-eabi': 4.63.1 | |
| 1852 | + '@rollup/rollup-android-arm64': 4.63.1 | |
| 1853 | + '@rollup/rollup-darwin-arm64': 4.63.1 | |
| 1854 | + '@rollup/rollup-darwin-x64': 4.63.1 | |
| 1855 | + '@rollup/rollup-freebsd-arm64': 4.63.1 | |
| 1856 | + '@rollup/rollup-freebsd-x64': 4.63.1 | |
| 1857 | + '@rollup/rollup-linux-arm-gnueabihf': 4.63.1 | |
| 1858 | + '@rollup/rollup-linux-arm-musleabihf': 4.63.1 | |
| 1859 | + '@rollup/rollup-linux-arm64-gnu': 4.63.1 | |
| 1860 | + '@rollup/rollup-linux-arm64-musl': 4.63.1 | |
| 1861 | + '@rollup/rollup-linux-loong64-gnu': 4.63.1 | |
| 1862 | + '@rollup/rollup-linux-loong64-musl': 4.63.1 | |
| 1863 | + '@rollup/rollup-linux-ppc64-gnu': 4.63.1 | |
| 1864 | + '@rollup/rollup-linux-ppc64-musl': 4.63.1 | |
| 1865 | + '@rollup/rollup-linux-riscv64-gnu': 4.63.1 | |
| 1866 | + '@rollup/rollup-linux-riscv64-musl': 4.63.1 | |
| 1867 | + '@rollup/rollup-linux-s390x-gnu': 4.63.1 | |
| 1868 | + '@rollup/rollup-linux-x64-gnu': 4.63.1 | |
| 1869 | + '@rollup/rollup-linux-x64-musl': 4.63.1 | |
| 1870 | + '@rollup/rollup-openbsd-x64': 4.63.1 | |
| 1871 | + '@rollup/rollup-openharmony-arm64': 4.63.1 | |
| 1872 | + '@rollup/rollup-win32-arm64-msvc': 4.63.1 | |
| 1873 | + '@rollup/rollup-win32-ia32-msvc': 4.63.1 | |
| 1874 | + '@rollup/rollup-win32-x64-gnu': 4.63.1 | |
| 1875 | + '@rollup/rollup-win32-x64-msvc': 4.63.1 | |
| 1876 | + fsevents: 2.3.3 | |
| 1877 | + | |
| 1878 | + safe-stable-stringify@2.5.0: {} | |
| 1879 | + | |
| 1880 | + siginfo@2.0.0: {} | |
| 1881 | + | |
| 1882 | + sonic-boom@4.2.1: | |
| 1883 | + dependencies: | |
| 1884 | + atomic-sleep: 1.0.0 | |
| 1885 | + | |
| 1886 | + source-map-js@1.2.1: {} | |
| 1887 | + | |
| 1888 | + source-map-support@0.5.21: | |
| 1889 | + dependencies: | |
| 1890 | + buffer-from: 1.1.2 | |
| 1891 | + source-map: 0.6.1 | |
| 1892 | + | |
| 1893 | + source-map@0.6.1: {} | |
| 1894 | + | |
| 1895 | + split2@4.2.0: {} | |
| 1896 | + | |
| 1897 | + stackback@0.0.2: {} | |
| 1898 | + | |
| 1899 | + std-env@3.10.0: {} | |
| 1900 | + | |
| 1901 | + strip-literal@3.1.0: | |
| 1902 | + dependencies: | |
| 1903 | + js-tokens: 9.0.1 | |
| 1904 | + | |
| 1905 | + strnum@2.4.2: | |
| 1906 | + dependencies: | |
| 1907 | + anynum: 1.0.1 | |
| 1908 | + | |
| 1909 | + thread-stream@3.2.0: | |
| 1910 | + dependencies: | |
| 1911 | + real-require: 0.2.0 | |
| 1912 | + | |
| 1913 | + tinybench@2.9.0: {} | |
| 1914 | + | |
| 1915 | + tinyexec@0.3.2: {} | |
| 1916 | + | |
| 1917 | + tinyglobby@0.2.17: | |
| 1918 | + dependencies: | |
| 1919 | + fdir: 6.5.0(picomatch@4.0.7) | |
| 1920 | + picomatch: 4.0.7 | |
| 1921 | + | |
| 1922 | + tinypool@1.1.1: {} | |
| 1923 | + | |
| 1924 | + tinyrainbow@2.0.0: {} | |
| 1925 | + | |
| 1926 | + tinyspy@4.0.6: {} | |
| 1927 | + | |
| 1928 | + tsx@4.23.13: | |
| 1929 | + dependencies: | |
| 1930 | + esbuild: 0.28.2 | |
| 1931 | + optionalDependencies: | |
| 1932 | + fsevents: 2.3.3 | |
| 1933 | + | |
| 1934 | + typescript@5.9.3: {} | |
| 1935 | + | |
| 1936 | + undici-types@7.18.2: {} | |
| 1937 | + | |
| 1938 | + vite-node@3.2.4(@types/node@24.13.3)(tsx@4.23.13)(yaml@2.9.0): | |
| 1939 | + dependencies: | |
| 1940 | + cac: 6.7.14 | |
| 1941 | + debug: 4.4.3 | |
| 1942 | + es-module-lexer: 1.7.0 | |
| 1943 | + pathe: 2.0.3 | |
| 1944 | + vite: 7.3.6(@types/node@24.13.3)(tsx@4.23.13)(yaml@2.9.0) | |
| 1945 | + transitivePeerDependencies: | |
| 1946 | + - '@types/node' | |
| 1947 | + - jiti | |
| 1948 | + - less | |
| 1949 | + - lightningcss | |
| 1950 | + - sass | |
| 1951 | + - sass-embedded | |
| 1952 | + - stylus | |
| 1953 | + - sugarss | |
| 1954 | + - supports-color | |
| 1955 | + - terser | |
| 1956 | + - tsx | |
| 1957 | + - yaml | |
| 1958 | + | |
| 1959 | + vite@7.3.6(@types/node@24.13.3)(tsx@4.23.13)(yaml@2.9.0): | |
| 1960 | + dependencies: | |
| 1961 | + esbuild: 0.28.2 | |
| 1962 | + fdir: 6.5.0(picomatch@4.0.7) | |
| 1963 | + picomatch: 4.0.7 | |
| 1964 | + postcss: 8.5.28 | |
| 1965 | + rollup: 4.63.1 | |
| 1966 | + tinyglobby: 0.2.17 | |
| 1967 | + optionalDependencies: | |
| 1968 | + '@types/node': 24.13.3 | |
| 1969 | + fsevents: 2.3.3 | |
| 1970 | + tsx: 4.23.13 | |
| 1971 | + yaml: 2.9.0 | |
| 1972 | + | |
| 1973 | + vitest@3.2.7(@types/node@24.13.3)(tsx@4.23.13)(yaml@2.9.0): | |
| 1974 | + dependencies: | |
| 1975 | + '@types/chai': 5.2.3 | |
| 1976 | + '@vitest/expect': 3.2.7 | |
| 1977 | + '@vitest/mocker': 3.2.7(vite@7.3.6(@types/node@24.13.3)(tsx@4.23.13)(yaml@2.9.0)) | |
| 1978 | + '@vitest/pretty-format': 3.2.7 | |
| 1979 | + '@vitest/runner': 3.2.7 | |
| 1980 | + '@vitest/snapshot': 3.2.7 | |
| 1981 | + '@vitest/spy': 3.2.7 | |
| 1982 | + '@vitest/utils': 3.2.7 | |
| 1983 | + chai: 5.3.3 | |
| 1984 | + debug: 4.4.3 | |
| 1985 | + expect-type: 1.4.0 | |
| 1986 | + magic-string: 0.30.21 | |
| 1987 | + pathe: 2.0.3 | |
| 1988 | + picomatch: 4.0.7 | |
| 1989 | + std-env: 3.10.0 | |
| 1990 | + tinybench: 2.9.0 | |
| 1991 | + tinyexec: 0.3.2 | |
| 1992 | + tinyglobby: 0.2.17 | |
| 1993 | + tinypool: 1.1.1 | |
| 1994 | + tinyrainbow: 2.0.0 | |
| 1995 | + vite: 7.3.6(@types/node@24.13.3)(tsx@4.23.13)(yaml@2.9.0) | |
| 1996 | + vite-node: 3.2.4(@types/node@24.13.3)(tsx@4.23.13)(yaml@2.9.0) | |
| 1997 | + why-is-node-running: 2.3.0 | |
| 1998 | + optionalDependencies: | |
| 1999 | + '@types/node': 24.13.3 | |
| 2000 | + transitivePeerDependencies: | |
| 2001 | + - jiti | |
| 2002 | + - less | |
| 2003 | + - lightningcss | |
| 2004 | + - msw | |
| 2005 | + - sass | |
| 2006 | + - sass-embedded | |
| 2007 | + - stylus | |
| 2008 | + - sugarss | |
| 2009 | + - supports-color | |
| 2010 | + - terser | |
| 2011 | + - tsx | |
| 2012 | + - yaml | |
| 2013 | + | |
| 2014 | + why-is-node-running@2.3.0: | |
| 2015 | + dependencies: | |
| 2016 | + siginfo: 2.0.0 | |
| 2017 | + stackback: 0.0.2 | |
| 2018 | + | |
| 2019 | + xml-naming@0.3.0: {} | |
| 2020 | + | |
| 2021 | + yaml@2.9.0: {} | |
| 2022 | + | |
| 2023 | + zod@4.5.4: {} | |
added
pnpm-workspace.yaml
+4 −0
@@ -0,0 +1,4 @@ | ||
| 1 | +packages: | |
| 2 | + - apps/* | |
| 3 | + - packages/* | |
| 4 | + - workers | |
added
scripts/ci.ts
+136 −0
@@ -0,0 +1,136 @@ | ||
| 1 | +#!/usr/bin/env tsx | |
| 2 | +/** | |
| 3 | + * CancerIndex operator CLI. | |
| 4 | + * | |
| 5 | + * pnpm cix connectors list connectors + health | |
| 6 | + * pnpm cix run <id> [--mode full|incremental|dry_run] [--max-records N] [--max-minutes M] [--reset-cursor] | |
| 7 | + * pnpm cix run-all [--max-minutes M] run every active connector in registry order | |
| 8 | + * pnpm cix health <id> source liveness probe | |
| 9 | + * pnpm cix sources:sync manifests → sources table | |
| 10 | + * pnpm cix counters recompute entity counters | |
| 11 | + * pnpm cix rank [--scope …] recompute ranking snapshots | |
| 12 | + * pnpm cix stats table counts | |
| 13 | + * pnpm cix trace <table> <id> lineage of a value (rankings/epidemiology/frequency) | |
| 14 | + */ | |
| 15 | +import { existsSync } from 'node:fs'; | |
| 16 | +import path from 'node:path'; | |
| 17 | +import { sql } from 'drizzle-orm'; | |
| 18 | + | |
| 19 | +for (const candidate of [path.resolve(process.cwd(), '.env')]) { | |
| 20 | + if (existsSync(candidate)) { | |
| 21 | + try { | |
| 22 | + process.loadEnvFile(candidate); | |
| 23 | + } catch { | |
| 24 | + /* ignore */ | |
| 25 | + } | |
| 26 | + } | |
| 27 | +} | |
| 28 | + | |
| 29 | +const [, , cmd, ...rest] = process.argv; | |
| 30 | +const flags: Record<string, string | boolean> = {}; | |
| 31 | +const positional: string[] = []; | |
| 32 | +for (let i = 0; i < rest.length; i++) { | |
| 33 | + const a = rest[i]!; | |
| 34 | + if (a.startsWith('--')) { | |
| 35 | + const k = a.slice(2); | |
| 36 | + const next = rest[i + 1]; | |
| 37 | + if (next && !next.startsWith('--')) { | |
| 38 | + flags[k] = next; | |
| 39 | + i++; | |
| 40 | + } else flags[k] = true; | |
| 41 | + } else positional.push(a); | |
| 42 | +} | |
| 43 | + | |
| 44 | +async function main() { | |
| 45 | + const { getDb, closeDb } = await import('@cancerindex/database'); | |
| 46 | + const db = getDb(); | |
| 47 | + try { | |
| 48 | + switch (cmd) { | |
| 49 | + case 'connectors': { | |
| 50 | + const { CONNECTORS } = await import('@cancerindex/connectors'); | |
| 51 | + const rows = await db.execute<{ connector_id: string; health: string; last_success_at: string | null; cursor: unknown }>(sql`SELECT connector_id, health, last_success_at, cursor FROM connector_cursors`); | |
| 52 | + const byId = new Map(rows.map((r) => [r.connector_id, r])); | |
| 53 | + for (const c of CONNECTORS) { | |
| 54 | + const h = byId.get(c.manifest.id); | |
| 55 | + console.log(`${c.manifest.id.padEnd(18)} tier${c.manifest.tier} ${c.manifest.status.padEnd(20)} license=${c.manifest.licenseStatus.padEnd(10)} health=${(h?.health ?? 'never-run').padEnd(12)} last_ok=${h?.last_success_at ?? '-'}`); | |
| 56 | + } | |
| 57 | + break; | |
| 58 | + } | |
| 59 | + case 'sources:sync': { | |
| 60 | + const { syncSources, PLANNED_MANIFESTS } = await import('@cancerindex/connectors'); | |
| 61 | + const r = await syncSources(db, PLANNED_MANIFESTS); | |
| 62 | + console.log(`[sources] created=${r.created} updated=${r.updated}`); | |
| 63 | + break; | |
| 64 | + } | |
| 65 | + case 'health': { | |
| 66 | + const { getConnector, RunContext } = await import('@cancerindex/connectors'); | |
| 67 | + const c = getConnector(positional[0] ?? ''); | |
| 68 | + if (!c) throw new Error(`unknown connector ${positional[0]}`); | |
| 69 | + const [src] = await db.execute<{ id: string }>(sql`SELECT id FROM sources WHERE slug = ${c.manifest.id}`); | |
| 70 | + const ctx = new RunContext(db, c.manifest, src?.id ?? 'CI-SOURCE-00000000', 'probe', { maxMinutes: 1 }, 0); | |
| 71 | + console.log(await c.healthCheck(ctx)); | |
| 72 | + break; | |
| 73 | + } | |
| 74 | + case 'run': { | |
| 75 | + const { getConnector, runConnector } = await import('@cancerindex/connectors'); | |
| 76 | + const c = getConnector(positional[0] ?? ''); | |
| 77 | + if (!c) throw new Error(`unknown connector ${positional[0]}`); | |
| 78 | + const r = await runConnector(db, c, { | |
| 79 | + mode: (flags.mode as never) ?? undefined, | |
| 80 | + maxRecords: flags['max-records'] ? Number(flags['max-records']) : undefined, | |
| 81 | + maxMinutes: flags['max-minutes'] ? Number(flags['max-minutes']) : undefined, | |
| 82 | + resetCursor: !!flags['reset-cursor'], | |
| 83 | + }); | |
| 84 | + console.log(`[run] ${r.runId} ${r.status}`, r.counters); | |
| 85 | + if (r.status === 'failed') process.exitCode = 1; | |
| 86 | + break; | |
| 87 | + } | |
| 88 | + case 'run-all': { | |
| 89 | + const { CONNECTORS, runConnector } = await import('@cancerindex/connectors'); | |
| 90 | + for (const c of CONNECTORS) { | |
| 91 | + if (c.manifest.status !== 'active') { | |
| 92 | + console.log(`[skip] ${c.manifest.id} (${c.manifest.status})`); | |
| 93 | + continue; | |
| 94 | + } | |
| 95 | + const r = await runConnector(db, c, { maxMinutes: flags['max-minutes'] ? Number(flags['max-minutes']) : undefined }); | |
| 96 | + console.log(`[run] ${r.runId} ${r.status}`, r.counters); | |
| 97 | + } | |
| 98 | + break; | |
| 99 | + } | |
| 100 | + case 'counters': { | |
| 101 | + const { refreshCounters } = await import('@cancerindex/ranking'); | |
| 102 | + const n = await refreshCounters(db); | |
| 103 | + console.log(`[counters] refreshed ${n} entities`); | |
| 104 | + break; | |
| 105 | + } | |
| 106 | + case 'rank': { | |
| 107 | + const { computeAllRankings } = await import('@cancerindex/ranking'); | |
| 108 | + const res = await computeAllRankings(db); | |
| 109 | + for (const r of res) console.log(`[rank] ${r.metricSlug} ${r.scopeKey} eligible=${r.eligible}`); | |
| 110 | + break; | |
| 111 | + } | |
| 112 | + case 'stats': { | |
| 113 | + const tables = ['sources', 'ingest_runs', 'source_records', 'provenance', 'cancers', 'cancer_aliases', 'cancer_hierarchy', 'cancer_codes', 'genes', 'variants', 'drugs', 'clinical_trials', 'trial_conditions', 'publications', 'literature_counts', 'civic_evidence_items', 'genomic_cohorts', 'cancer_gene_frequencies', 'epidemiology_observations', 'survival_observations', 'knowledge_edges', 'unresolved_labels', 'rankings', 'ranking_snapshots']; | |
| 114 | + for (const t of tables) { | |
| 115 | + const [r] = await db.execute<{ n: string }>(sql.raw(`SELECT count(*)::text AS n FROM ${t}`)); | |
| 116 | + console.log(`${t.padEnd(28)} ${r?.n}`); | |
| 117 | + } | |
| 118 | + break; | |
| 119 | + } | |
| 120 | + case 'trace': { | |
| 121 | + const { traceValue } = await import('@cancerindex/ranking'); | |
| 122 | + console.log(JSON.stringify(await traceValue(db, positional[0] ?? '', positional[1] ?? ''), null, 2)); | |
| 123 | + break; | |
| 124 | + } | |
| 125 | + default: | |
| 126 | + console.log('usage: pnpm cix <connectors|run|run-all|health|sources:sync|counters|rank|stats|trace>'); | |
| 127 | + } | |
| 128 | + } finally { | |
| 129 | + await closeDb(); | |
| 130 | + } | |
| 131 | +} | |
| 132 | + | |
| 133 | +main().catch((e) => { | |
| 134 | + console.error(e); | |
| 135 | + process.exit(1); | |
| 136 | +}); | |
added
tsconfig.base.json
+22 −0
@@ -0,0 +1,22 @@ | ||
| 1 | +{ | |
| 2 | + "compilerOptions": { | |
| 3 | + "target": "ES2023", | |
| 4 | + "lib": ["ES2023"], | |
| 5 | + "module": "NodeNext", | |
| 6 | + "moduleResolution": "NodeNext", | |
| 7 | + "strict": true, | |
| 8 | + "noUncheckedIndexedAccess": true, | |
| 9 | + "noImplicitOverride": true, | |
| 10 | + "noFallthroughCasesInSwitch": true, | |
| 11 | + "exactOptionalPropertyTypes": false, | |
| 12 | + "esModuleInterop": true, | |
| 13 | + "skipLibCheck": true, | |
| 14 | + "resolveJsonModule": true, | |
| 15 | + "declaration": true, | |
| 16 | + "declarationMap": true, | |
| 17 | + "sourceMap": true, | |
| 18 | + "isolatedModules": true, | |
| 19 | + "verbatimModuleSyntax": true, | |
| 20 | + "forceConsistentCasingInFileNames": true | |
| 21 | + } | |
| 22 | +} | |
added
vitest.workspace.ts
+1 −0
@@ -0,0 +1 @@ | ||
| 1 | +export default ['packages/*', 'apps/*', 'workers']; | |
| 2 | ||