v5.2.0: undo/redo, version history, embedded AI assistant, mobile builder & more (#3205)

This commit is contained in:
Amruth Pillai
2026-07-04 14:57:25 +02:00
committed by GitHub
parent 09bc6ec521
commit 57e9c8c487
181 changed files with 46794 additions and 11348 deletions
+7
View File
@@ -1,4 +1,5 @@
import type { ResumeData } from "@reactive-resume/schema/resume/data";
import { sql } from "drizzle-orm";
import * as pg from "drizzle-orm/pg-core";
import { generateId } from "@reactive-resume/utils/string";
import { user } from "./auth";
@@ -86,6 +87,12 @@ export const agentThread = pg.pgTable(
pg.index().on(t.userId, t.status, t.lastMessageAt.desc()),
pg.index().on(t.workingResumeId),
pg.index().on(t.aiProviderId),
// At most one active in-place thread per (user, working, source) resume; guards the
// getOrCreateForResume SELECT-then-INSERT race via onConflictDoNothing.
pg
.uniqueIndex("agent_threads_active_in_place_unique")
.on(t.userId, t.workingResumeId, t.sourceResumeId)
.where(sql`${t.status} = 'active' and ${t.deletedAt} is null`),
],
);
+49
View File
@@ -44,6 +44,30 @@ export const resume = pg.pgTable(
],
);
export const resumeVersion = pg.pgTable(
"resume_version",
{
id: pg
.text("id")
.notNull()
.primaryKey()
.$defaultFn(() => generateId()),
resumeId: pg
.text("resume_id")
.notNull()
.references(() => resume.id, { onDelete: "cascade" }),
userId: pg
.text("user_id")
.notNull()
.references(() => user.id, { onDelete: "cascade" }),
// Immutable snapshot of the resume data at a milestone (template change, import, AI edit, manual save).
data: pg.jsonb("data").notNull().$type<ResumeData>(),
label: pg.text("label").notNull(),
createdAt: pg.timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
},
(t) => [pg.index().on(t.resumeId, t.createdAt.desc())],
);
export const resumeStatistics = pg.pgTable("resume_statistics", {
id: pg
.text("id")
@@ -67,6 +91,31 @@ export const resumeStatistics = pg.pgTable("resume_statistics", {
.$onUpdate(() => /* @__PURE__ */ new Date()),
});
export const resumeStatisticsDaily = pg.pgTable(
"resume_statistics_daily",
{
id: pg
.text("id")
.notNull()
.primaryKey()
.$defaultFn(() => generateId()),
date: pg.date("date", { mode: "string" }).notNull(),
views: pg.integer("views").notNull().default(0),
downloads: pg.integer("downloads").notNull().default(0),
resumeId: pg
.text("resume_id")
.notNull()
.references(() => resume.id, { onDelete: "cascade" }),
createdAt: pg.timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
updatedAt: pg
.timestamp("updated_at", { withTimezone: true })
.notNull()
.defaultNow()
.$onUpdate(() => /* @__PURE__ */ new Date()),
},
(t) => [pg.unique().on(t.resumeId, t.date), pg.index().on(t.resumeId, t.date.desc())],
);
export const resumeAnalysis = pg.pgTable(
"resume_analysis",
{