spb/autoscholar
Public
1import { sql } from "drizzle-orm";2import {3 boolean,4 customType,5 index,6 integer,7 jsonb,8 pgTable,9 primaryKey,10 text,11 timestamp,12 uniqueIndex,13 uuid,14} from "drizzle-orm/pg-core";1516export const tsvector = customType<{ data: string }>({17 dataType() {18 return "tsvector";19 },20});2122export const series = pgTable("series", {23 id: uuid("id").primaryKey().defaultRandom(),24 slug: text("slug").notNull().unique(),25 name: text("name").notNull(),26 description: text("description").notNull().default(""),27 createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),28});2930export const topics = pgTable("topics", {31 id: uuid("id").primaryKey().defaultRandom(),32 slug: text("slug").notNull().unique(),33 name: text("name").notNull(),34 description: text("description").notNull().default(""),35 parentId: uuid("parent_id"),36 sortOrder: integer("sort_order").notNull().default(0),37});3839export const papers = pgTable(40 "papers",41 {42 id: uuid("id").primaryKey().defaultRandom(),43 publicId: text("public_id").notNull().unique(), // AS-WP-2026-000144 slug: text("slug").notNull().unique(),45 title: text("title").notNull(),46 status: text("status").notNull().default("published"),47 discipline: text("discipline").notNull(),48 researchMode: text("research_mode").notNull(),49 currentVersionId: uuid("current_version_id"),50 seriesId: uuid("series_id").references(() => series.id),51 firstPublishedAt: timestamp("first_published_at", { withTimezone: true }).notNull().defaultNow(),52 updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),53 retractedAt: timestamp("retracted_at", { withTimezone: true }),54 synthetic: boolean("synthetic").notNull().default(false),55 },56 (t) => [57 index("papers_status_idx").on(t.status, t.firstPublishedAt),58 index("papers_discipline_idx").on(t.discipline),59 ],60);6162export const paperVersions = pgTable(63 "paper_versions",64 {65 id: uuid("id").primaryKey().defaultRandom(),66 paperId: uuid("paper_id")67 .notNull()68 .references(() => papers.id, { onDelete: "cascade" }),69 versionNo: integer("version_no").notNull(),70 runId: uuid("run_id").notNull(),71 title: text("title").notNull(),72 abstract: text("abstract").notNull(),73 keywords: text("keywords").array().notNull().default([]),74 metadata: jsonb("metadata").$type<Record<string, unknown>>().notNull().default({}),75 artifactSet: jsonb("artifact_set")76 .$type<77 Array<{78 path: string;79 kind: string;80 sha256: string;81 bytes: number;82 contentType: string;83 publicPath: string;84 }>85 >()86 .notNull()87 .default([]),88 pdfPublicPath: text("pdf_public_path").notNull(),89 qaSummary: jsonb("qa_summary").$type<Record<string, unknown>>().notNull().default({}),90 provenance: jsonb("provenance").$type<Record<string, unknown>>().notNull().default({}),91 changelog: text("changelog").notNull().default(""),92 publishedAt: timestamp("published_at", { withTimezone: true }).notNull().defaultNow(),93 search: tsvector("search")94 .notNull()95 .generatedAlwaysAs(96 sql`setweight(to_tsvector('english', coalesce(title, '')), 'A') || setweight(to_tsvector('english', coalesce(abstract, '')), 'B') || setweight(to_tsvector('english', as_immutable_array_to_string(keywords)), 'A')`,97 ),98 },99 (t) => [100 uniqueIndex("paper_versions_uq").on(t.paperId, t.versionNo),101 uniqueIndex("paper_versions_run_uq").on(t.runId),102 index("paper_versions_search_idx").using("gin", t.search),103 ],104);105106export const paperTopics = pgTable(107 "paper_topics",108 {109 paperId: uuid("paper_id")110 .notNull()111 .references(() => papers.id, { onDelete: "cascade" }),112 topicId: uuid("topic_id")113 .notNull()114 .references(() => topics.id, { onDelete: "cascade" }),115 },116 (t) => [primaryKey({ columns: [t.paperId, t.topicId] })],117);118119export const paperSeries = pgTable(120 "paper_series",121 {122 paperId: uuid("paper_id")123 .notNull()124 .references(() => papers.id, { onDelete: "cascade" }),125 seriesId: uuid("series_id")126 .notNull()127 .references(() => series.id, { onDelete: "cascade" }),128 },129 (t) => [primaryKey({ columns: [t.paperId, t.seriesId] })],130);131132export const publicationEvents = pgTable(133 "publication_events",134 {135 id: uuid("id").primaryKey().defaultRandom(),136 paperId: uuid("paper_id")137 .notNull()138 .references(() => papers.id, { onDelete: "cascade" }),139 versionId: uuid("version_id"),140 kind: text("kind").notNull(), // published | corrected | retracted | metadata_updated | unpublished141 actor: text("actor").notNull(),142 detail: jsonb("detail").$type<Record<string, unknown>>().notNull().default({}),143 createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),144 },145 (t) => [index("publication_events_paper_idx").on(t.paperId, t.createdAt)],146);147148export const corrections = pgTable("corrections", {149 id: uuid("id").primaryKey().defaultRandom(),150 paperId: uuid("paper_id")151 .notNull()152 .references(() => papers.id, { onDelete: "cascade" }),153 fromVersionNo: integer("from_version_no").notNull(),154 toVersionNo: integer("to_version_no").notNull(),155 notice: text("notice").notNull(),156 createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),157});158159export const retractions = pgTable("retractions", {160 id: uuid("id").primaryKey().defaultRandom(),161 paperId: uuid("paper_id")162 .notNull()163 .references(() => papers.id, { onDelete: "cascade" }),164 reason: text("reason").notNull(),165 notice: text("notice").notNull(),166 retractedBy: text("retracted_by").notNull(),167 retractedAt: timestamp("retracted_at", { withTimezone: true }).notNull().defaultNow(),168});169170export const paperSequences = pgTable("paper_sequences", {171 year: integer("year").primaryKey(),172 last: integer("last").notNull().default(0),173});174