diff --git a/.cursor/rules/packages/email.mdc b/.cursor/rules/packages/email.mdc index 2d0f3843a..dd03ffb43 100644 --- a/.cursor/rules/packages/email.mdc +++ b/.cursor/rules/packages/email.mdc @@ -189,7 +189,7 @@ await sendEmail({ template: }) ``` diff --git a/README.md b/README.md index 8a4cdc093..ecec7d169 100644 --- a/README.md +++ b/README.md @@ -50,11 +50,11 @@ πŸ‘· **Incorporation** (wip) - Captable, Inc. helps you incorporate your company in minutes, with all the necessary legal documents and filings taken care of. -πŸ‘· **Cap table management** (wip) - Captable, Inc. helps you keep track of your company’s ownership structure, including who owns what percentage of the company, how much stock/options has been issued, and more. +πŸ‘· **Cap table management** (wip) - Captable, Inc. helps you keep track of your company's ownership structure, including who owns what percentage of the company, how much stock/options has been issued, and more. βœ… **Fundraise** - Captable, Inc. can help you raise capital, whether its signing standard or custom SAFE or creating and managing fundraising rounds, tracking investor commitments, and more. -βœ… **Investor updates** - Delight your investors and team members by sending them regular updates on your company’s progress. +βœ… **Investor updates** - Delight your investors and team members by sending them regular updates on your company's progress. βœ… **eSign Documents** - Sign SAFE, NDA, contracts, offere letters or any type of documents with Captable Sign. @@ -78,7 +78,9 @@ We have a community of developers, designers, and entrepreneurs who are passiona - [Next.js](https://nextjs.org) - [Tailwind](https://tailwindcss.com) -- [Prisma ORM](https://prisma.io) +- [Drizzle ORM](https://orm.drizzle.team) +- [tRPC](https://trpc.io) +- [NextAuth.js](https://next-auth.js.org) --- @@ -106,7 +108,7 @@ When contributing to Captable, Inc., whether on GitHub or in ot - Install Docker & Docker Compose - Fork & clone the forked repository -- Install node and pnpm. (optional) +- Install node and bun. (optional) - Copy `.env.example` to `.env` ```bash diff --git a/apps/captable/README.md b/apps/captable/README.md index 8a4cdc093..c61efadc8 100644 --- a/apps/captable/README.md +++ b/apps/captable/README.md @@ -78,9 +78,34 @@ We have a community of developers, designers, and entrepreneurs who are passiona - [Next.js](https://nextjs.org) - [Tailwind](https://tailwindcss.com) -- [Prisma ORM](https://prisma.io) +- [Drizzle ORM](https://orm.drizzle.team) --- +

Background Jobs

+ +Captable uses a custom job queue system for handling background tasks like: +- πŸ“§ Email notifications (welcome, password reset, invites) +- πŸ“„ PDF generation (e-signatures, documents) +- πŸ”„ Data processing and synchronization + +**Development Setup:** +```bash +# Start with job processing (recommended) +bun dx + +# Or start jobs separately +bun run jobs:dev +``` + +**Job Management:** +```bash +bun run jobs # Process pending jobs +bun run test-jobs # Queue sample test jobs +bun run jobs stats # View queue statistics +``` + +Jobs are automatically processed in production via Cron jobs. +

Getting started

When contributing to Captable, Inc., whether on GitHub or in other community spaces: @@ -106,7 +131,7 @@ When contributing to Captable, Inc., whether on GitHub or in ot - Install Docker & Docker Compose - Fork & clone the forked repository -- Install node and pnpm. (optional) +- Install node and bun. (optional) - Copy `.env.example` to `.env` ```bash @@ -117,10 +142,10 @@ When contributing to Captable, Inc., whether on GitHub or in ot ```bash - # With pnpm installed - pnpm dx + # With bun installed + bun dx - # Without pnpm installed + # Without bun installed docker compose up ``` @@ -129,8 +154,8 @@ When contributing to Captable, Inc., whether on GitHub or in ot ```bash - docker compose exec app pnpm db:migrate - docker compose exec app pnpm db:seed + docker compose exec app bun db:migrate + docker compose exec app bun db:seed ``` @@ -143,15 +168,15 @@ When contributing to Captable, Inc., whether on GitHub or in ot - Emails will be intercepted: [http://localhost:8025](http://localhost:8025) - SMTP will be on PORT `http://localhost:1025` - Postgres will be on PORT `http://localhost:5432` - - Prisma studio will be on PORT `http://localhost:5555` + - Database studio will be on PORT `http://localhost:5555` - Frequently used commands - `docker compose up` - Start the development environment - `docker compose down` - Stop the development environment - `docker compose logs -f` - View logs of the running services - `docker compose up --build` - Rebuild the docker image - - `docker compose run app pnpm db:migrate` - Run database migrations - - `docker compose run app pnpm db:seed` - Seed the database + - `docker compose run app bun db:migrate` - Run database migrations + - `docker compose run app bun db:seed` - Seed the database --- @@ -218,23 +243,23 @@ When contributing to Captable, Inc., whether on GitHub or in ot - Run the following command to install dependencies ```bash - pnpm install + bun install ``` - Run the following command to migrate and seed the database ```bash - pnpm db:migrate - pnpm db:seed + bun db:migrate + bun db:seed ``` - Run the following command to start the development server ```bash - pnpm dev + bun dev # On a different terminal, run the following command to start the mail server - pnpm email:dev + bun email:dev ``` - App will be running on [http://localhost:3000](http://localhost:3000) @@ -243,10 +268,10 @@ When contributing to Captable, Inc., whether on GitHub or in ot - Postgres will be on PORT `http://localhost:5432` - Frequently used commands - - `pnpm dev` - Start the development server - - `pnpm email:dev` - Start the mail server - - `pnpm db:migrate` - Run database migrations - - `pnpm db:seed` - Seed the database + - `bun dev` - Start the development server + - `bun email:dev` - Start the mail server + - `bun db:migrate` - Run database migrations + - `bun db:seed` - Seed the database

Implement your changes

diff --git a/apps/captable/app/api/cron/cleanup-jobs/route.ts b/apps/captable/app/api/cron/cleanup-jobs/route.ts new file mode 100644 index 000000000..a1ab26cac --- /dev/null +++ b/apps/captable/app/api/cron/cleanup-jobs/route.ts @@ -0,0 +1,30 @@ +import { logger } from "@captable/logger"; +import { cleanupJobs } from "@captable/queue"; +import { type NextRequest, NextResponse } from "next/server"; + +const log = logger.child({ module: "cron-cleanup" }); + +export async function GET(request: NextRequest) { + // Verify cron secret + const authHeader = request.headers.get("authorization"); + if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) { + return new Response("Unauthorized", { status: 401 }); + } + + try { + const cleaned = await cleanupJobs(7); // Clean jobs older than 7 days + + log.info({ cleaned }, "Job cleanup completed"); + + return NextResponse.json({ + success: true, + cleaned, + }); + } catch (error) { + log.error({ error }, "Job cleanup failed"); + return NextResponse.json( + { error: "Internal server error" }, + { status: 500 }, + ); + } +} diff --git a/apps/captable/app/api/cron/process-jobs/route.ts b/apps/captable/app/api/cron/process-jobs/route.ts new file mode 100644 index 000000000..207411eb9 --- /dev/null +++ b/apps/captable/app/api/cron/process-jobs/route.ts @@ -0,0 +1,63 @@ +import { logger } from "@captable/logger"; +import { processJobs } from "@captable/queue"; +import { type NextRequest, NextResponse } from "next/server"; +import "@/jobs"; // Import to register all jobs + +const log = logger.child({ module: "cron-jobs" }); + +export async function GET(request: NextRequest) { + // Verify cron secret for security + const authHeader = request.headers.get("authorization"); + const expectedAuth = `Bearer ${process.env.CRON_SECRET}`; + + if (!expectedAuth || authHeader !== expectedAuth) { + log.warn({ authHeader }, "Unauthorized cron job access attempt"); + return new Response("Unauthorized", { status: 401 }); + } + + try { + const startTime = Date.now(); + + // Process jobs in batches + let totalProcessed = 0; + let batchCount = 0; + const maxBatches = 10; // Prevent infinite loops + + while (batchCount < maxBatches) { + const processed = await processJobs(20); // Process 20 jobs per batch + totalProcessed += processed; + batchCount++; + + if (processed === 0) { + break; // No more jobs to process + } + + // Small delay between batches + await new Promise((resolve) => setTimeout(resolve, 100)); + } + + const duration = Date.now() - startTime; + + log.info( + { + totalProcessed, + batches: batchCount, + duration, + }, + "Cron job processing completed", + ); + + return NextResponse.json({ + success: true, + processed: totalProcessed, + batches: batchCount, + duration, + }); + } catch (error) { + log.error({ error }, "Cron job processing failed"); + return NextResponse.json( + { error: "Internal server error" }, + { status: 500 }, + ); + } +} diff --git a/apps/captable/components/onboarding/forgot-password/index.tsx b/apps/captable/components/onboarding/forgot-password/index.tsx index 1e26204bb..93f320118 100644 --- a/apps/captable/components/onboarding/forgot-password/index.tsx +++ b/apps/captable/components/onboarding/forgot-password/index.tsx @@ -40,7 +40,7 @@ const ForgotPassword = () => { }, }); const onSubmit = async (values: z.infer) => { - await mutateAsync(values.email); + await mutateAsync(values); }; return ( diff --git a/apps/captable/jobs/auth-verification-email.ts b/apps/captable/jobs/auth-verification-email.ts index aa786c6d9..f361b0fc2 100644 --- a/apps/captable/jobs/auth-verification-email.ts +++ b/apps/captable/jobs/auth-verification-email.ts @@ -1,7 +1,8 @@ -import { env } from "@/env"; -import { BaseJob } from "@/jobs/base"; import { sendMail } from "@/server/mailer"; -import type { Job } from "pg-boss"; +import { logger } from "@captable/logger"; +import { BaseJob } from "@captable/queue"; + +const log = logger.child({ module: "auth-verification-email-job" }); export type AuthVerificationEmailPayloadType = { email: string; @@ -11,6 +12,8 @@ export type AuthVerificationEmailPayloadType = { const sendAuthVerificationEmail = async ( payload: AuthVerificationEmailPayloadType, ) => { + log.info({ email: payload.email }, "Sending auth verification email"); + // Dynamic import to avoid build-time processing const { render } = await import("@captable/email"); const { AccountVerificationEmail } = await import( @@ -28,14 +31,30 @@ const sendAuthVerificationEmail = async ( subject: "Verify your account", html, }); + + log.info( + { email: payload.email }, + "Auth verification email sent successfully", + ); }; export { sendAuthVerificationEmail }; export class AuthVerificationEmailJob extends BaseJob { readonly type = "email.auth-verify"; + protected readonly options = { + maxAttempts: 5, // Critical for account verification + retryDelay: 1000, + priority: 3, // High priority + }; - async work(job: Job): Promise { - await sendAuthVerificationEmail(job.data); + async work(payload: AuthVerificationEmailPayloadType): Promise { + await sendAuthVerificationEmail(payload); } } + +// Create and register the job instance +const authVerificationEmailJob = new AuthVerificationEmailJob(); +authVerificationEmailJob.register(); + +export { authVerificationEmailJob }; diff --git a/apps/captable/jobs/base.ts b/apps/captable/jobs/base.ts deleted file mode 100644 index c4a22e5f8..000000000 --- a/apps/captable/jobs/base.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { env } from "@/env"; -import type { JOB_TYPES } from "@/lib/constants/job"; -import { singleton } from "@/lib/singleton"; -import { logger } from "@captable/logger"; -import pgBoss from "pg-boss"; - -type JobTypes = typeof JOB_TYPES; - -const log = logger.child({ module: "jobs" }); - -export type JobType = { - [Key in keyof JobTypes]: `${Key}.${JobTypes[Key][number]}`; -}[keyof JobTypes]; - -interface Job { - type: JobType; - options: pgBoss.SendOptions; - start: () => Promise; - work: (job: pgBoss.Job) => Promise; - emit: (data: T) => Promise; -} - -export abstract class BaseJob implements Job { - protected boss: pgBoss; - abstract readonly type: JobType; - readonly options = { retryLimit: 3, retryDelay: 1000 }; - - constructor() { - this.boss = boss; - } - - async start(): Promise { - await this.boss.work( - this.type, - this.work.bind(this) as unknown as pgBoss.WorkHandler, - ); - } - - abstract work(job: pgBoss.Job): Promise; - - async emit(data: T, options?: pgBoss.SendOptions): Promise { - await this.boss.send(this.type, data, options ?? this.options); - } - async bulkEmit(data: Omit, "name">[]): Promise { - await this.boss.insert( - data.map((items) => ({ ...items, name: this.type })), - ); - } -} - -export class JobManager { - private readonly boss: pgBoss; - // biome-ignore lint/suspicious/noExplicitAny: - private jobs = new Map>(); - - constructor(boss: pgBoss) { - this.boss = boss; - } - - // biome-ignore lint/suspicious/noExplicitAny: - register(job: new (boss: pgBoss) => Job): JobManager { - const jobInstance = new job(this.boss); - this.jobs.set(jobInstance.type, jobInstance); - return this; - } - - async start(): Promise { - const startTime = Date.now(); - log.info("Starting pg-boss job queue manager"); - - await this.boss.start(); - - const endTime = Date.now(); - const elapsedTime = endTime - startTime; - - log.info( - `Successfully started pg-boss job queue manager in ${elapsedTime}ms`, - ); - - for (const [jobName, job] of this.jobs.entries()) { - const jobStartTime = Date.now(); - log.info(`Registering pg-boss job:${jobName}`); - - await job.start(); - - const jobEndTime = Date.now(); - const jobElapsedTime = jobEndTime - jobStartTime; - - log.info( - `Successfully registered pg-boss job:${jobName} in ${jobElapsedTime}ms`, - ); - } - } -} - -export const boss = singleton( - "pg-boss", - () => - new pgBoss({ - connectionString: env.DATABASE_URL, - max: 5, - retryBackoff: true, - retryLimit: 4, - archiveCompletedAfterSeconds: 60 * 60 * 2, // 2 hours - deleteAfterDays: 2, - retentionDays: 2, - }), -); diff --git a/apps/captable/jobs/esign-confirmation-email.ts b/apps/captable/jobs/esign-confirmation-email.ts index d8ffc74d9..b7bf87728 100644 --- a/apps/captable/jobs/esign-confirmation-email.ts +++ b/apps/captable/jobs/esign-confirmation-email.ts @@ -1,7 +1,8 @@ -import { env } from "@/env"; -import { BaseJob } from "@/jobs/base"; import { sendMail } from "@/server/mailer"; -import type { Job } from "pg-boss"; +import { logger } from "@captable/logger"; +import { BaseJob } from "@captable/queue"; + +const log = logger.child({ module: "esign-confirmation-email-job" }); export type EsignConfirmationEmailPayloadType = { documentName: string; @@ -11,12 +12,24 @@ export type EsignConfirmationEmailPayloadType = { name: string; logo?: string | null; }; - recipient: { name?: string | null; email: string }; + recipient: { + name?: string | null; + email: string; + }; }; const sendEsignConfirmationEmail = async ( payload: EsignConfirmationEmailPayloadType, ) => { + log.info( + { + email: payload.recipient.email, + document: payload.documentName, + company: payload.company.name, + }, + "Sending esign confirmation email", + ); + // Dynamic import to avoid build-time processing const { render } = await import("@captable/email"); const { EsignConfirmationEmail } = await import("@captable/email/templates"); @@ -33,15 +46,37 @@ const sendEsignConfirmationEmail = async ( await sendMail({ to: [payload.recipient.email], - subject: `Document signed: ${payload.documentName}`, + subject: `Document signed confirmation - ${payload.documentName}`, html, }); + + log.info( + { + email: payload.recipient.email, + document: payload.documentName, + company: payload.company.name, + }, + "Esign confirmation email sent successfully", + ); }; +export { sendEsignConfirmationEmail }; + export class EsignConfirmationEmailJob extends BaseJob { readonly type = "email.esign-confirmation"; + protected readonly options = { + maxAttempts: 3, + retryDelay: 1000, + priority: 1, // Normal priority for confirmations + }; - async work(job: Job): Promise { - await sendEsignConfirmationEmail(job.data); + async work(payload: EsignConfirmationEmailPayloadType): Promise { + await sendEsignConfirmationEmail(payload); } } + +// Create and register the job instance +const esignConfirmationEmailJob = new EsignConfirmationEmailJob(); +esignConfirmationEmailJob.register(); + +export { esignConfirmationEmailJob }; diff --git a/apps/captable/jobs/esign-email.ts b/apps/captable/jobs/esign-email.ts index 2ba582c06..fc7102a66 100644 --- a/apps/captable/jobs/esign-email.ts +++ b/apps/captable/jobs/esign-email.ts @@ -1,8 +1,8 @@ -import { env } from "@/env"; -import { BaseJob } from "@/jobs/base"; import { sendMail } from "@/server/mailer"; -import { db, eq, esignRecipients, templates } from "@captable/db"; -import type { Job } from "pg-boss"; +import { logger } from "@captable/logger"; +import { BaseJob } from "@captable/queue"; + +const log = logger.child({ module: "esign-email-job" }); export type EsignEmailPayloadType = { documentName?: string; @@ -24,6 +24,15 @@ export type EsignEmailPayloadType = { }; const sendEsignEmail = async (payload: EsignEmailPayloadType) => { + log.info( + { + recipientEmail: payload.recipient.email, + documentName: payload.documentName, + company: payload.company?.name, + }, + "Sending esign email", + ); + // Dynamic import to avoid build-time processing const { render } = await import("@captable/email"); const { EsignEmail } = await import("@captable/email/templates"); @@ -44,12 +53,34 @@ const sendEsignEmail = async (payload: EsignEmailPayloadType) => { subject: `${payload.sender?.name} has sent you a document to sign`, html, }); + + log.info( + { + recipientEmail: payload.recipient.email, + documentName: payload.documentName, + company: payload.company?.name, + }, + "Esign email sent successfully", + ); }; +export { sendEsignEmail }; + export class EsignEmailJob extends BaseJob { readonly type = "email.esign-notification"; + protected readonly options = { + maxAttempts: 3, + retryDelay: 1500, + priority: 2, // High priority for legal documents + }; - async work(job: Job): Promise { - await sendEsignEmail(job.data); + async work(payload: EsignEmailPayloadType): Promise { + await sendEsignEmail(payload); } } + +// Create and register the job instance +const esignEmailJob = new EsignEmailJob(); +esignEmailJob.register(); + +export { esignEmailJob }; diff --git a/apps/captable/jobs/esign-pdf.ts b/apps/captable/jobs/esign-pdf.ts index 0ec9287d7..dce63ba3f 100644 --- a/apps/captable/jobs/esign-pdf.ts +++ b/apps/captable/jobs/esign-pdf.ts @@ -1,4 +1,3 @@ -import { BaseJob } from "@/jobs/base"; import { type EsignGetTemplateType, completeEsignDocuments, @@ -7,11 +6,11 @@ import { } from "@/server/esign"; import { getPresignedGetUrl } from "@/server/file-uploads"; import { db } from "@captable/db"; -import type { Job } from "pg-boss"; -import { - EsignConfirmationEmailJob, - type EsignConfirmationEmailPayloadType, -} from "./esign-confirmation-email"; +import { logger } from "@captable/logger"; +import { BaseJob } from "@captable/queue"; +import { esignConfirmationEmailJob } from "./esign-confirmation-email"; + +const log = logger.child({ module: "esign-pdf-job" }); export type EsignPdfPayloadType = { fields: EsignGetTemplateType["fields"]; @@ -38,8 +37,13 @@ export type EsignPdfPayloadType = { export class EsignPdfJob extends BaseJob { readonly type = "generate.esign-pdf"; + protected readonly options = { + maxAttempts: 2, // PDF generation is heavy, limit retries + retryDelay: 5000, // Longer delay for PDF processing + priority: 3, // High priority for document processing + }; - async work(job: Job): Promise { + async work(payload: EsignPdfPayloadType): Promise { const { bucketKey, data, @@ -53,48 +57,110 @@ export class EsignPdfJob extends BaseJob { templateId, recipients, company, - } = job.data; + } = payload; - const modifiedPdfBytes = await generateEsignPdf({ - bucketKey, - data, - fields, - audits, - templateName, - }); - const { fileUrl: _fileUrl, ...bucketData } = await uploadEsignDocuments({ - buffer: Buffer.from(modifiedPdfBytes), - companyId, - templateName, - }); + log.info( + { + templateId, + templateName, + companyId, + recipientCount: recipients.length, + }, + "Starting esign PDF generation", + ); + + try { + const modifiedPdfBytes = await generateEsignPdf({ + bucketKey, + data, + fields, + audits, + templateName, + }); - await db.transaction(async (tx) => { - await completeEsignDocuments({ - bucketData: bucketData, + log.info( + { + templateId, + templateName, + pdfSize: modifiedPdfBytes.length, + }, + "PDF generated successfully", + ); + + const { fileUrl: _fileUrl, ...bucketData } = await uploadEsignDocuments({ + buffer: Buffer.from(modifiedPdfBytes), companyId, - db: tx, - requestIp, - templateId, templateName, - uploaderName: sender.name || "Captable", - userAgent, }); - }); - const file = await getPresignedGetUrl(bucketData.key); + log.info( + { + templateId, + bucketKey: bucketData.key, + }, + "PDF uploaded to storage", + ); + + await db.transaction(async (tx) => { + await completeEsignDocuments({ + bucketData: bucketData, + companyId, + db: tx, + requestIp, + templateId, + templateName, + uploaderName: sender.name || "Captable", + userAgent, + }); + }); + + log.info( + { + templateId, + templateName, + }, + "Esign documents completed in database", + ); - const recipientData: { data: EsignConfirmationEmailPayloadType }[] = - recipients.map((recipient) => ({ - data: { - fileUrl: file.url, + const _file = await getPresignedGetUrl(bucketData.key); + + // Send confirmation emails to all recipients + const emailPromises = recipients.map((recipient) => + esignConfirmationEmailJob.emit({ documentName: templateName, recipient, company, senderName: sender.name || "Captable", senderEmail: sender.email as string, - }, - })); + }), + ); - await new EsignConfirmationEmailJob().bulkEmit(recipientData); + await Promise.all(emailPromises); + + log.info( + { + templateId, + templateName, + recipientCount: recipients.length, + }, + "Esign PDF job completed successfully", + ); + } catch (error) { + log.error( + { + templateId, + templateName, + error: error instanceof Error ? error.message : String(error), + }, + "Esign PDF job failed", + ); + throw error; + } } } + +// Create and register the job instance +const esignPdfJob = new EsignPdfJob(); +esignPdfJob.register(); + +export { esignPdfJob }; diff --git a/apps/captable/jobs/index.ts b/apps/captable/jobs/index.ts new file mode 100644 index 000000000..0c26de88f --- /dev/null +++ b/apps/captable/jobs/index.ts @@ -0,0 +1,31 @@ +// Import all job files to register them +import "./password-reset-email"; +import "./member-inivite-email"; +import "./auth-verification-email"; +import "./share-update-email"; +import "./share-data-room-email"; +import "./esign-email"; +import "./esign-confirmation-email"; +import "./esign-pdf"; + +// Re-export job instances for direct usage +export { passwordResetEmailJob } from "./password-reset-email"; +export { memberInviteEmailJob } from "./member-inivite-email"; +export { authVerificationEmailJob } from "./auth-verification-email"; +export { shareUpdateEmailJob } from "./share-update-email"; +export { shareDataRoomEmailJob } from "./share-data-room-email"; +export { esignEmailJob } from "./esign-email"; +export { esignConfirmationEmailJob } from "./esign-confirmation-email"; +export { esignPdfJob } from "./esign-pdf"; + +// Re-export queue functions for direct usage +export { + addJob, + addJobs, + processJobs, + cleanupJobs, + getStats, + getRegisteredProcessors, + clearProcessors, + register, +} from "@captable/queue"; diff --git a/apps/captable/jobs/member-inivite-email.ts b/apps/captable/jobs/member-inivite-email.ts index ea459c916..96aef8ea4 100644 --- a/apps/captable/jobs/member-inivite-email.ts +++ b/apps/captable/jobs/member-inivite-email.ts @@ -1,7 +1,8 @@ -import { env } from "@/env"; -import { BaseJob } from "@/jobs/base"; import { sendMail } from "@/server/mailer"; -import type { Job } from "pg-boss"; +import { logger } from "@captable/logger"; +import { BaseJob } from "@captable/queue"; + +const log = logger.child({ module: "member-invite-email-job" }); export type MemberInviteEmailPayloadType = { email: string; @@ -11,6 +12,14 @@ export type MemberInviteEmailPayloadType = { }; const sendMemberInviteEmail = async (payload: MemberInviteEmailPayloadType) => { + log.info( + { + email: payload.email, + company: payload.companyName, + }, + "Sending member invite email", + ); + // Dynamic import to avoid build-time processing const { render } = await import("@captable/email"); const { MemberInviteEmail } = await import("@captable/email/templates"); @@ -28,14 +37,33 @@ const sendMemberInviteEmail = async (payload: MemberInviteEmailPayloadType) => { subject: `You're invited to join ${payload.companyName}`, html, }); + + log.info( + { + email: payload.email, + company: payload.companyName, + }, + "Member invite email sent successfully", + ); }; export { sendMemberInviteEmail }; export class MemberInviteEmailJob extends BaseJob { readonly type = "email.member-invite"; + protected readonly options = { + maxAttempts: 3, + retryDelay: 1500, + priority: 2, // Higher priority for invites + }; - async work(job: Job): Promise { - await sendMemberInviteEmail(job.data); + async work(payload: MemberInviteEmailPayloadType): Promise { + await sendMemberInviteEmail(payload); } } + +// Create and register the job instance +const memberInviteEmailJob = new MemberInviteEmailJob(); +memberInviteEmailJob.register(); + +export { memberInviteEmailJob }; diff --git a/apps/captable/jobs/password-reset-email.ts b/apps/captable/jobs/password-reset-email.ts index c55475cbf..46ac8c468 100644 --- a/apps/captable/jobs/password-reset-email.ts +++ b/apps/captable/jobs/password-reset-email.ts @@ -1,7 +1,8 @@ -import { env } from "@/env"; -import { BaseJob } from "@/jobs/base"; import { sendMail } from "@/server/mailer"; -import type { Job } from "pg-boss"; +import { logger } from "@captable/logger"; +import { BaseJob } from "@captable/queue"; + +const log = logger.child({ module: "password-reset-email-job" }); export type PasswordResetEmailPayloadType = { email: string; @@ -11,6 +12,8 @@ export type PasswordResetEmailPayloadType = { const sendPasswordResetEmail = async ( payload: PasswordResetEmailPayloadType, ) => { + log.info({ email: payload.email }, "Sending password reset email"); + // Dynamic import to avoid build-time processing const { render } = await import("@captable/email"); const { PasswordResetEmail } = await import("@captable/email/templates"); @@ -26,14 +29,27 @@ const sendPasswordResetEmail = async ( subject: "Reset your password", html, }); + + log.info({ email: payload.email }, "Password reset email sent successfully"); }; export { sendPasswordResetEmail }; export class PasswordResetEmailJob extends BaseJob { readonly type = "email.password-reset"; + protected readonly options = { + maxAttempts: 5, // Email is critical + retryDelay: 2000, + priority: 1, // High priority + }; - async work(job: Job): Promise { - await sendPasswordResetEmail(job.data); + async work(payload: PasswordResetEmailPayloadType): Promise { + await sendPasswordResetEmail(payload); } } + +// Create and register the job instance +const passwordResetEmailJob = new PasswordResetEmailJob(); +passwordResetEmailJob.register(); + +export { passwordResetEmailJob }; diff --git a/apps/captable/jobs/share-data-room-email.ts b/apps/captable/jobs/share-data-room-email.ts index e92d43daa..9937739e8 100644 --- a/apps/captable/jobs/share-data-room-email.ts +++ b/apps/captable/jobs/share-data-room-email.ts @@ -1,48 +1,78 @@ import { env } from "@/env"; -import { BaseJob } from "@/jobs/base"; import { sendMail } from "@/server/mailer"; -import { render } from "@captable/email"; -import { ShareDataRoomEmail } from "@captable/email/templates"; -import type { Job } from "pg-boss"; +import { logger } from "@captable/logger"; +import { BaseJob } from "@captable/queue"; + +const log = logger.child({ module: "share-data-room-email-job" }); export type ShareDataRoomEmailPayloadType = { - to: string; - dataRoom: string; + dataRoomId: string; + dataRoomName: string; + recipientName: string | null; + link: string; + email: string; companyName: string; senderName: string; - link: string; - recipientName?: string | null; }; -const sendShareDataRoomEmail = async ({ - to, - dataRoom, - companyName, - senderName, - link, - recipientName, -}: ShareDataRoomEmailPayloadType) => { +const sendShareDataRoomEmail = async ( + payload: ShareDataRoomEmailPayloadType, +) => { + log.info( + { + email: payload.email, + dataRoomId: payload.dataRoomId, + company: payload.companyName, + }, + "Sending share data room email", + ); + + const { render } = await import("@captable/email"); + const { ShareDataRoomEmail } = await import("@captable/email/templates"); + const html = await render( ShareDataRoomEmail({ - dataRoom, - companyName, - senderName, - link, - recipientName, + senderName: payload.senderName, + recipientName: payload.recipientName, + companyName: payload.companyName, + dataRoom: payload.dataRoomName, + link: payload.link, }), ); await sendMail({ - to: [to], - subject: `${senderName} shared a data room with you`, + to: [payload.email], + subject: `${payload.senderName} shared a data room with you`, html, }); + + log.info( + { + email: payload.email, + dataRoomId: payload.dataRoomId, + company: payload.companyName, + }, + "Share data room email sent successfully", + ); }; +export { sendShareDataRoomEmail }; + export class ShareDataRoomEmailJob extends BaseJob { readonly type = "email.share-data-room"; + protected readonly options = { + maxAttempts: 3, + retryDelay: 1000, + priority: 2, // High priority for data room access + }; - async work(job: Job): Promise { - await sendShareDataRoomEmail(job.data); + async work(payload: ShareDataRoomEmailPayloadType): Promise { + await sendShareDataRoomEmail(payload); } } + +// Create and register the job instance +const shareDataRoomEmailJob = new ShareDataRoomEmailJob(); +shareDataRoomEmailJob.register(); + +export { shareDataRoomEmailJob }; diff --git a/apps/captable/jobs/share-update-email.ts b/apps/captable/jobs/share-update-email.ts index 9a3173840..74967f0b0 100644 --- a/apps/captable/jobs/share-update-email.ts +++ b/apps/captable/jobs/share-update-email.ts @@ -1,7 +1,8 @@ -import { env } from "@/env"; -import { BaseJob } from "@/jobs/base"; import { sendMail } from "@/server/mailer"; -import type { Job } from "pg-boss"; +import { logger } from "@captable/logger"; +import { BaseJob } from "@captable/queue"; + +const log = logger.child({ module: "share-update-email-job" }); export type ShareUpdateEmailPayloadType = { to: string; @@ -13,6 +14,15 @@ export type ShareUpdateEmailPayloadType = { }; const sendShareUpdateEmail = async (payload: ShareUpdateEmailPayloadType) => { + log.info( + { + to: payload.to, + company: payload.companyName, + update: payload.updateTitle, + }, + "Sending share update email", + ); + // Dynamic import to avoid build-time processing const { render } = await import("@captable/email"); const { ShareUpdateEmail } = await import("@captable/email/templates"); @@ -32,12 +42,34 @@ const sendShareUpdateEmail = async (payload: ShareUpdateEmailPayloadType) => { subject: `${payload.senderName} shared an update with you`, html, }); + + log.info( + { + to: payload.to, + company: payload.companyName, + update: payload.updateTitle, + }, + "Share update email sent successfully", + ); }; +export { sendShareUpdateEmail }; + export class ShareUpdateEmailJob extends BaseJob { readonly type = "email.share-update"; + protected readonly options = { + maxAttempts: 3, + retryDelay: 1000, + priority: 1, + }; - async work(job: Job): Promise { - await sendShareUpdateEmail(job.data); + async work(payload: ShareUpdateEmailPayloadType): Promise { + await sendShareUpdateEmail(payload); } } + +// Create and register the job instance +const shareUpdateEmailJob = new ShareUpdateEmailJob(); +shareUpdateEmailJob.register(); + +export { shareUpdateEmailJob }; diff --git a/apps/captable/jobs/start.ts b/apps/captable/jobs/start.ts index 9ed363376..90a239891 100644 --- a/apps/captable/jobs/start.ts +++ b/apps/captable/jobs/start.ts @@ -1,23 +1,10 @@ -import { AuthVerificationEmailJob } from "@/jobs/auth-verification-email"; -import { JobManager, boss } from "@/jobs/base"; -import { EsignConfirmationEmailJob } from "@/jobs/esign-confirmation-email"; -import { EsignEmailJob } from "@/jobs/esign-email"; -import { EsignPdfJob } from "@/jobs/esign-pdf"; -import { MemberInviteEmailJob } from "@/jobs/member-inivite-email"; -import { PasswordResetEmailJob } from "@/jobs/password-reset-email"; -import { ShareDataRoomEmailJob } from "@/jobs/share-data-room-email"; -import { ShareUpdateEmailJob } from "@/jobs/share-update-email"; +// Import all jobs to register them with the new queue system +import "@/jobs"; -export async function startJobs() { - const jobs = new JobManager(boss) - .register(AuthVerificationEmailJob) - .register(ShareUpdateEmailJob) - .register(ShareDataRoomEmailJob) - .register(MemberInviteEmailJob) - .register(PasswordResetEmailJob) - .register(EsignEmailJob) - .register(EsignConfirmationEmailJob) - .register(EsignPdfJob); - - await jobs.start(); +// Jobs are now auto-registered when imported +// No need for manual job manager startup +export function startJobs() { + // All jobs are automatically registered when the module is imported + // The Queue will process them via cron jobs + console.log("Jobs are auto-registered and ready for processing"); } diff --git a/apps/captable/package.json b/apps/captable/package.json index 70f5e9394..fc26a2eb9 100644 --- a/apps/captable/package.json +++ b/apps/captable/package.json @@ -9,7 +9,10 @@ "start": "next start", "check-types": "tsc --noEmit", "format": "biome format --write", - "lint": "biome check" + "lint": "biome check", + "jobs": "bun run scripts/dev/jobs.ts", + "jobs:dev": "bun run scripts/dev/jobs.ts process --watch", + "test-jobs": "bun run scripts/dev/test-jobs.ts" }, "dependencies": { "@aws-sdk/client-s3": "^3.812.0", @@ -19,6 +22,7 @@ "@captable/auth": "*", "@captable/db": "*", "@captable/email": "*", + "@captable/queue": "*", "@captable/rbac": "*", "@captable/utils": "*", "@hono/zod-openapi": "^0.19.6", @@ -73,7 +77,6 @@ "nodemailer": "^7.0.3", "papaparse": "^5.5.3", "pdf-lib": "^1.17.1", - "pg-boss": "^10.2.0", "pushmodal": "^1.0.5", "react": "^19.1.0", "react-dom": "^19.1.0", diff --git a/apps/captable/scripts/dev/README.md b/apps/captable/scripts/dev/README.md new file mode 100644 index 000000000..c032cfa6c --- /dev/null +++ b/apps/captable/scripts/dev/README.md @@ -0,0 +1,76 @@ +# Development Scripts + +This directory contains development utilities for the Captable application. + +## Jobs Script (`jobs.ts`) + +Process jobs from the queue system. + +### Usage + +```bash +# Process all pending jobs once +bun run jobs + +# Process jobs continuously (watch mode) +bun run jobs:dev + +# Show queue statistics +bun run jobs stats + +# Clean up old completed jobs +bun run jobs cleanup +``` + +### Watch Mode + +The watch mode (`--watch` flag) continuously monitors the job queue and processes jobs as they appear. This is useful during development when you want jobs to be processed automatically. + +- Checks for new jobs every 5 seconds when idle +- Processes jobs immediately when found +- Gracefully handles shutdown with Ctrl+C + +## Test Jobs Script (`test-jobs.ts`) + +Queue sample jobs for testing purposes. + +### Usage + +```bash +# Queue all test email jobs +bun run test-jobs + +# Queue specific job types +bun run test-jobs password-reset +bun run test-jobs member-invite +bun run test-jobs auth-verification + +# Just show queue statistics +bun run test-jobs stats +``` + +## Running Jobs with Development Server + +To run jobs automatically alongside your development server: + +```bash +# Run everything including job processing +bun dx +``` + +This will start: +- Next.js development server +- Database studio +- Email development server +- Job processor in watch mode + +## Job Types Available + +- `email.password-reset` - Password reset emails +- `email.member-invite` - Member invitation emails +- `email.auth-verify` - Account verification emails +- `email.share-update` - Share update notifications +- `email.share-data-room` - Data room sharing emails +- `email.esign` - E-signature request emails +- `email.esign-confirmation` - E-signature confirmation emails +- `generate.esign-pdf` - PDF generation for e-signatures \ No newline at end of file diff --git a/apps/captable/scripts/dev/jobs.ts b/apps/captable/scripts/dev/jobs.ts new file mode 100644 index 000000000..931b06839 --- /dev/null +++ b/apps/captable/scripts/dev/jobs.ts @@ -0,0 +1,177 @@ +#!/usr/bin/env bun + +import { logger } from "@captable/logger"; +import { cleanupJobs, getStats, processJobs } from "@captable/queue"; + +// Import all jobs to register them +import "@/jobs"; + +const log = logger.child({ module: "dev-job-runner" }); + +async function runJobs(_silent = false) { + try { + // Get current stats first + const initialStats = await getStats(); + + if (initialStats.pending === 0) { + // Only log occasionally when idle, not every time + return 0; + } + + // Only log when we actually have jobs to process + log.info(initialStats, "Found jobs to process"); + + // Process jobs in batches + let totalProcessed = 0; + let batchCount = 0; + const maxBatches = 10; + + while (batchCount < maxBatches) { + const processed = await processJobs(20); + totalProcessed += processed; + batchCount++; + + if (processed === 0) { + break; + } + + log.info({ processed, batch: batchCount }, "Batch completed"); + + // Small delay between batches + await new Promise((resolve) => setTimeout(resolve, 100)); + } + + const finalStats = await getStats(); + log.info( + { + totalProcessed, + batches: batchCount, + finalStats, + }, + "Job processing completed", + ); + + return totalProcessed; + } catch (error) { + log.error({ error }, "Job processing failed"); + throw error; + } +} + +async function runJobsInWatchMode() { + log.info("πŸ”„ Starting job processor in watch mode..."); + log.info("πŸ’€ Monitoring queue (quiet mode - only logs when jobs found)"); + log.info("Press Ctrl+C to stop"); + + const POLL_INTERVAL = 5000; // Check every 5 seconds + const HEARTBEAT_INTERVAL = 60000; // Show heartbeat every minute + + let isShuttingDown = false; + let lastHeartbeat = Date.now(); + let totalChecks = 0; + let totalJobsProcessed = 0; + const startTime = Date.now(); + + // Handle graceful shutdown + process.on("SIGINT", () => { + log.info("Received SIGINT, shutting down gracefully..."); + isShuttingDown = true; + }); + + process.on("SIGTERM", () => { + log.info("Received SIGTERM, shutting down gracefully..."); + isShuttingDown = true; + }); + + while (!isShuttingDown) { + try { + totalChecks++; + const processed = await runJobs(true); // Silent mode for routine checks + totalJobsProcessed += processed; + + // Show heartbeat periodically when idle + const now = Date.now(); + if (now - lastHeartbeat >= HEARTBEAT_INTERVAL) { + const uptime = Math.round((now - startTime) / 1000 / 60); + log.info( + { + checks: totalChecks, + jobsProcessed: totalJobsProcessed, + uptime: `${uptime}m`, + }, + "πŸ’“ Job processor active", + ); + lastHeartbeat = now; + } + + if (processed === 0) { + // No jobs processed, wait before checking again + await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL)); + } else { + // Jobs were processed, check again more quickly + await new Promise((resolve) => setTimeout(resolve, 1000)); + } + } catch (error) { + log.error({ error }, "Error in watch mode, continuing..."); + await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL)); + } + } + + const finalUptime = Math.round((Date.now() - startTime) / 1000 / 60); + log.info( + { + totalChecks, + jobsProcessed: totalJobsProcessed, + uptime: `${finalUptime}m`, + }, + "βœ… Job processor stopped", + ); +} + +async function cleanupOldJobs() { + try { + log.info("Cleaning up old jobs..."); + const cleaned = await cleanupJobs(1); // Clean jobs older than 1 day in dev + log.info({ cleaned }, "Cleanup completed"); + } catch (error) { + log.error({ error }, "Cleanup failed"); + } +} + +async function showStats() { + try { + const stats = await getStats(); + console.log("\nπŸ“Š Queue Statistics:"); + console.table(stats); + } catch (error) { + log.error({ error }, "Failed to get stats"); + } +} + +// Parse command line arguments +const args = process.argv.slice(2); +const command = args[0] || "process"; +const isWatchMode = args.includes("--watch") || args.includes("-w"); + +switch (command) { + case "process": + if (isWatchMode) { + runJobsInWatchMode(); + } else { + runJobs().catch(() => process.exit(1)); + } + break; + case "cleanup": + cleanupOldJobs(); + break; + case "stats": + showStats(); + break; + default: + console.log("Usage: bun run jobs [process|cleanup|stats] [--watch]"); + console.log(" process - Process pending jobs (default)"); + console.log(" cleanup - Clean up old completed jobs"); + console.log(" stats - Show queue statistics"); + console.log(" --watch - Run in watch mode (continuous processing)"); + process.exit(1); +} diff --git a/apps/captable/scripts/dev/test-jobs.ts b/apps/captable/scripts/dev/test-jobs.ts new file mode 100644 index 000000000..458596bd5 --- /dev/null +++ b/apps/captable/scripts/dev/test-jobs.ts @@ -0,0 +1,118 @@ +#!/usr/bin/env bun + +import { logger } from "@captable/logger"; + +// Import all jobs to register them +import { + authVerificationEmailJob, + esignConfirmationEmailJob, + esignEmailJob, + esignPdfJob, + getStats, + memberInviteEmailJob, + passwordResetEmailJob, + shareDataRoomEmailJob, + shareUpdateEmailJob, +} from "@/jobs"; + +const log = logger.child({ module: "test-jobs" }); + +async function testPasswordResetEmail() { + try { + log.info("Testing password reset email job..."); + + const jobId = await passwordResetEmailJob.emit({ + email: "test@example.com", + resetLink: + "https://cloud.captable.inc/auth/reset-password?token=test-token", + }); + + log.info({ jobId }, "Password reset email job queued"); + } catch (error) { + log.error({ error }, "Failed to queue password reset email job"); + } +} + +async function testMemberInviteEmail() { + try { + log.info("Testing member invite email job..."); + + const jobId = await memberInviteEmailJob.emit({ + email: "newmember@example.com", + invitedBy: "John Doe", + companyName: "Test Company", + inviteLink: "https://cloud.captable.inc/invite?token=test-invite-token", + }); + + log.info({ jobId }, "Member invite email job queued"); + } catch (error) { + log.error({ error }, "Failed to queue member invite email job"); + } +} + +async function testAuthVerificationEmail() { + try { + log.info("Testing auth verification email job..."); + + const jobId = await authVerificationEmailJob.emit({ + email: "user@example.com", + verifyLink: + "https://cloud.captable.inc/auth/verify?token=test-verification-token", + }); + + log.info({ jobId }, "Auth verification email job queued"); + } catch (error) { + log.error({ error }, "Failed to queue auth verification email job"); + } +} + +async function showQueueStats() { + try { + const stats = await getStats(); + console.log("\nπŸ“Š Queue Statistics:"); + console.table(stats); + } catch (error) { + log.error({ error }, "Failed to get queue stats"); + } +} + +async function testAllEmailJobs() { + log.info("πŸ§ͺ Testing all email jobs..."); + + await testPasswordResetEmail(); + await testMemberInviteEmail(); + await testAuthVerificationEmail(); + + log.info("βœ… All test jobs queued!"); + await showQueueStats(); +} + +// Parse command line arguments +const command = process.argv[2] || "all"; + +switch (command) { + case "password-reset": + testPasswordResetEmail().then(() => showQueueStats()); + break; + case "member-invite": + testMemberInviteEmail().then(() => showQueueStats()); + break; + case "auth-verification": + testAuthVerificationEmail().then(() => showQueueStats()); + break; + case "stats": + showQueueStats(); + break; + case "all": + testAllEmailJobs(); + break; + default: + console.log("Usage: bun run test-jobs [job-type]"); + console.log("Available job types:"); + console.log(" password-reset - Test password reset email"); + console.log(" member-invite - Test member invite email"); + console.log(" auth-verification - Test auth verification email"); + console.log(" stats - Show queue statistics"); + console.log(" all - Test all email jobs (default)"); + process.exit(1); +} diff --git a/apps/captable/trpc/routers/auth/procedure/forgot-password.ts b/apps/captable/trpc/routers/auth/procedure/forgot-password.ts index a17b18a15..01d30b291 100644 --- a/apps/captable/trpc/routers/auth/procedure/forgot-password.ts +++ b/apps/captable/trpc/routers/auth/procedure/forgot-password.ts @@ -1,41 +1,35 @@ import { env } from "@/env"; -import { PasswordResetEmailJob } from "@/jobs/password-reset-email"; -import { generatePasswordResetToken } from "@/lib/token"; -import { getUserByEmail } from "@/server/user"; +import { passwordResetEmailJob } from "@/jobs"; import { withoutAuth } from "@/trpc/api/trpc"; +import { db, eq, users } from "@captable/db"; import { TRPCError } from "@trpc/server"; import { z } from "zod"; +const forgotPasswordSchema = z.object({ + email: z.string().email(), +}); + export const forgotPasswordProcedure = withoutAuth - .input(z.string().email()) + .input(forgotPasswordSchema) .mutation(async ({ input }) => { - const existingUser = await getUserByEmail(input); - - if (!existingUser) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: "Email not found!", - }); - } + const { email } = input; - const passwordResetToken = await generatePasswordResetToken(input); + const existingUser = await db.query.users.findFirst({ + where: eq(users.email, email), + }); - if (!passwordResetToken) { + if (!existingUser) { throw new TRPCError({ - code: "INTERNAL_SERVER_ERROR", - message: "Failed to generate password reset token", + code: "NOT_FOUND", + message: "User not found. Please check your email and try again.", }); } - const { email, token } = passwordResetToken; - - const resetLink = `${env.NEXT_PUBLIC_BASE_URL}/reset-password?token=${token}`; + const resetToken = crypto.randomUUID(); + const resetLink = `${env.NEXT_PUBLIC_BASE_URL}/reset-password?token=${resetToken}&email=${encodeURIComponent(email)}`; - await new PasswordResetEmailJob().emit({ email, resetLink }); + // Emit password reset email job + await passwordResetEmailJob.emit({ email, resetLink }); - return { - success: true, - message: - "To reset your password, please click the link sent to your email.", - }; + return { success: true }; }); diff --git a/apps/captable/trpc/routers/data-room-router/router.ts b/apps/captable/trpc/routers/data-room-router/router.ts index f89ba75ac..4c40770f7 100644 --- a/apps/captable/trpc/routers/data-room-router/router.ts +++ b/apps/captable/trpc/routers/data-room-router/router.ts @@ -1,8 +1,5 @@ import { env } from "@/env"; -import { - ShareDataRoomEmailJob, - type ShareDataRoomEmailPayloadType, -} from "@/jobs/share-data-room-email"; +import { shareDataRoomEmailJob } from "@/jobs"; import { generatePublicId } from "@/lib/common/id"; import { encode } from "@/lib/jwt"; import { ShareRecipientSchema } from "@/schema/contacts"; @@ -390,16 +387,17 @@ export const dataRoomRouter = createTRPCRouter({ const link = `${baseUrl}/data-rooms/${dataRoom.publicId}?token=${token}`; - const payload: ShareDataRoomEmailPayloadType = { - to: email, - senderName: `${senderName}`, - recipientName: recipient.name, - companyName: company.name, - dataRoom: dataRoom.name, + const payload = { + dataRoomId: dataRoomId, + dataRoomName: dataRoom.name, + recipientName: recipient.name || null, link, + email, + companyName: company.name, + senderName: senderName || "Team", }; - await new ShareDataRoomEmailJob().emit(payload); + await shareDataRoomEmailJob.emit(payload); await db.transaction(async (tx) => { await Audit.create( diff --git a/apps/captable/trpc/routers/member-router/procedures/invite-member.ts b/apps/captable/trpc/routers/member-router/procedures/invite-member.ts index efbd59e7d..368ee1a4d 100644 --- a/apps/captable/trpc/routers/member-router/procedures/invite-member.ts +++ b/apps/captable/trpc/routers/member-router/procedures/invite-member.ts @@ -1,4 +1,5 @@ import { env } from "@/env"; +import { memberInviteEmailJob } from "@/jobs"; import { MemberInviteEmailJob } from "@/jobs/member-inivite-email"; import { generatePasswordResetToken } from "@/lib/token"; import { Audit } from "@/server/audit"; @@ -227,13 +228,13 @@ export const inviteMemberProcedure = withAccessControl const inviteLink = `${env.NEXT_PUBLIC_BASE_URL}/verify-member?token=${verificationToken}&passwordResetToken=${passwordResetTokenResult.token}`; const payload = { - invitedBy: user.name || "Someone", - companyName: company.name, - inviteLink, email, + inviteLink, + companyName: company.name, + invitedBy: user.name || "Unknown", }; - await new MemberInviteEmailJob().emit(payload); + await memberInviteEmailJob.emit(payload); return { success: true }; }); diff --git a/apps/captable/trpc/routers/member-router/procedures/re-invite.ts b/apps/captable/trpc/routers/member-router/procedures/re-invite.ts index 86d5620ec..94fdddbdc 100644 --- a/apps/captable/trpc/routers/member-router/procedures/re-invite.ts +++ b/apps/captable/trpc/routers/member-router/procedures/re-invite.ts @@ -1,5 +1,5 @@ import { env } from "@/env"; -import { MemberInviteEmailJob } from "@/jobs/member-inivite-email"; +import { memberInviteEmailJob } from "@/jobs"; import { generatePasswordResetToken } from "@/lib/token"; import { Audit } from "@/server/audit"; import { checkMembership } from "@/server/member"; @@ -148,13 +148,13 @@ export const reInviteProcedure = withAuth const inviteLink = `${env.NEXT_PUBLIC_BASE_URL}/verify-member?token=${verificationToken}&passwordResetToken=${passwordResetToken}`; const payload = { - invitedBy: user.name || "Someone", - companyName: company.name, + email, inviteLink, - email: email, + companyName: company.name, + invitedBy: user.name || "Unknown", }; - await new MemberInviteEmailJob().emit(payload); + await memberInviteEmailJob.emit(payload); return { success: true }; }); diff --git a/apps/captable/trpc/routers/template-field-router/procedures/add-fields.ts b/apps/captable/trpc/routers/template-field-router/procedures/add-fields.ts index b03180071..c20002111 100644 --- a/apps/captable/trpc/routers/template-field-router/procedures/add-fields.ts +++ b/apps/captable/trpc/routers/template-field-router/procedures/add-fields.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/prefer-for-of */ import { env } from "@/env"; -import { EsignEmailJob, type EsignEmailPayloadType } from "@/jobs/esign-email"; +import { esignEmailJob } from "@/jobs"; import { decode, encode } from "@/lib/jwt"; import { Audit } from "@/server/audit"; import { checkMembership } from "@/server/member"; @@ -55,7 +55,24 @@ export const addFieldProcedure = withAuth try { const user = ctx.session.user; const { userAgent, requestIp } = ctx; - const mails: EsignEmailPayloadType[] = []; + const mails: Array<{ + signingLink: string; + recipient: { + id: string; + name: string | null | undefined; + email: string; + }; + sender?: { + name: string | null | undefined; + email: string | null | undefined; + }; + message?: string | null; + documentName?: string; + company?: { + name: string; + logo: string | null | undefined; + }; + }> = []; if (input.status === "COMPLETE" && (!user.email || !user.name)) { return { @@ -65,7 +82,7 @@ export const addFieldProcedure = withAuth }; } - const template = await db.transaction(async (tx) => { + const _template = await db.transaction(async (tx) => { const { companyId } = await checkMembership({ tx, session: ctx.session, @@ -232,12 +249,7 @@ export const addFieldProcedure = withAuth }); if (mails.length) { - new EsignEmailJob().bulkEmit( - mails.map((data) => ({ - data, - singletonKey: `esign-notify-${template.id}-${data.recipient.id}`, - })), - ); + await esignEmailJob.bulkEmit(mails); } return { diff --git a/apps/captable/trpc/routers/update/procedures/share-update.ts b/apps/captable/trpc/routers/update/procedures/share-update.ts index c665456fc..715d05f55 100644 --- a/apps/captable/trpc/routers/update/procedures/share-update.ts +++ b/apps/captable/trpc/routers/update/procedures/share-update.ts @@ -1,4 +1,5 @@ import { env } from "@/env"; +import { shareUpdateEmailJob } from "@/jobs"; import { ShareUpdateEmailJob, type ShareUpdateEmailPayloadType, @@ -135,16 +136,16 @@ export const shareUpdateProcedure = withAuth const link = `${baseUrl}/updates/${update.publicId}?token=${token}`; - const payload: ShareUpdateEmailPayloadType = { - to: email, - senderName: `${senderName}`, - recipientName: recipient.name ?? null, + const payload = { + to: recipient.email || recipient.value, + senderName: senderName || "Team", + recipientName: recipient.name || null, companyName: update.companyName || "", updateTitle: update.title, - link, + link: link, }; - await new ShareUpdateEmailJob().emit(payload); + await shareUpdateEmailJob.emit(payload); } }; diff --git a/apps/captable/vercel.json b/apps/captable/vercel.json new file mode 100644 index 000000000..53db7b8ca --- /dev/null +++ b/apps/captable/vercel.json @@ -0,0 +1,12 @@ +{ + "crons": [ + { + "path": "/api/cron/process-jobs", + "schedule": "* * * * *" + }, + { + "path": "/api/cron/cleanup-jobs", + "schedule": "0 2 * * *" + } + ] +} diff --git a/bun.lock b/bun.lock index d140aadb7..b497ddc65 100644 --- a/bun.lock +++ b/bun.lock @@ -26,6 +26,7 @@ "@captable/auth": "*", "@captable/db": "*", "@captable/email": "*", + "@captable/queue": "*", "@captable/rbac": "*", "@captable/utils": "*", "@hono/zod-openapi": "^0.19.6", @@ -80,7 +81,6 @@ "nodemailer": "^7.0.3", "papaparse": "^5.5.3", "pdf-lib": "^1.17.1", - "pg-boss": "^10.2.0", "pushmodal": "^1.0.5", "react": "^19.1.0", "react-dom": "^19.1.0", @@ -192,6 +192,21 @@ "typescript": "^5.0.0", }, }, + "packages/queue": { + "name": "@captable/queue", + "dependencies": { + "@captable/db": "workspace:*", + "@captable/logger": "workspace:*", + "drizzle-orm": "^0.43.1", + }, + "devDependencies": { + "@biomejs/biome": "1.9.4", + "@types/bun": "latest", + }, + "peerDependencies": { + "typescript": "^5.0.0", + }, + }, "packages/rbac": { "name": "@captable/rbac", "version": "1.0.0", @@ -372,6 +387,8 @@ "@captable/logger": ["@captable/logger@workspace:packages/logger"], + "@captable/queue": ["@captable/queue@workspace:packages/queue"], + "@captable/rbac": ["@captable/rbac@workspace:packages/rbac"], "@captable/utils": ["@captable/utils@workspace:packages/utils"], @@ -1408,8 +1425,6 @@ "crelt": ["crelt@1.0.6", "", {}, "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g=="], - "cron-parser": ["cron-parser@4.9.0", "", { "dependencies": { "luxon": "^3.2.1" } }, "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q=="], - "cross-fetch": ["cross-fetch@4.1.0", "", { "dependencies": { "node-fetch": "^2.7.0" } }, "sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw=="], "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], @@ -1842,8 +1857,6 @@ "lru-cache": ["lru-cache@11.1.0", "", {}, "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A=="], - "luxon": ["luxon@3.6.1", "", {}, "sha512-tJLxrKJhO2ukZ5z0gyjY1zPh3Rh88Ej9P7jNrZiHMUXHae1yvI2imgOZtL1TO8TW6biMMKfTtAOoEJANgtWBMQ=="], - "magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="], "make-cancellable-promise": ["make-cancellable-promise@1.3.2", "", {}, "sha512-GCXh3bq/WuMbS+Ky4JBPW1hYTOU+znU+Q5m9Pu+pI8EoUqIHk9+tviOKC6/qhHh8C4/As3tzJ69IF32kdz85ww=="], @@ -2074,8 +2087,6 @@ "pg": ["pg@8.16.0", "", { "dependencies": { "pg-connection-string": "^2.9.0", "pg-pool": "^3.10.0", "pg-protocol": "^1.10.0", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "optionalDependencies": { "pg-cloudflare": "^1.2.5" }, "peerDependencies": { "pg-native": ">=3.0.1" }, "optionalPeers": ["pg-native"] }, "sha512-7SKfdvP8CTNXjMUzfcVTaI+TDzBEeaUnVwiVGZQD1Hh33Kpev7liQba9uLd4CfN8r9mCVsD0JIpq03+Unpz+kg=="], - "pg-boss": ["pg-boss@10.2.0", "", { "dependencies": { "cron-parser": "^4.9.0", "pg": "^8.14.1", "serialize-error": "^8.1.0" } }, "sha512-5izR07hpNY6LktlidFmvgK9LGErdUeV+vfjMOTLP01FV9tcGpYu6nZitXU6bYl6nOGgoegzhahlSlAgi4PXpEA=="], - "pg-cloudflare": ["pg-cloudflare@1.2.5", "", {}, "sha512-OOX22Vt0vOSRrdoUPKJ8Wi2OpE/o/h9T8X1s4qSkCedbNah9ei2W2765be8iMVxQUsvgT7zIAT2eIa9fs5+vtg=="], "pg-connection-string": ["pg-connection-string@2.9.0", "", {}, "sha512-P2DEBKuvh5RClafLngkAuGe9OUlFV7ebu8w1kmaaOgPcpJd1RIFh7otETfI6hAR8YupOLFTY7nuvvIn7PLciUQ=="], @@ -2316,8 +2327,6 @@ "semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], - "serialize-error": ["serialize-error@8.1.0", "", { "dependencies": { "type-fest": "^0.20.2" } }, "sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ=="], - "serialize-javascript": ["serialize-javascript@6.0.2", "", { "dependencies": { "randombytes": "^2.1.0" } }, "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g=="], "set-cookie-parser": ["set-cookie-parser@2.7.1", "", {}, "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ=="], @@ -2732,8 +2741,6 @@ "restore-cursor/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], - "serialize-error/type-fest": ["type-fest@0.20.2", "", {}, "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ=="], - "socket.io/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="], "socket.io-adapter/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="], diff --git a/package.json b/package.json index 44159d4d5..6e78223a0 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "email:dev": "bun run --cwd packages/email dev --port 3001", "// Parallel execution scripts": "", - "dx": "dotenv -- turbo run dev db:studio email:dev --parallel", + "dx": "dotenv -- turbo run dev db:studio email:dev jobs:dev --parallel", "lint:all": "dotenv -- turbo run lint check-types format --parallel", "clean": "dotenv -- turbo run clean && rm -rf node_modules/.cache", "fresh": "bun run clean && bun install && bun run db:generate" diff --git a/packages/db/migrations/0003_small_emma_frost.sql b/packages/db/migrations/0003_small_emma_frost.sql new file mode 100644 index 000000000..d350ccbc4 --- /dev/null +++ b/packages/db/migrations/0003_small_emma_frost.sql @@ -0,0 +1,16 @@ +CREATE TABLE "cap_job_queue" ( + "id" varchar(128) PRIMARY KEY NOT NULL, + "type" varchar(100) NOT NULL, + "payload" json NOT NULL, + "status" varchar(20) DEFAULT 'pending' NOT NULL, + "attempts" integer DEFAULT 0 NOT NULL, + "max_attempts" integer DEFAULT 3 NOT NULL, + "priority" integer DEFAULT 0 NOT NULL, + "scheduled_for" timestamp DEFAULT now() NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL, + "processed_at" timestamp, + "failed_at" timestamp, + "error" varchar(1000), + "retry_delay" integer DEFAULT 1000 NOT NULL +); diff --git a/packages/db/migrations/0004_moaning_red_hulk.sql b/packages/db/migrations/0004_moaning_red_hulk.sql new file mode 100644 index 000000000..edc62aa47 --- /dev/null +++ b/packages/db/migrations/0004_moaning_red_hulk.sql @@ -0,0 +1,6 @@ +CREATE INDEX "job_processing_idx" ON "cap_job_queue" USING btree ("status","scheduled_for","attempts");--> statement-breakpoint +CREATE INDEX "priority_ordering_idx" ON "cap_job_queue" USING btree ("priority" DESC NULLS LAST,"created_at");--> statement-breakpoint +CREATE INDEX "cleanup_idx" ON "cap_job_queue" USING btree ("status","created_at");--> statement-breakpoint +CREATE INDEX "status_idx" ON "cap_job_queue" USING btree ("status");--> statement-breakpoint +CREATE INDEX "type_idx" ON "cap_job_queue" USING btree ("type");--> statement-breakpoint +CREATE INDEX "failed_jobs_idx" ON "cap_job_queue" USING btree ("status","failed_at" DESC NULLS LAST); \ No newline at end of file diff --git a/packages/db/migrations/meta/0003_snapshot.json b/packages/db/migrations/meta/0003_snapshot.json new file mode 100644 index 000000000..9962bbc86 --- /dev/null +++ b/packages/db/migrations/meta/0003_snapshot.json @@ -0,0 +1,4918 @@ +{ + "id": "7ed962da-2914-4589-a951-0df9c3bd1ec4", + "prevId": "5dc53db0-8120-4c40-a770-a68445ff1ae4", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.cap_better_auth_accounts": { + "name": "cap_better_auth_accounts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "cap_better_auth_accounts_user_id_cap_better_auth_users_id_fk": { + "name": "cap_better_auth_accounts_user_id_cap_better_auth_users_id_fk", + "tableFrom": "cap_better_auth_accounts", + "tableTo": "cap_better_auth_users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_better_auth_sessions": { + "name": "cap_better_auth_sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "cap_better_auth_sessions_user_id_cap_better_auth_users_id_fk": { + "name": "cap_better_auth_sessions_user_id_cap_better_auth_users_id_fk", + "tableFrom": "cap_better_auth_sessions", + "tableTo": "cap_better_auth_users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "cap_better_auth_sessions_token_unique": { + "name": "cap_better_auth_sessions_token_unique", + "nullsNotDistinct": false, + "columns": ["token"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_better_auth_users": { + "name": "cap_better_auth_users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "cap_better_auth_users_email_unique": { + "name": "cap_better_auth_users_email_unique", + "nullsNotDistinct": false, + "columns": ["email"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_better_auth_verifications": { + "name": "cap_better_auth_verifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_accounts": { + "name": "cap_accounts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "provider_account_id": { + "name": "provider_account_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "refresh_token": { + "name": "refresh_token", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "access_token": { + "name": "access_token", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "token_type": { + "name": "token_type", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "session_state": { + "name": "session_state", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "accounts_user_id_idx": { + "name": "accounts_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "accounts_provider_provider_account_id_idx": { + "name": "accounts_provider_provider_account_id_idx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_account_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_sessions": { + "name": "cap_sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "session_token": { + "name": "session_token", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "expires": { + "name": "expires", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "sessions_session_token_idx": { + "name": "sessions_session_token_idx", + "columns": [ + { + "expression": "session_token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "sessions_user_id_idx": { + "name": "sessions_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_users": { + "name": "cap_users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "email_verified": { + "name": "email_verified", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "image": { + "name": "image", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "last_signed_in": { + "name": "last_signed_in", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "identity_provider": { + "name": "identity_provider", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "users_email_idx": { + "name": "users_email_idx", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_passkeys": { + "name": "cap_passkeys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "credential_id": { + "name": "credential_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "credential_public_key": { + "name": "credential_public_key", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "counter": { + "name": "counter", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "credential_device_type": { + "name": "credential_device_type", + "type": "CredentialDeviceTypeEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "credential_backed_up": { + "name": "credential_backed_up", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "transports": { + "name": "transports", + "type": "varchar(191)[]", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "passkeys_user_id_idx": { + "name": "passkeys_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_passkey_verification_tokens": { + "name": "cap_passkey_verification_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "cap_passkey_verification_tokens_token_unique": { + "name": "cap_passkey_verification_tokens_token_unique", + "nullsNotDistinct": false, + "columns": ["token"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_password_reset_tokens": { + "name": "cap_password_reset_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "expires": { + "name": "expires", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "password_reset_tokens_email_token_idx": { + "name": "password_reset_tokens_email_token_idx", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "cap_password_reset_tokens_token_unique": { + "name": "cap_password_reset_tokens_token_unique", + "nullsNotDistinct": false, + "columns": ["token"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_verification_tokens": { + "name": "cap_verification_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "secondary_id": { + "name": "secondary_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "expires": { + "name": "expires", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "user_id": { + "name": "user_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "verification_tokens_identifier_token_idx": { + "name": "verification_tokens_identifier_token_idx", + "columns": [ + { + "expression": "identifier", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "verification_tokens_user_id_idx": { + "name": "verification_tokens_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "cap_verification_tokens_secondary_id_unique": { + "name": "cap_verification_tokens_secondary_id_unique", + "nullsNotDistinct": false, + "columns": ["secondary_id"] + }, + "cap_verification_tokens_token_unique": { + "name": "cap_verification_tokens_token_unique", + "nullsNotDistinct": false, + "columns": ["token"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_companies": { + "name": "cap_companies", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "logo": { + "name": "logo", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "public_id": { + "name": "public_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "website": { + "name": "website", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "incorporation_type": { + "name": "incorporation_type", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "incorporation_date": { + "name": "incorporation_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "incorporation_country": { + "name": "incorporation_country", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "incorporation_state": { + "name": "incorporation_state", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "street_address": { + "name": "street_address", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "city": { + "name": "city", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "state": { + "name": "state", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "zipcode": { + "name": "zipcode", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "country": { + "name": "country", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "companies_public_id_unique": { + "name": "companies_public_id_unique", + "columns": [ + { + "expression": "public_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "cap_companies_public_id_unique": { + "name": "cap_companies_public_id_unique", + "nullsNotDistinct": false, + "columns": ["public_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_bank_accounts": { + "name": "cap_bank_accounts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "beneficiary_name": { + "name": "beneficiary_name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "beneficiary_address": { + "name": "beneficiary_address", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "bank_name": { + "name": "bank_name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "bank_address": { + "name": "bank_address", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "account_number": { + "name": "account_number", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "routing_number": { + "name": "routing_number", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "account_type": { + "name": "account_type", + "type": "BankAccountTypeEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'CHECKING'" + }, + "swift_code": { + "name": "swift_code", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "primary": { + "name": "primary", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "bank_accounts_company_id_idx": { + "name": "bank_accounts_company_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_custom_roles": { + "name": "cap_custom_roles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "permissions": { + "name": "permissions", + "type": "varchar(191)[]", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "custom_roles_company_id_idx": { + "name": "custom_roles_company_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_members": { + "name": "cap_members", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "MemberStatusEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'PENDING'" + }, + "is_onboarded": { + "name": "is_onboarded", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "role": { + "name": "role", + "type": "Roles", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'ADMIN'" + }, + "work_email": { + "name": "work_email", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "last_accessed": { + "name": "last_accessed", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "custom_role_id": { + "name": "custom_role_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "members_company_id_idx": { + "name": "members_company_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "members_status_idx": { + "name": "members_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "members_user_id_idx": { + "name": "members_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "members_custom_role_id_idx": { + "name": "members_custom_role_id_idx", + "columns": [ + { + "expression": "custom_role_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "members_company_id_user_id_unique": { + "name": "members_company_id_user_id_unique", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_stakeholders": { + "name": "cap_stakeholders", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "institution_name": { + "name": "institution_name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "stakeholder_type": { + "name": "stakeholder_type", + "type": "StakeholderTypeEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'INDIVIDUAL'" + }, + "current_relationship": { + "name": "current_relationship", + "type": "StakeholderRelationshipEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'EMPLOYEE'" + }, + "tax_id": { + "name": "tax_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "street_address": { + "name": "street_address", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "city": { + "name": "city", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "zipcode": { + "name": "zipcode", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "country": { + "name": "country", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true, + "default": "'US'" + }, + "company_id": { + "name": "company_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "stakeholders_company_id_idx": { + "name": "stakeholders_company_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "cap_stakeholders_email_unique": { + "name": "cap_stakeholders_email_unique", + "nullsNotDistinct": false, + "columns": ["email"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_audits": { + "name": "cap_audits", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "summary": { + "name": "summary", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "occurred_at": { + "name": "occurred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "actor": { + "name": "actor", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "target": { + "name": "target", + "type": "jsonb[]", + "primaryKey": false, + "notNull": true + }, + "context": { + "name": "context", + "type": "jsonb", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "audits_company_id_idx": { + "name": "audits_company_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_share_classes": { + "name": "cap_share_classes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "idx": { + "name": "idx", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "class_type": { + "name": "class_type", + "type": "ShareTypeEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'COMMON'" + }, + "prefix": { + "name": "prefix", + "type": "SharePrefixEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'CS'" + }, + "initial_shares_authorized": { + "name": "initial_shares_authorized", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "board_approval_date": { + "name": "board_approval_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "stockholder_approval_date": { + "name": "stockholder_approval_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "votes_per_share": { + "name": "votes_per_share", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "par_value": { + "name": "par_value", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "price_per_share": { + "name": "price_per_share", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "seniority": { + "name": "seniority", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "conversion_rights": { + "name": "conversion_rights", + "type": "ConversionRightsEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'CONVERTS_TO_FUTURE_ROUND'" + }, + "converts_to_share_class_id": { + "name": "converts_to_share_class_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "liquidation_preference_multiple": { + "name": "liquidation_preference_multiple", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "participation_cap_multiple": { + "name": "participation_cap_multiple", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "share_classes_company_id_idx": { + "name": "share_classes_company_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "share_classes_company_id_idx_unique": { + "name": "share_classes_company_id_idx_unique", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idx", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_equity_plans": { + "name": "cap_equity_plans", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "board_approval_date": { + "name": "board_approval_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "plan_effective_date": { + "name": "plan_effective_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "initial_shares_reserved": { + "name": "initial_shares_reserved", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "default_cancellaton_behavior": { + "name": "default_cancellaton_behavior", + "type": "CancellationBehaviorEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "comments": { + "name": "comments", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "company_id": { + "name": "company_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "share_class_id": { + "name": "share_class_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "equity_plans_share_class_id_idx": { + "name": "equity_plans_share_class_id_idx", + "columns": [ + { + "expression": "share_class_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "equity_plans_company_id_idx": { + "name": "equity_plans_company_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_buckets": { + "name": "cap_buckets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "mime_type": { + "name": "mime_type", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "tags": { + "name": "tags", + "type": "varchar(191)[]", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_document_shares": { + "name": "cap_document_shares", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "link": { + "name": "link", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "public_id": { + "name": "public_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "link_expires_at": { + "name": "link_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "recipients": { + "name": "recipients", + "type": "varchar(191)[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "email_protected": { + "name": "email_protected", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "document_id": { + "name": "document_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "document_shares_document_id_idx": { + "name": "document_shares_document_id_idx", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_documents": { + "name": "cap_documents", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "public_id": { + "name": "public_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "bucket_id": { + "name": "bucket_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "uploader_id": { + "name": "uploader_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "company_id": { + "name": "company_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "share_id": { + "name": "share_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "option_id": { + "name": "option_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "safe_id": { + "name": "safe_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "convertible_note_id": { + "name": "convertible_note_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "documents_bucket_id_idx": { + "name": "documents_bucket_id_idx", + "columns": [ + { + "expression": "bucket_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "documents_uploader_id_idx": { + "name": "documents_uploader_id_idx", + "columns": [ + { + "expression": "uploader_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "documents_company_id_idx": { + "name": "documents_company_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "documents_share_id_idx": { + "name": "documents_share_id_idx", + "columns": [ + { + "expression": "share_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "documents_option_id_idx": { + "name": "documents_option_id_idx", + "columns": [ + { + "expression": "option_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "documents_safe_id_idx": { + "name": "documents_safe_id_idx", + "columns": [ + { + "expression": "safe_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "documents_convertible_note_id_idx": { + "name": "documents_convertible_note_id_idx", + "columns": [ + { + "expression": "convertible_note_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "cap_documents_public_id_unique": { + "name": "cap_documents_public_id_unique", + "nullsNotDistinct": false, + "columns": ["public_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_data_room_documents": { + "name": "cap_data_room_documents", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "data_room_id": { + "name": "data_room_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "document_id": { + "name": "document_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "data_room_documents_data_room_id_idx": { + "name": "data_room_documents_data_room_id_idx", + "columns": [ + { + "expression": "data_room_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "data_room_documents_document_id_idx": { + "name": "data_room_documents_document_id_idx", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "data_room_documents_data_room_id_document_id_unique": { + "name": "data_room_documents_data_room_id_document_id_unique", + "columns": [ + { + "expression": "data_room_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_data_room_recipients": { + "name": "cap_data_room_recipients", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "data_room_id": { + "name": "data_room_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "member_id": { + "name": "member_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "stakeholder_id": { + "name": "stakeholder_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "data_room_recipients_id_data_room_id_idx": { + "name": "data_room_recipients_id_data_room_id_idx", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "data_room_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "data_room_recipients_member_id_idx": { + "name": "data_room_recipients_member_id_idx", + "columns": [ + { + "expression": "member_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "data_room_recipients_data_room_id_idx": { + "name": "data_room_recipients_data_room_id_idx", + "columns": [ + { + "expression": "data_room_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "data_room_recipients_stakeholder_id_idx": { + "name": "data_room_recipients_stakeholder_id_idx", + "columns": [ + { + "expression": "stakeholder_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "data_room_recipients_data_room_id_email_unique": { + "name": "data_room_recipients_data_room_id_email_unique", + "columns": [ + { + "expression": "data_room_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_data_rooms": { + "name": "cap_data_rooms", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "public_id": { + "name": "public_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "public": { + "name": "public", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "company_id": { + "name": "company_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "data_rooms_public_id_idx": { + "name": "data_rooms_public_id_idx", + "columns": [ + { + "expression": "public_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "data_rooms_company_id_idx": { + "name": "data_rooms_company_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "data_rooms_company_id_name_unique": { + "name": "data_rooms_company_id_name_unique", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "cap_data_rooms_public_id_unique": { + "name": "cap_data_rooms_public_id_unique", + "nullsNotDistinct": false, + "columns": ["public_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_update_recipients": { + "name": "cap_update_recipients", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "update_id": { + "name": "update_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "member_id": { + "name": "member_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "stakeholder_id": { + "name": "stakeholder_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "update_recipients_id_update_id_idx": { + "name": "update_recipients_id_update_id_idx", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "update_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "update_recipients_member_id_idx": { + "name": "update_recipients_member_id_idx", + "columns": [ + { + "expression": "member_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "update_recipients_update_id_idx": { + "name": "update_recipients_update_id_idx", + "columns": [ + { + "expression": "update_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "update_recipients_stakeholder_id_idx": { + "name": "update_recipients_stakeholder_id_idx", + "columns": [ + { + "expression": "stakeholder_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "update_recipients_update_id_email_unique": { + "name": "update_recipients_update_id_email_unique", + "columns": [ + { + "expression": "update_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_esign_recipients": { + "name": "cap_esign_recipients", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "template_id": { + "name": "template_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "EsignRecipientStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'PENDING'" + }, + "member_id": { + "name": "member_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "esign_recipients_member_id_idx": { + "name": "esign_recipients_member_id_idx", + "columns": [ + { + "expression": "member_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "esign_recipients_template_id_idx": { + "name": "esign_recipients_template_id_idx", + "columns": [ + { + "expression": "template_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_template_fields": { + "name": "cap_template_fields", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "FieldTypes", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'TEXT'" + }, + "default_value": { + "name": "default_value", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "read_only": { + "name": "read_only", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "required": { + "name": "required", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "prefilled_value": { + "name": "prefilled_value", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "top": { + "name": "top", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "left": { + "name": "left", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "width": { + "name": "width", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "height": { + "name": "height", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "recipient_id": { + "name": "recipient_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "template_id": { + "name": "template_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "viewport_height": { + "name": "viewport_height", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "viewport_width": { + "name": "viewport_width", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "page": { + "name": "page", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "meta": { + "name": "meta", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "template_fields_template_id_idx": { + "name": "template_fields_template_id_idx", + "columns": [ + { + "expression": "template_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "template_fields_recipient_id_idx": { + "name": "template_fields_recipient_id_idx", + "columns": [ + { + "expression": "recipient_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_templates": { + "name": "cap_templates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "public_id": { + "name": "public_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "TemplateStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'DRAFT'" + }, + "ordered_delivery": { + "name": "ordered_delivery", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "message": { + "name": "message", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "bucket_id": { + "name": "bucket_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "uploader_id": { + "name": "uploader_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "completed_on": { + "name": "completed_on", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "templates_bucket_id_idx": { + "name": "templates_bucket_id_idx", + "columns": [ + { + "expression": "bucket_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "templates_uploader_id_idx": { + "name": "templates_uploader_id_idx", + "columns": [ + { + "expression": "uploader_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "templates_company_id_idx": { + "name": "templates_company_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_shares": { + "name": "cap_shares", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "status": { + "name": "status", + "type": "SecuritiesStatusEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'DRAFT'" + }, + "certificate_id": { + "name": "certificate_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "quantity": { + "name": "quantity", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "price_per_share": { + "name": "price_per_share", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "capital_contribution": { + "name": "capital_contribution", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "ip_contribution": { + "name": "ip_contribution", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "debt_cancelled": { + "name": "debt_cancelled", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "other_contributions": { + "name": "other_contributions", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "cliff_years": { + "name": "cliff_years", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "vesting_years": { + "name": "vesting_years", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "company_legends": { + "name": "company_legends", + "type": "ShareLegendsEnum[]", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "issue_date": { + "name": "issue_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "rule_144_date": { + "name": "rule_144_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "vesting_start_date": { + "name": "vesting_start_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "board_approval_date": { + "name": "board_approval_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "stakeholder_id": { + "name": "stakeholder_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "share_class_id": { + "name": "share_class_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "shares_company_id_idx": { + "name": "shares_company_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "shares_stakeholder_id_idx": { + "name": "shares_stakeholder_id_idx", + "columns": [ + { + "expression": "stakeholder_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "shares_share_class_id_idx": { + "name": "shares_share_class_id_idx", + "columns": [ + { + "expression": "share_class_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_options": { + "name": "cap_options", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "grant_id": { + "name": "grant_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "quantity": { + "name": "quantity", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "exercise_price": { + "name": "exercise_price", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "OptionTypeEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "OptionStatusEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'DRAFT'" + }, + "cliff_years": { + "name": "cliff_years", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "vesting_years": { + "name": "vesting_years", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "issue_date": { + "name": "issue_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "expiration_date": { + "name": "expiration_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "vesting_start_date": { + "name": "vesting_start_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "board_approval_date": { + "name": "board_approval_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "rule_144_date": { + "name": "rule_144_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "stakeholder_id": { + "name": "stakeholder_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "equity_plan_id": { + "name": "equity_plan_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "options_company_id_idx": { + "name": "options_company_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "options_equity_plan_id_idx": { + "name": "options_equity_plan_id_idx", + "columns": [ + { + "expression": "equity_plan_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "options_stakeholder_id_idx": { + "name": "options_stakeholder_id_idx", + "columns": [ + { + "expression": "stakeholder_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_investments": { + "name": "cap_investments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "amount": { + "name": "amount", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "shares": { + "name": "shares", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "date": { + "name": "date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "comments": { + "name": "comments", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "share_class_id": { + "name": "share_class_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "stakeholder_id": { + "name": "stakeholder_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "investments_company_id_idx": { + "name": "investments_company_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "investments_share_class_id_idx": { + "name": "investments_share_class_id_idx", + "columns": [ + { + "expression": "share_class_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "investments_stakeholder_id_idx": { + "name": "investments_stakeholder_id_idx", + "columns": [ + { + "expression": "stakeholder_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_safes": { + "name": "cap_safes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "public_id": { + "name": "public_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "SafeTypeEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'POST_MONEY'" + }, + "status": { + "name": "status", + "type": "SafeStatusEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'DRAFT'" + }, + "capital": { + "name": "capital", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "safe_template": { + "name": "safe_template", + "type": "SafeTemplateEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "safe_id": { + "name": "safe_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "valuation_cap": { + "name": "valuation_cap", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "discount_rate": { + "name": "discount_rate", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "mfn": { + "name": "mfn", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "pro_rata": { + "name": "pro_rata", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "additional_terms": { + "name": "additional_terms", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "stakeholder_id": { + "name": "stakeholder_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "issue_date": { + "name": "issue_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "board_approval_date": { + "name": "board_approval_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "safes_company_id_idx": { + "name": "safes_company_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "safes_stakeholder_id_idx": { + "name": "safes_stakeholder_id_idx", + "columns": [ + { + "expression": "stakeholder_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_convertible_notes": { + "name": "cap_convertible_notes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "public_id": { + "name": "public_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "ConvertibleStatusEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'DRAFT'" + }, + "type": { + "name": "type", + "type": "ConvertibleTypeEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'NOTE'" + }, + "capital": { + "name": "capital", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "conversion_cap": { + "name": "conversion_cap", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "discount_rate": { + "name": "discount_rate", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "mfn": { + "name": "mfn", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "additional_terms": { + "name": "additional_terms", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "interest_rate": { + "name": "interest_rate", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "interest_method": { + "name": "interest_method", + "type": "ConvertibleInterestMethodEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "interest_accrual": { + "name": "interest_accrual", + "type": "ConvertibleInterestAccrualEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "interest_payment_schedule": { + "name": "interest_payment_schedule", + "type": "ConvertibleInterestPaymentScheduleEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "stakeholder_id": { + "name": "stakeholder_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "issue_date": { + "name": "issue_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "board_approval_date": { + "name": "board_approval_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "convertible_notes_company_id_idx": { + "name": "convertible_notes_company_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "convertible_notes_stakeholder_id_idx": { + "name": "convertible_notes_stakeholder_id_idx", + "columns": [ + { + "expression": "stakeholder_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_esign_audits": { + "name": "cap_esign_audits", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "template_id": { + "name": "template_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "recipient_id": { + "name": "recipient_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "ip": { + "name": "ip", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "user_agent": { + "name": "user_agent", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "location": { + "name": "location", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "summary": { + "name": "summary", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "occurred_at": { + "name": "occurred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "esign_audits_company_id_idx": { + "name": "esign_audits_company_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "esign_audits_template_id_idx": { + "name": "esign_audits_template_id_idx", + "columns": [ + { + "expression": "template_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "esign_audits_recipient_id_idx": { + "name": "esign_audits_recipient_id_idx", + "columns": [ + { + "expression": "recipient_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_updates": { + "name": "cap_updates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "public_id": { + "name": "public_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "html": { + "name": "html", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "public": { + "name": "public", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "status": { + "name": "status", + "type": "UpdateStatusEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'DRAFT'" + }, + "author_id": { + "name": "author_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "updates_public_id_idx": { + "name": "updates_public_id_idx", + "columns": [ + { + "expression": "public_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "updates_author_id_idx": { + "name": "updates_author_id_idx", + "columns": [ + { + "expression": "author_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "updates_company_id_idx": { + "name": "updates_company_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "cap_updates_public_id_unique": { + "name": "cap_updates_public_id_unique", + "nullsNotDistinct": false, + "columns": ["public_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_billing_customers": { + "name": "cap_billing_customers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "billing_customers_company_id_idx": { + "name": "billing_customers_company_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_billing_prices": { + "name": "cap_billing_prices", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "product_id": { + "name": "product_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "unit_amount": { + "name": "unit_amount", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "currency": { + "name": "currency", + "type": "varchar(3)", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "PricingType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "interval": { + "name": "interval", + "type": "PricingPlanInterval", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "interval_count": { + "name": "interval_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "trial_period_days": { + "name": "trial_period_days", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "billing_prices_product_id_idx": { + "name": "billing_prices_product_id_idx", + "columns": [ + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_billing_products": { + "name": "cap_billing_products", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_billing_subscriptions": { + "name": "cap_billing_subscriptions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "price_id": { + "name": "price_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "quantity": { + "name": "quantity", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "SubscriptionStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "cancel_at_period_end": { + "name": "cancel_at_period_end", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "created": { + "name": "created", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "current_period_start": { + "name": "current_period_start", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "current_period_end": { + "name": "current_period_end", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "ended_at": { + "name": "ended_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "cancel_at": { + "name": "cancel_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "canceled_at": { + "name": "canceled_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "trial_start": { + "name": "trial_start", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "trial_end": { + "name": "trial_end", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "customer_id": { + "name": "customer_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "billing_subscriptions_price_id_idx": { + "name": "billing_subscriptions_price_id_idx", + "columns": [ + { + "expression": "price_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_subscriptions_customer_id_idx": { + "name": "billing_subscriptions_customer_id_idx", + "columns": [ + { + "expression": "customer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_access_tokens": { + "name": "cap_access_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "client_id": { + "name": "client_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "client_secret": { + "name": "client_secret", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "type_enum": { + "name": "type_enum", + "type": "AccessTokenType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'api'" + }, + "user_id": { + "name": "user_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_used": { + "name": "last_used", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "access_tokens_user_id_idx": { + "name": "access_tokens_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "access_tokens_type_enum_client_id_idx": { + "name": "access_tokens_type_enum_client_id_idx", + "columns": [ + { + "expression": "type_enum", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_job_queue": { + "name": "cap_job_queue", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(128)", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "max_attempts": { + "name": "max_attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 3 + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "scheduled_for": { + "name": "scheduled_for", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "processed_at": { + "name": "processed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "failed_at": { + "name": "failed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "varchar(1000)", + "primaryKey": false, + "notNull": false + }, + "retry_delay": { + "name": "retry_delay", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1000 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.AccessTokenType": { + "name": "AccessTokenType", + "schema": "public", + "values": ["sig", "doc", "api", "upd"] + }, + "public.AuditAction": { + "name": "AuditAction", + "schema": "public", + "values": ["CREATE", "UPDATE", "DELETE"] + }, + "public.BankAccountTypeEnum": { + "name": "BankAccountTypeEnum", + "schema": "public", + "values": ["CHECKING", "SAVINGS"] + }, + "public.CancellationBehaviorEnum": { + "name": "CancellationBehaviorEnum", + "schema": "public", + "values": [ + "RETIRE", + "RETURN_TO_POOL", + "HOLD_AS_CAPITAL_STOCK", + "DEFINED_PER_PLAN_SECURITY" + ] + }, + "public.ConversionRightsEnum": { + "name": "ConversionRightsEnum", + "schema": "public", + "values": ["CONVERTS_TO_FUTURE_ROUND", "CONVERTS_TO_SHARE_CLASS_ID"] + }, + "public.ConvertibleInterestAccrualEnum": { + "name": "ConvertibleInterestAccrualEnum", + "schema": "public", + "values": [ + "DAILY", + "MONTHLY", + "SEMI_ANNUALLY", + "ANNUALLY", + "YEARLY", + "CONTINUOUSLY" + ] + }, + "public.ConvertibleInterestMethodEnum": { + "name": "ConvertibleInterestMethodEnum", + "schema": "public", + "values": ["SIMPLE", "COMPOUND"] + }, + "public.ConvertibleInterestPaymentScheduleEnum": { + "name": "ConvertibleInterestPaymentScheduleEnum", + "schema": "public", + "values": ["DEFERRED", "PAY_AT_MATURITY"] + }, + "public.ConvertibleStatusEnum": { + "name": "ConvertibleStatusEnum", + "schema": "public", + "values": ["DRAFT", "ACTIVE", "PENDING", "EXPIRED", "CANCELLED"] + }, + "public.ConvertibleTypeEnum": { + "name": "ConvertibleTypeEnum", + "schema": "public", + "values": ["CCD", "OCD", "NOTE"] + }, + "public.CredentialDeviceTypeEnum": { + "name": "CredentialDeviceTypeEnum", + "schema": "public", + "values": ["SINGLE_DEVICE", "MULTI_DEVICE"] + }, + "public.EsignRecipientStatus": { + "name": "EsignRecipientStatus", + "schema": "public", + "values": ["SENT", "SIGNED", "PENDING"] + }, + "public.FieldTypes": { + "name": "FieldTypes", + "schema": "public", + "values": [ + "TEXT", + "RADIO", + "EMAIL", + "DATE", + "DATETIME", + "TEXTAREA", + "CHECKBOX", + "SIGNATURE", + "SELECT" + ] + }, + "public.MemberStatusEnum": { + "name": "MemberStatusEnum", + "schema": "public", + "values": ["ACTIVE", "INACTIVE", "PENDING"] + }, + "public.OptionStatusEnum": { + "name": "OptionStatusEnum", + "schema": "public", + "values": ["DRAFT", "ACTIVE", "EXERCISED", "EXPIRED", "CANCELLED"] + }, + "public.OptionTypeEnum": { + "name": "OptionTypeEnum", + "schema": "public", + "values": ["ISO", "NSO", "RSU"] + }, + "public.PricingPlanInterval": { + "name": "PricingPlanInterval", + "schema": "public", + "values": ["day", "week", "month", "year"] + }, + "public.PricingType": { + "name": "PricingType", + "schema": "public", + "values": ["one_time", "recurring"] + }, + "public.Roles": { + "name": "Roles", + "schema": "public", + "values": ["ADMIN", "CUSTOM"] + }, + "public.SafeStatusEnum": { + "name": "SafeStatusEnum", + "schema": "public", + "values": ["DRAFT", "ACTIVE", "PENDING", "EXPIRED", "CANCELLED"] + }, + "public.SafeTemplateEnum": { + "name": "SafeTemplateEnum", + "schema": "public", + "values": [ + "POST_MONEY_CAP", + "POST_MONEY_DISCOUNT", + "POST_MONEY_MFN", + "POST_MONEY_CAP_WITH_PRO_RATA", + "POST_MONEY_DISCOUNT_WITH_PRO_RATA", + "POST_MONEY_MFN_WITH_PRO_RATA", + "CUSTOM" + ] + }, + "public.SafeTypeEnum": { + "name": "SafeTypeEnum", + "schema": "public", + "values": ["PRE_MONEY", "POST_MONEY"] + }, + "public.SecuritiesStatusEnum": { + "name": "SecuritiesStatusEnum", + "schema": "public", + "values": ["ACTIVE", "DRAFT", "SIGNED", "PENDING"] + }, + "public.ShareLegendsEnum": { + "name": "ShareLegendsEnum", + "schema": "public", + "values": ["US_SECURITIES_ACT", "SALE_AND_ROFR", "TRANSFER_RESTRICTIONS"] + }, + "public.SharePrefixEnum": { + "name": "SharePrefixEnum", + "schema": "public", + "values": ["CS", "PS"] + }, + "public.ShareTypeEnum": { + "name": "ShareTypeEnum", + "schema": "public", + "values": ["COMMON", "PREFERRED"] + }, + "public.StakeholderRelationshipEnum": { + "name": "StakeholderRelationshipEnum", + "schema": "public", + "values": [ + "ADVISOR", + "BOARD_MEMBER", + "CONSULTANT", + "EMPLOYEE", + "EX_ADVISOR", + "EX_CONSULTANT", + "EX_EMPLOYEE", + "EXECUTIVE", + "FOUNDER", + "INVESTOR", + "NON_US_EMPLOYEE", + "OFFICER", + "OTHER" + ] + }, + "public.StakeholderTypeEnum": { + "name": "StakeholderTypeEnum", + "schema": "public", + "values": ["INDIVIDUAL", "INSTITUTION"] + }, + "public.SubscriptionStatus": { + "name": "SubscriptionStatus", + "schema": "public", + "values": [ + "trialing", + "active", + "canceled", + "incomplete", + "incomplete_expired", + "past_due", + "unpaid", + "paused" + ] + }, + "public.TemplateStatus": { + "name": "TemplateStatus", + "schema": "public", + "values": ["DRAFT", "COMPLETE", "SENT", "WAITING", "CANCELLED"] + }, + "public.UpdateStatusEnum": { + "name": "UpdateStatusEnum", + "schema": "public", + "values": ["DRAFT", "PUBLIC", "PRIVATE"] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/packages/db/migrations/meta/0004_snapshot.json b/packages/db/migrations/meta/0004_snapshot.json new file mode 100644 index 000000000..a65e21149 --- /dev/null +++ b/packages/db/migrations/meta/0004_snapshot.json @@ -0,0 +1,5039 @@ +{ + "id": "3ac57fb9-e8fa-4294-8e32-0cd048b6ed49", + "prevId": "7ed962da-2914-4589-a951-0df9c3bd1ec4", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.cap_better_auth_accounts": { + "name": "cap_better_auth_accounts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "cap_better_auth_accounts_user_id_cap_better_auth_users_id_fk": { + "name": "cap_better_auth_accounts_user_id_cap_better_auth_users_id_fk", + "tableFrom": "cap_better_auth_accounts", + "tableTo": "cap_better_auth_users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_better_auth_sessions": { + "name": "cap_better_auth_sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "cap_better_auth_sessions_user_id_cap_better_auth_users_id_fk": { + "name": "cap_better_auth_sessions_user_id_cap_better_auth_users_id_fk", + "tableFrom": "cap_better_auth_sessions", + "tableTo": "cap_better_auth_users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "cap_better_auth_sessions_token_unique": { + "name": "cap_better_auth_sessions_token_unique", + "nullsNotDistinct": false, + "columns": ["token"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_better_auth_users": { + "name": "cap_better_auth_users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "cap_better_auth_users_email_unique": { + "name": "cap_better_auth_users_email_unique", + "nullsNotDistinct": false, + "columns": ["email"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_better_auth_verifications": { + "name": "cap_better_auth_verifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_accounts": { + "name": "cap_accounts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "provider_account_id": { + "name": "provider_account_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "refresh_token": { + "name": "refresh_token", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "access_token": { + "name": "access_token", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "token_type": { + "name": "token_type", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "session_state": { + "name": "session_state", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "accounts_user_id_idx": { + "name": "accounts_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "accounts_provider_provider_account_id_idx": { + "name": "accounts_provider_provider_account_id_idx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_account_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_sessions": { + "name": "cap_sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "session_token": { + "name": "session_token", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "expires": { + "name": "expires", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "sessions_session_token_idx": { + "name": "sessions_session_token_idx", + "columns": [ + { + "expression": "session_token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "sessions_user_id_idx": { + "name": "sessions_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_users": { + "name": "cap_users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "email_verified": { + "name": "email_verified", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "image": { + "name": "image", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "last_signed_in": { + "name": "last_signed_in", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "identity_provider": { + "name": "identity_provider", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "users_email_idx": { + "name": "users_email_idx", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_passkeys": { + "name": "cap_passkeys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "credential_id": { + "name": "credential_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "credential_public_key": { + "name": "credential_public_key", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "counter": { + "name": "counter", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "credential_device_type": { + "name": "credential_device_type", + "type": "CredentialDeviceTypeEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "credential_backed_up": { + "name": "credential_backed_up", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "transports": { + "name": "transports", + "type": "varchar(191)[]", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "passkeys_user_id_idx": { + "name": "passkeys_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_passkey_verification_tokens": { + "name": "cap_passkey_verification_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "cap_passkey_verification_tokens_token_unique": { + "name": "cap_passkey_verification_tokens_token_unique", + "nullsNotDistinct": false, + "columns": ["token"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_password_reset_tokens": { + "name": "cap_password_reset_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "expires": { + "name": "expires", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "password_reset_tokens_email_token_idx": { + "name": "password_reset_tokens_email_token_idx", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "cap_password_reset_tokens_token_unique": { + "name": "cap_password_reset_tokens_token_unique", + "nullsNotDistinct": false, + "columns": ["token"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_verification_tokens": { + "name": "cap_verification_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "secondary_id": { + "name": "secondary_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "expires": { + "name": "expires", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "user_id": { + "name": "user_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "verification_tokens_identifier_token_idx": { + "name": "verification_tokens_identifier_token_idx", + "columns": [ + { + "expression": "identifier", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "verification_tokens_user_id_idx": { + "name": "verification_tokens_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "cap_verification_tokens_secondary_id_unique": { + "name": "cap_verification_tokens_secondary_id_unique", + "nullsNotDistinct": false, + "columns": ["secondary_id"] + }, + "cap_verification_tokens_token_unique": { + "name": "cap_verification_tokens_token_unique", + "nullsNotDistinct": false, + "columns": ["token"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_companies": { + "name": "cap_companies", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "logo": { + "name": "logo", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "public_id": { + "name": "public_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "website": { + "name": "website", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "incorporation_type": { + "name": "incorporation_type", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "incorporation_date": { + "name": "incorporation_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "incorporation_country": { + "name": "incorporation_country", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "incorporation_state": { + "name": "incorporation_state", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "street_address": { + "name": "street_address", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "city": { + "name": "city", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "state": { + "name": "state", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "zipcode": { + "name": "zipcode", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "country": { + "name": "country", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "companies_public_id_unique": { + "name": "companies_public_id_unique", + "columns": [ + { + "expression": "public_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "cap_companies_public_id_unique": { + "name": "cap_companies_public_id_unique", + "nullsNotDistinct": false, + "columns": ["public_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_bank_accounts": { + "name": "cap_bank_accounts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "beneficiary_name": { + "name": "beneficiary_name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "beneficiary_address": { + "name": "beneficiary_address", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "bank_name": { + "name": "bank_name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "bank_address": { + "name": "bank_address", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "account_number": { + "name": "account_number", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "routing_number": { + "name": "routing_number", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "account_type": { + "name": "account_type", + "type": "BankAccountTypeEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'CHECKING'" + }, + "swift_code": { + "name": "swift_code", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "primary": { + "name": "primary", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "bank_accounts_company_id_idx": { + "name": "bank_accounts_company_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_custom_roles": { + "name": "cap_custom_roles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "permissions": { + "name": "permissions", + "type": "varchar(191)[]", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "custom_roles_company_id_idx": { + "name": "custom_roles_company_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_members": { + "name": "cap_members", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "MemberStatusEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'PENDING'" + }, + "is_onboarded": { + "name": "is_onboarded", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "role": { + "name": "role", + "type": "Roles", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'ADMIN'" + }, + "work_email": { + "name": "work_email", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "last_accessed": { + "name": "last_accessed", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "custom_role_id": { + "name": "custom_role_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "members_company_id_idx": { + "name": "members_company_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "members_status_idx": { + "name": "members_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "members_user_id_idx": { + "name": "members_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "members_custom_role_id_idx": { + "name": "members_custom_role_id_idx", + "columns": [ + { + "expression": "custom_role_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "members_company_id_user_id_unique": { + "name": "members_company_id_user_id_unique", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_stakeholders": { + "name": "cap_stakeholders", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "institution_name": { + "name": "institution_name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "stakeholder_type": { + "name": "stakeholder_type", + "type": "StakeholderTypeEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'INDIVIDUAL'" + }, + "current_relationship": { + "name": "current_relationship", + "type": "StakeholderRelationshipEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'EMPLOYEE'" + }, + "tax_id": { + "name": "tax_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "street_address": { + "name": "street_address", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "city": { + "name": "city", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "zipcode": { + "name": "zipcode", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "country": { + "name": "country", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true, + "default": "'US'" + }, + "company_id": { + "name": "company_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "stakeholders_company_id_idx": { + "name": "stakeholders_company_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "cap_stakeholders_email_unique": { + "name": "cap_stakeholders_email_unique", + "nullsNotDistinct": false, + "columns": ["email"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_audits": { + "name": "cap_audits", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "summary": { + "name": "summary", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "occurred_at": { + "name": "occurred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "actor": { + "name": "actor", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "target": { + "name": "target", + "type": "jsonb[]", + "primaryKey": false, + "notNull": true + }, + "context": { + "name": "context", + "type": "jsonb", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "audits_company_id_idx": { + "name": "audits_company_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_share_classes": { + "name": "cap_share_classes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "idx": { + "name": "idx", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "class_type": { + "name": "class_type", + "type": "ShareTypeEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'COMMON'" + }, + "prefix": { + "name": "prefix", + "type": "SharePrefixEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'CS'" + }, + "initial_shares_authorized": { + "name": "initial_shares_authorized", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "board_approval_date": { + "name": "board_approval_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "stockholder_approval_date": { + "name": "stockholder_approval_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "votes_per_share": { + "name": "votes_per_share", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "par_value": { + "name": "par_value", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "price_per_share": { + "name": "price_per_share", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "seniority": { + "name": "seniority", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "conversion_rights": { + "name": "conversion_rights", + "type": "ConversionRightsEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'CONVERTS_TO_FUTURE_ROUND'" + }, + "converts_to_share_class_id": { + "name": "converts_to_share_class_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "liquidation_preference_multiple": { + "name": "liquidation_preference_multiple", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "participation_cap_multiple": { + "name": "participation_cap_multiple", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "share_classes_company_id_idx": { + "name": "share_classes_company_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "share_classes_company_id_idx_unique": { + "name": "share_classes_company_id_idx_unique", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idx", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_equity_plans": { + "name": "cap_equity_plans", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "board_approval_date": { + "name": "board_approval_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "plan_effective_date": { + "name": "plan_effective_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "initial_shares_reserved": { + "name": "initial_shares_reserved", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "default_cancellaton_behavior": { + "name": "default_cancellaton_behavior", + "type": "CancellationBehaviorEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "comments": { + "name": "comments", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "company_id": { + "name": "company_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "share_class_id": { + "name": "share_class_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "equity_plans_share_class_id_idx": { + "name": "equity_plans_share_class_id_idx", + "columns": [ + { + "expression": "share_class_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "equity_plans_company_id_idx": { + "name": "equity_plans_company_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_buckets": { + "name": "cap_buckets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "mime_type": { + "name": "mime_type", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "tags": { + "name": "tags", + "type": "varchar(191)[]", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_document_shares": { + "name": "cap_document_shares", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "link": { + "name": "link", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "public_id": { + "name": "public_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "link_expires_at": { + "name": "link_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "recipients": { + "name": "recipients", + "type": "varchar(191)[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "email_protected": { + "name": "email_protected", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "document_id": { + "name": "document_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "document_shares_document_id_idx": { + "name": "document_shares_document_id_idx", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_documents": { + "name": "cap_documents", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "public_id": { + "name": "public_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "bucket_id": { + "name": "bucket_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "uploader_id": { + "name": "uploader_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "company_id": { + "name": "company_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "share_id": { + "name": "share_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "option_id": { + "name": "option_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "safe_id": { + "name": "safe_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "convertible_note_id": { + "name": "convertible_note_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "documents_bucket_id_idx": { + "name": "documents_bucket_id_idx", + "columns": [ + { + "expression": "bucket_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "documents_uploader_id_idx": { + "name": "documents_uploader_id_idx", + "columns": [ + { + "expression": "uploader_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "documents_company_id_idx": { + "name": "documents_company_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "documents_share_id_idx": { + "name": "documents_share_id_idx", + "columns": [ + { + "expression": "share_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "documents_option_id_idx": { + "name": "documents_option_id_idx", + "columns": [ + { + "expression": "option_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "documents_safe_id_idx": { + "name": "documents_safe_id_idx", + "columns": [ + { + "expression": "safe_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "documents_convertible_note_id_idx": { + "name": "documents_convertible_note_id_idx", + "columns": [ + { + "expression": "convertible_note_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "cap_documents_public_id_unique": { + "name": "cap_documents_public_id_unique", + "nullsNotDistinct": false, + "columns": ["public_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_data_room_documents": { + "name": "cap_data_room_documents", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "data_room_id": { + "name": "data_room_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "document_id": { + "name": "document_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "data_room_documents_data_room_id_idx": { + "name": "data_room_documents_data_room_id_idx", + "columns": [ + { + "expression": "data_room_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "data_room_documents_document_id_idx": { + "name": "data_room_documents_document_id_idx", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "data_room_documents_data_room_id_document_id_unique": { + "name": "data_room_documents_data_room_id_document_id_unique", + "columns": [ + { + "expression": "data_room_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_data_room_recipients": { + "name": "cap_data_room_recipients", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "data_room_id": { + "name": "data_room_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "member_id": { + "name": "member_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "stakeholder_id": { + "name": "stakeholder_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "data_room_recipients_id_data_room_id_idx": { + "name": "data_room_recipients_id_data_room_id_idx", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "data_room_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "data_room_recipients_member_id_idx": { + "name": "data_room_recipients_member_id_idx", + "columns": [ + { + "expression": "member_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "data_room_recipients_data_room_id_idx": { + "name": "data_room_recipients_data_room_id_idx", + "columns": [ + { + "expression": "data_room_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "data_room_recipients_stakeholder_id_idx": { + "name": "data_room_recipients_stakeholder_id_idx", + "columns": [ + { + "expression": "stakeholder_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "data_room_recipients_data_room_id_email_unique": { + "name": "data_room_recipients_data_room_id_email_unique", + "columns": [ + { + "expression": "data_room_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_data_rooms": { + "name": "cap_data_rooms", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "public_id": { + "name": "public_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "public": { + "name": "public", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "company_id": { + "name": "company_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "data_rooms_public_id_idx": { + "name": "data_rooms_public_id_idx", + "columns": [ + { + "expression": "public_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "data_rooms_company_id_idx": { + "name": "data_rooms_company_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "data_rooms_company_id_name_unique": { + "name": "data_rooms_company_id_name_unique", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "cap_data_rooms_public_id_unique": { + "name": "cap_data_rooms_public_id_unique", + "nullsNotDistinct": false, + "columns": ["public_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_update_recipients": { + "name": "cap_update_recipients", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "update_id": { + "name": "update_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "member_id": { + "name": "member_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "stakeholder_id": { + "name": "stakeholder_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "update_recipients_id_update_id_idx": { + "name": "update_recipients_id_update_id_idx", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "update_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "update_recipients_member_id_idx": { + "name": "update_recipients_member_id_idx", + "columns": [ + { + "expression": "member_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "update_recipients_update_id_idx": { + "name": "update_recipients_update_id_idx", + "columns": [ + { + "expression": "update_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "update_recipients_stakeholder_id_idx": { + "name": "update_recipients_stakeholder_id_idx", + "columns": [ + { + "expression": "stakeholder_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "update_recipients_update_id_email_unique": { + "name": "update_recipients_update_id_email_unique", + "columns": [ + { + "expression": "update_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_esign_recipients": { + "name": "cap_esign_recipients", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "template_id": { + "name": "template_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "EsignRecipientStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'PENDING'" + }, + "member_id": { + "name": "member_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "esign_recipients_member_id_idx": { + "name": "esign_recipients_member_id_idx", + "columns": [ + { + "expression": "member_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "esign_recipients_template_id_idx": { + "name": "esign_recipients_template_id_idx", + "columns": [ + { + "expression": "template_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_template_fields": { + "name": "cap_template_fields", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "FieldTypes", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'TEXT'" + }, + "default_value": { + "name": "default_value", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "read_only": { + "name": "read_only", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "required": { + "name": "required", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "prefilled_value": { + "name": "prefilled_value", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "top": { + "name": "top", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "left": { + "name": "left", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "width": { + "name": "width", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "height": { + "name": "height", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "recipient_id": { + "name": "recipient_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "template_id": { + "name": "template_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "viewport_height": { + "name": "viewport_height", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "viewport_width": { + "name": "viewport_width", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "page": { + "name": "page", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "meta": { + "name": "meta", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "template_fields_template_id_idx": { + "name": "template_fields_template_id_idx", + "columns": [ + { + "expression": "template_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "template_fields_recipient_id_idx": { + "name": "template_fields_recipient_id_idx", + "columns": [ + { + "expression": "recipient_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_templates": { + "name": "cap_templates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "public_id": { + "name": "public_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "TemplateStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'DRAFT'" + }, + "ordered_delivery": { + "name": "ordered_delivery", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "message": { + "name": "message", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "bucket_id": { + "name": "bucket_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "uploader_id": { + "name": "uploader_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "completed_on": { + "name": "completed_on", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "templates_bucket_id_idx": { + "name": "templates_bucket_id_idx", + "columns": [ + { + "expression": "bucket_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "templates_uploader_id_idx": { + "name": "templates_uploader_id_idx", + "columns": [ + { + "expression": "uploader_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "templates_company_id_idx": { + "name": "templates_company_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_shares": { + "name": "cap_shares", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "status": { + "name": "status", + "type": "SecuritiesStatusEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'DRAFT'" + }, + "certificate_id": { + "name": "certificate_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "quantity": { + "name": "quantity", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "price_per_share": { + "name": "price_per_share", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "capital_contribution": { + "name": "capital_contribution", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "ip_contribution": { + "name": "ip_contribution", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "debt_cancelled": { + "name": "debt_cancelled", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "other_contributions": { + "name": "other_contributions", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "cliff_years": { + "name": "cliff_years", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "vesting_years": { + "name": "vesting_years", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "company_legends": { + "name": "company_legends", + "type": "ShareLegendsEnum[]", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "issue_date": { + "name": "issue_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "rule_144_date": { + "name": "rule_144_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "vesting_start_date": { + "name": "vesting_start_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "board_approval_date": { + "name": "board_approval_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "stakeholder_id": { + "name": "stakeholder_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "share_class_id": { + "name": "share_class_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "shares_company_id_idx": { + "name": "shares_company_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "shares_stakeholder_id_idx": { + "name": "shares_stakeholder_id_idx", + "columns": [ + { + "expression": "stakeholder_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "shares_share_class_id_idx": { + "name": "shares_share_class_id_idx", + "columns": [ + { + "expression": "share_class_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_options": { + "name": "cap_options", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "grant_id": { + "name": "grant_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "quantity": { + "name": "quantity", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "exercise_price": { + "name": "exercise_price", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "OptionTypeEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "OptionStatusEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'DRAFT'" + }, + "cliff_years": { + "name": "cliff_years", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "vesting_years": { + "name": "vesting_years", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "issue_date": { + "name": "issue_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "expiration_date": { + "name": "expiration_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "vesting_start_date": { + "name": "vesting_start_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "board_approval_date": { + "name": "board_approval_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "rule_144_date": { + "name": "rule_144_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "stakeholder_id": { + "name": "stakeholder_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "equity_plan_id": { + "name": "equity_plan_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "options_company_id_idx": { + "name": "options_company_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "options_equity_plan_id_idx": { + "name": "options_equity_plan_id_idx", + "columns": [ + { + "expression": "equity_plan_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "options_stakeholder_id_idx": { + "name": "options_stakeholder_id_idx", + "columns": [ + { + "expression": "stakeholder_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_investments": { + "name": "cap_investments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "amount": { + "name": "amount", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "shares": { + "name": "shares", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "date": { + "name": "date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "comments": { + "name": "comments", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "share_class_id": { + "name": "share_class_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "stakeholder_id": { + "name": "stakeholder_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "investments_company_id_idx": { + "name": "investments_company_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "investments_share_class_id_idx": { + "name": "investments_share_class_id_idx", + "columns": [ + { + "expression": "share_class_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "investments_stakeholder_id_idx": { + "name": "investments_stakeholder_id_idx", + "columns": [ + { + "expression": "stakeholder_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_safes": { + "name": "cap_safes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "public_id": { + "name": "public_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "SafeTypeEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'POST_MONEY'" + }, + "status": { + "name": "status", + "type": "SafeStatusEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'DRAFT'" + }, + "capital": { + "name": "capital", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "safe_template": { + "name": "safe_template", + "type": "SafeTemplateEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "safe_id": { + "name": "safe_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "valuation_cap": { + "name": "valuation_cap", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "discount_rate": { + "name": "discount_rate", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "mfn": { + "name": "mfn", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "pro_rata": { + "name": "pro_rata", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "additional_terms": { + "name": "additional_terms", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "stakeholder_id": { + "name": "stakeholder_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "issue_date": { + "name": "issue_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "board_approval_date": { + "name": "board_approval_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "safes_company_id_idx": { + "name": "safes_company_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "safes_stakeholder_id_idx": { + "name": "safes_stakeholder_id_idx", + "columns": [ + { + "expression": "stakeholder_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_convertible_notes": { + "name": "cap_convertible_notes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "public_id": { + "name": "public_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "ConvertibleStatusEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'DRAFT'" + }, + "type": { + "name": "type", + "type": "ConvertibleTypeEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'NOTE'" + }, + "capital": { + "name": "capital", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "conversion_cap": { + "name": "conversion_cap", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "discount_rate": { + "name": "discount_rate", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "mfn": { + "name": "mfn", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "additional_terms": { + "name": "additional_terms", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "interest_rate": { + "name": "interest_rate", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "interest_method": { + "name": "interest_method", + "type": "ConvertibleInterestMethodEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "interest_accrual": { + "name": "interest_accrual", + "type": "ConvertibleInterestAccrualEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "interest_payment_schedule": { + "name": "interest_payment_schedule", + "type": "ConvertibleInterestPaymentScheduleEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "stakeholder_id": { + "name": "stakeholder_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "issue_date": { + "name": "issue_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "board_approval_date": { + "name": "board_approval_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "convertible_notes_company_id_idx": { + "name": "convertible_notes_company_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "convertible_notes_stakeholder_id_idx": { + "name": "convertible_notes_stakeholder_id_idx", + "columns": [ + { + "expression": "stakeholder_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_esign_audits": { + "name": "cap_esign_audits", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "template_id": { + "name": "template_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "recipient_id": { + "name": "recipient_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "ip": { + "name": "ip", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "user_agent": { + "name": "user_agent", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "location": { + "name": "location", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "summary": { + "name": "summary", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "occurred_at": { + "name": "occurred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "esign_audits_company_id_idx": { + "name": "esign_audits_company_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "esign_audits_template_id_idx": { + "name": "esign_audits_template_id_idx", + "columns": [ + { + "expression": "template_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "esign_audits_recipient_id_idx": { + "name": "esign_audits_recipient_id_idx", + "columns": [ + { + "expression": "recipient_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_updates": { + "name": "cap_updates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "public_id": { + "name": "public_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "html": { + "name": "html", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "public": { + "name": "public", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "status": { + "name": "status", + "type": "UpdateStatusEnum", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'DRAFT'" + }, + "author_id": { + "name": "author_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "updates_public_id_idx": { + "name": "updates_public_id_idx", + "columns": [ + { + "expression": "public_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "updates_author_id_idx": { + "name": "updates_author_id_idx", + "columns": [ + { + "expression": "author_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "updates_company_id_idx": { + "name": "updates_company_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "cap_updates_public_id_unique": { + "name": "cap_updates_public_id_unique", + "nullsNotDistinct": false, + "columns": ["public_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_billing_customers": { + "name": "cap_billing_customers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "company_id": { + "name": "company_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "billing_customers_company_id_idx": { + "name": "billing_customers_company_id_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_billing_prices": { + "name": "cap_billing_prices", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "product_id": { + "name": "product_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "unit_amount": { + "name": "unit_amount", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "currency": { + "name": "currency", + "type": "varchar(3)", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "PricingType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "interval": { + "name": "interval", + "type": "PricingPlanInterval", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "interval_count": { + "name": "interval_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "trial_period_days": { + "name": "trial_period_days", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "billing_prices_product_id_idx": { + "name": "billing_prices_product_id_idx", + "columns": [ + { + "expression": "product_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_billing_products": { + "name": "cap_billing_products", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "varchar(191)", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_billing_subscriptions": { + "name": "cap_billing_subscriptions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "price_id": { + "name": "price_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "quantity": { + "name": "quantity", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "SubscriptionStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "cancel_at_period_end": { + "name": "cancel_at_period_end", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "created": { + "name": "created", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "current_period_start": { + "name": "current_period_start", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "current_period_end": { + "name": "current_period_end", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "ended_at": { + "name": "ended_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "cancel_at": { + "name": "cancel_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "canceled_at": { + "name": "canceled_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "trial_start": { + "name": "trial_start", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "trial_end": { + "name": "trial_end", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "customer_id": { + "name": "customer_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "billing_subscriptions_price_id_idx": { + "name": "billing_subscriptions_price_id_idx", + "columns": [ + { + "expression": "price_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "billing_subscriptions_customer_id_idx": { + "name": "billing_subscriptions_customer_id_idx", + "columns": [ + { + "expression": "customer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_access_tokens": { + "name": "cap_access_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(191)", + "primaryKey": true, + "notNull": true + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "client_id": { + "name": "client_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "client_secret": { + "name": "client_secret", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "type_enum": { + "name": "type_enum", + "type": "AccessTokenType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'api'" + }, + "user_id": { + "name": "user_id", + "type": "varchar(191)", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_used": { + "name": "last_used", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "access_tokens_user_id_idx": { + "name": "access_tokens_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "access_tokens_type_enum_client_id_idx": { + "name": "access_tokens_type_enum_client_id_idx", + "columns": [ + { + "expression": "type_enum", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cap_job_queue": { + "name": "cap_job_queue", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(128)", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "max_attempts": { + "name": "max_attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 3 + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "scheduled_for": { + "name": "scheduled_for", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "processed_at": { + "name": "processed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "failed_at": { + "name": "failed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "varchar(1000)", + "primaryKey": false, + "notNull": false + }, + "retry_delay": { + "name": "retry_delay", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1000 + } + }, + "indexes": { + "job_processing_idx": { + "name": "job_processing_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scheduled_for", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "attempts", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "priority_ordering_idx": { + "name": "priority_ordering_idx", + "columns": [ + { + "expression": "priority", + "isExpression": false, + "asc": false, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "cleanup_idx": { + "name": "cleanup_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "status_idx": { + "name": "status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "type_idx": { + "name": "type_idx", + "columns": [ + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "failed_jobs_idx": { + "name": "failed_jobs_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "failed_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.AccessTokenType": { + "name": "AccessTokenType", + "schema": "public", + "values": ["sig", "doc", "api", "upd"] + }, + "public.AuditAction": { + "name": "AuditAction", + "schema": "public", + "values": ["CREATE", "UPDATE", "DELETE"] + }, + "public.BankAccountTypeEnum": { + "name": "BankAccountTypeEnum", + "schema": "public", + "values": ["CHECKING", "SAVINGS"] + }, + "public.CancellationBehaviorEnum": { + "name": "CancellationBehaviorEnum", + "schema": "public", + "values": [ + "RETIRE", + "RETURN_TO_POOL", + "HOLD_AS_CAPITAL_STOCK", + "DEFINED_PER_PLAN_SECURITY" + ] + }, + "public.ConversionRightsEnum": { + "name": "ConversionRightsEnum", + "schema": "public", + "values": ["CONVERTS_TO_FUTURE_ROUND", "CONVERTS_TO_SHARE_CLASS_ID"] + }, + "public.ConvertibleInterestAccrualEnum": { + "name": "ConvertibleInterestAccrualEnum", + "schema": "public", + "values": [ + "DAILY", + "MONTHLY", + "SEMI_ANNUALLY", + "ANNUALLY", + "YEARLY", + "CONTINUOUSLY" + ] + }, + "public.ConvertibleInterestMethodEnum": { + "name": "ConvertibleInterestMethodEnum", + "schema": "public", + "values": ["SIMPLE", "COMPOUND"] + }, + "public.ConvertibleInterestPaymentScheduleEnum": { + "name": "ConvertibleInterestPaymentScheduleEnum", + "schema": "public", + "values": ["DEFERRED", "PAY_AT_MATURITY"] + }, + "public.ConvertibleStatusEnum": { + "name": "ConvertibleStatusEnum", + "schema": "public", + "values": ["DRAFT", "ACTIVE", "PENDING", "EXPIRED", "CANCELLED"] + }, + "public.ConvertibleTypeEnum": { + "name": "ConvertibleTypeEnum", + "schema": "public", + "values": ["CCD", "OCD", "NOTE"] + }, + "public.CredentialDeviceTypeEnum": { + "name": "CredentialDeviceTypeEnum", + "schema": "public", + "values": ["SINGLE_DEVICE", "MULTI_DEVICE"] + }, + "public.EsignRecipientStatus": { + "name": "EsignRecipientStatus", + "schema": "public", + "values": ["SENT", "SIGNED", "PENDING"] + }, + "public.FieldTypes": { + "name": "FieldTypes", + "schema": "public", + "values": [ + "TEXT", + "RADIO", + "EMAIL", + "DATE", + "DATETIME", + "TEXTAREA", + "CHECKBOX", + "SIGNATURE", + "SELECT" + ] + }, + "public.MemberStatusEnum": { + "name": "MemberStatusEnum", + "schema": "public", + "values": ["ACTIVE", "INACTIVE", "PENDING"] + }, + "public.OptionStatusEnum": { + "name": "OptionStatusEnum", + "schema": "public", + "values": ["DRAFT", "ACTIVE", "EXERCISED", "EXPIRED", "CANCELLED"] + }, + "public.OptionTypeEnum": { + "name": "OptionTypeEnum", + "schema": "public", + "values": ["ISO", "NSO", "RSU"] + }, + "public.PricingPlanInterval": { + "name": "PricingPlanInterval", + "schema": "public", + "values": ["day", "week", "month", "year"] + }, + "public.PricingType": { + "name": "PricingType", + "schema": "public", + "values": ["one_time", "recurring"] + }, + "public.Roles": { + "name": "Roles", + "schema": "public", + "values": ["ADMIN", "CUSTOM"] + }, + "public.SafeStatusEnum": { + "name": "SafeStatusEnum", + "schema": "public", + "values": ["DRAFT", "ACTIVE", "PENDING", "EXPIRED", "CANCELLED"] + }, + "public.SafeTemplateEnum": { + "name": "SafeTemplateEnum", + "schema": "public", + "values": [ + "POST_MONEY_CAP", + "POST_MONEY_DISCOUNT", + "POST_MONEY_MFN", + "POST_MONEY_CAP_WITH_PRO_RATA", + "POST_MONEY_DISCOUNT_WITH_PRO_RATA", + "POST_MONEY_MFN_WITH_PRO_RATA", + "CUSTOM" + ] + }, + "public.SafeTypeEnum": { + "name": "SafeTypeEnum", + "schema": "public", + "values": ["PRE_MONEY", "POST_MONEY"] + }, + "public.SecuritiesStatusEnum": { + "name": "SecuritiesStatusEnum", + "schema": "public", + "values": ["ACTIVE", "DRAFT", "SIGNED", "PENDING"] + }, + "public.ShareLegendsEnum": { + "name": "ShareLegendsEnum", + "schema": "public", + "values": ["US_SECURITIES_ACT", "SALE_AND_ROFR", "TRANSFER_RESTRICTIONS"] + }, + "public.SharePrefixEnum": { + "name": "SharePrefixEnum", + "schema": "public", + "values": ["CS", "PS"] + }, + "public.ShareTypeEnum": { + "name": "ShareTypeEnum", + "schema": "public", + "values": ["COMMON", "PREFERRED"] + }, + "public.StakeholderRelationshipEnum": { + "name": "StakeholderRelationshipEnum", + "schema": "public", + "values": [ + "ADVISOR", + "BOARD_MEMBER", + "CONSULTANT", + "EMPLOYEE", + "EX_ADVISOR", + "EX_CONSULTANT", + "EX_EMPLOYEE", + "EXECUTIVE", + "FOUNDER", + "INVESTOR", + "NON_US_EMPLOYEE", + "OFFICER", + "OTHER" + ] + }, + "public.StakeholderTypeEnum": { + "name": "StakeholderTypeEnum", + "schema": "public", + "values": ["INDIVIDUAL", "INSTITUTION"] + }, + "public.SubscriptionStatus": { + "name": "SubscriptionStatus", + "schema": "public", + "values": [ + "trialing", + "active", + "canceled", + "incomplete", + "incomplete_expired", + "past_due", + "unpaid", + "paused" + ] + }, + "public.TemplateStatus": { + "name": "TemplateStatus", + "schema": "public", + "values": ["DRAFT", "COMPLETE", "SENT", "WAITING", "CANCELLED"] + }, + "public.UpdateStatusEnum": { + "name": "UpdateStatusEnum", + "schema": "public", + "values": ["DRAFT", "PUBLIC", "PRIVATE"] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/packages/db/migrations/meta/_journal.json b/packages/db/migrations/meta/_journal.json index d2517b9b4..bea8d80f3 100644 --- a/packages/db/migrations/meta/_journal.json +++ b/packages/db/migrations/meta/_journal.json @@ -22,6 +22,20 @@ "when": 1748503844931, "tag": "0002_nostalgic_donald_blake", "breakpoints": true + }, + { + "idx": 3, + "version": "7", + "when": 1748838315996, + "tag": "0003_small_emma_frost", + "breakpoints": true + }, + { + "idx": 4, + "version": "7", + "when": 1748840687375, + "tag": "0004_moaning_red_hulk", + "breakpoints": true } ] } diff --git a/packages/db/schema/index.ts b/packages/db/schema/index.ts index 57494e0a1..5f3ad5889 100644 --- a/packages/db/schema/index.ts +++ b/packages/db/schema/index.ts @@ -26,4 +26,5 @@ export * from "./convertible-notes"; export * from "./updates"; export * from "./billing"; export * from "./access-tokens"; +export * from "./job-queue"; export * from "./relations"; diff --git a/packages/db/schema/job-queue.ts b/packages/db/schema/job-queue.ts new file mode 100644 index 000000000..758281f6e --- /dev/null +++ b/packages/db/schema/job-queue.ts @@ -0,0 +1,68 @@ +import { createId } from "@paralleldrive/cuid2"; +import { + index, + integer, + json, + pgTable, + timestamp, + varchar, +} from "drizzle-orm/pg-core"; +import { createTable } from "./table"; + +export const jobQueue = createTable( + "job_queue", + { + id: varchar("id", { length: 128 }) + .primaryKey() + .$defaultFn(() => createId()), + type: varchar("type", { length: 100 }).notNull(), + payload: json("payload").notNull(), + status: varchar("status", { length: 20 }).default("pending").notNull(), + attempts: integer("attempts").default(0).notNull(), + maxAttempts: integer("max_attempts").default(3).notNull(), + priority: integer("priority").default(0).notNull(), + scheduledFor: timestamp("scheduled_for").defaultNow().notNull(), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().notNull(), + processedAt: timestamp("processed_at"), + failedAt: timestamp("failed_at"), + error: varchar("error", { length: 1000 }), + retryDelay: integer("retry_delay").default(1000).notNull(), + }, + (table) => ({ + // Primary index for job processing - covers the main WHERE clause + // Used in: WHERE status = 'pending' AND scheduled_for <= NOW() AND attempts < max_attempts + jobProcessingIdx: index("job_processing_idx").on( + table.status, + table.scheduledFor, + table.attempts, + ), + + // Index for priority ordering - optimizes ORDER BY priority DESC, created_at ASC + priorityOrderingIdx: index("priority_ordering_idx").on( + table.priority.desc(), + table.createdAt.asc(), + ), + + // Index for cleanup operations - covers cleanup WHERE clause + // Used in: WHERE created_at <= cutoff AND status = 'completed' + cleanupIdx: index("cleanup_idx").on(table.status, table.createdAt), + + // Index for status filtering and stats queries + // Used in: GROUP BY status and general status filtering + statusIdx: index("status_idx").on(table.status), + + // Index for job type filtering - useful for monitoring specific job types + typeIdx: index("type_idx").on(table.type), + + // Composite index for failed job analysis + // Used for: WHERE status = 'failed' ORDER BY failed_at DESC + failedJobsIdx: index("failed_jobs_idx").on( + table.status, + table.failedAt.desc(), + ), + }), +); + +export type JobQueue = typeof jobQueue.$inferSelect; +export type NewJobQueue = typeof jobQueue.$inferInsert; diff --git a/packages/queue/README.md b/packages/queue/README.md new file mode 100644 index 000000000..1345fdd0a --- /dev/null +++ b/packages/queue/README.md @@ -0,0 +1,494 @@ +# @captable/queue + +A serverless-friendly job queue implementation for the Captable monorepo, designed to replace pg-boss and work seamlessly with Vercel, Netlify, and other serverless platforms. + +## Features + +- πŸš€ **Serverless-native** - No persistent connections required +- ⚑ **High performance** - Uses database-backed queue with efficient queries +- πŸ”„ **Automatic retries** - Exponential backoff with configurable limits +- πŸ“Š **Priority queues** - Process high-priority jobs first +- 🧹 **Auto cleanup** - Automatic removal of old completed jobs +- πŸ“ˆ **Job statistics** - Monitor queue health and performance +- πŸ” **Type-safe** - Full TypeScript support with proper typing +- πŸͺ΅ **Comprehensive logging** - Structured logging with @captable/logger + +## Installation + +```bash +# The package is already installed as part of the monorepo +``` + +## Quick Start + +### 1. Create a Job + +```typescript +import { BaseJob } from "@captable/queue"; +import { sendMail } from "@/server/mailer"; + +export type WelcomeEmailPayload = { + email: string; + name: string; + companyName: string; +}; + +export class WelcomeEmailJob extends BaseJob { + readonly type = "email.welcome"; + protected readonly options = { + maxAttempts: 3, + retryDelay: 1000, + priority: 1, + }; + + async work(payload: WelcomeEmailPayload): Promise { + // Your job logic here + await sendMail({ + to: [payload.email], + subject: `Welcome to ${payload.companyName}!`, + html: `

Welcome ${payload.name}!

`, + }); + } +} + +// Create and register the job +const welcomeEmailJob = new WelcomeEmailJob(); +welcomeEmailJob.register(); + +export { welcomeEmailJob }; +``` + +### 2. Queue Jobs + +```typescript +import { welcomeEmailJob } from "@/jobs/welcome-email"; + +// Emit a single job +await welcomeEmailJob.emit({ + email: "user@example.com", + name: "John Doe", + companyName: "Acme Corp", +}); + +// Emit with custom options +await welcomeEmailJob.emit( + { + email: "user@example.com", + name: "John Doe", + companyName: "Acme Corp", + }, + { + delay: 60, // Wait 60 seconds before processing + priority: 5, // High priority + maxAttempts: 5, + } +); + +// Bulk emit +await welcomeEmailJob.bulkEmit([ + { email: "user1@example.com", name: "User 1", companyName: "Acme" }, + { email: "user2@example.com", name: "User 2", companyName: "Acme" }, +]); +``` + +### 3. Process Jobs (Cron) + +Jobs are automatically processed via Cron: + +```typescript +// app/api/cron/process-jobs/route.ts +import { processJobs } from "@captable/queue"; +import "@/jobs"; // Import to register all jobs + +export async function GET() { + const processed = await processJobs(20); + return Response.json({ processed }); +} +``` + +## API Reference + +### BaseJob + +Abstract base class for creating jobs. + +```typescript +abstract class BaseJob> { + abstract readonly type: string; + protected readonly options: JobOptions; + + abstract work(payload: T): Promise; + + emit(payload: T, options?: JobOptions): Promise; + bulkEmit(payloads: T[], options?: JobOptions): Promise; + emitDelayed(payload: T, delayInSeconds: number, options?: JobOptions): Promise; + emitPriority(payload: T, priority: number, options?: JobOptions): Promise; + register(): void; +} +``` + +### Queue Functions + +Utility functions for queue management. + +```typescript +// Register a job processor +function register(processor: JobProcessor): void; + +// Add jobs to queue +function addJob(type: string, payload: T, options?: JobOptions): Promise; +function addJobs(jobs: Array>): Promise; + +// Process and manage jobs +function processJobs(limit?: number): Promise; +function getStats(): Promise; +function cleanupJobs(olderThanDays?: number): Promise; + +// Utility functions +function getRegisteredProcessors(): string[]; +function clearProcessors(): void; +``` + +### JobOptions + +Configuration options for jobs. + +```typescript +interface JobOptions { + delay?: number; // seconds to delay execution + maxAttempts?: number; // maximum retry attempts + priority?: number; // higher = more priority + retryDelay?: number; // milliseconds between retries +} +``` + +### JobStats + +Queue statistics. + +```typescript +interface JobStats { + pending: number; + processing: number; + completed: number; + failed: number; +} +``` + +## Configuration + +### Cron + +```json +// vercel.json +{ + "crons": [ + { + "path": "/api/cron/process-jobs", + "schedule": "* * * * *" // Every minute + }, + { + "path": "/api/cron/cleanup-jobs", + "schedule": "0 2 * * *" // Daily at 2 AM + } + ] +} +``` + +### Environment Variables + +```bash +CRON_SECRET=your-super-secret-cron-key +DATABASE_URL=your-database-url +``` + +## Database Schema + +The queue uses a single table `cap_job_queue`: + +```sql +CREATE TABLE cap_job_queue ( + id VARCHAR(128) PRIMARY KEY, + type VARCHAR(100) NOT NULL, + payload JSON NOT NULL, + status VARCHAR(20) DEFAULT 'pending', + attempts INTEGER DEFAULT 0, + max_attempts INTEGER DEFAULT 3, + priority INTEGER DEFAULT 0, + scheduled_for TIMESTAMP DEFAULT NOW(), + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW(), + processed_at TIMESTAMP, + failed_at TIMESTAMP, + error VARCHAR(1000), + retry_delay INTEGER DEFAULT 1000 +); +``` + +## Job Organization + +### Job Registry + +Create a central registry to import all jobs: + +```typescript +// jobs/index.ts +import "./welcome-email"; +import "./password-reset"; +import "./notifications"; + +export { welcomeEmailJob } from "./welcome-email"; +export { passwordResetJob } from "./password-reset"; +export * from "@captable/queue"; +``` + +### Job Types + +Follow consistent naming conventions: + +- Email jobs: `email.welcome`, `email.password-reset` +- PDF generation: `generate.invoice`, `generate.report` +- Data processing: `process.analytics`, `process.cleanup` + +## Best Practices + +### 1. Job Design + +- Keep jobs idempotent +- Handle errors gracefully +- Use appropriate retry limits +- Set meaningful priorities + +### 2. Payload Design + +```typescript +// βœ… Good: Specific, typed payload +type EmailPayload = { + to: string; + template: string; + data: Record; +}; + +// ❌ Bad: Generic, untyped payload +type GenericPayload = { + action: string; + params: any; +}; +``` + +### 3. Error Handling + +```typescript +async work(payload: EmailPayload): Promise { + try { + await sendEmail(payload); + } catch (error) { + if (error instanceof RateLimitError) { + // Will retry with exponential backoff + throw error; + } + + if (error instanceof PermanentError) { + // Log and don't retry + log.error({ error, payload }, "Permanent error"); + return; // Don't throw to avoid retries + } + + throw error; // Retry for unknown errors + } +} +``` + +### 4. Monitoring + +```typescript +import { getStats, getRegisteredProcessors } from "@captable/queue"; + +// Get queue statistics +const stats = await getStats(); +console.log(`Pending: ${stats.pending}, Failed: ${stats.failed}`); + +// List registered processors +const processors = getRegisteredProcessors(); +console.log(`Registered jobs: ${processors.join(", ")}`); +``` + +## Migration from pg-boss + +### Before (pg-boss) + +```typescript +import { BaseJob } from "@/jobs/base"; +import type { Job } from "pg-boss"; + +export class EmailJob extends BaseJob { + readonly type = "email.send"; + + async work(job: Job): Promise { + await sendEmail(job.data); + } +} + +// Usage +await boss.send("email.send", { to: "user@example.com" }); +``` + +### After (@captable/queue) + +```typescript +import { BaseJob } from "@captable/queue"; + +export class EmailJob extends BaseJob { + readonly type = "email.send"; + + async work(payload: EmailPayload): Promise { + await sendEmail(payload); + } +} + +const emailJob = new EmailJob(); +emailJob.register(); + +// Usage +await emailJob.emit({ to: "user@example.com" }); +``` + +## Troubleshooting + +### Jobs Not Processing + +1. Check cron routes are deployed +2. Verify `CRON_SECRET` is set +3. Ensure jobs are imported in registry +4. Check database connectivity + +### High Failure Rate + +1. Review error logs +2. Adjust retry limits +3. Check external service availability +4. Validate job payloads + +### Performance Issues + +1. Monitor queue depth +2. Adjust processing batch size +3. Consider job priorities +4. Review database indexes + +## License + +MIT + +## Development + +### Quick Development Setup + + +Start everything including job processing: + +```bash +# From monorepo root +bun run dx +``` + +This starts: +- Next.js development server (port 3000) +- Database studio +- Email development server (port 3001) +- **Job processor in watch mode** (with quiet logging) + +### Manual Job Management + +From the `apps/captable` directory: + +```bash +# Process all pending jobs once +bun run jobs + +# Process jobs continuously (watch mode) +bun run jobs:dev + +# Queue sample jobs for testing +bun run test-jobs + +# Show queue statistics +bun run jobs stats + +# Clean up old completed jobs +bun run jobs cleanup +``` + +### Development Scripts + +Job management scripts are located in `apps/captable/scripts/dev/`: + +- **`jobs.ts`** - Main job processor with watch mode +- **`test-jobs.ts`** - Queue sample jobs for testing +- **`README.md`** - Detailed documentation + +### Watch Mode Features + +The watch mode (`bun run jobs:dev`) includes: + +- πŸ”‡ **Quiet operation** - Only logs when jobs are found +- πŸ’“ **Heartbeat logging** - Status every 60 seconds when idle +- πŸ›‘ **Graceful shutdown** - Ctrl+C stops cleanly +- ⚑ **Fast processing** - 1s intervals when jobs found, 5s when idle + +### Development Workflow + +1. **Start full development environment:** + ```bash + bun run dx + ``` + +2. **Queue test jobs (in another terminal):** + ```bash + cd apps/captable + bun run test-jobs + ``` + +3. **Monitor queue status:** + ```bash + bun run jobs stats + ``` + +4. **Manual processing (if needed):** + ```bash + bun run jobs + ``` + +### Testing Individual Job Types + +```bash +# Test specific email jobs +bun run test-jobs password-reset +bun run test-jobs member-invite +bun run test-jobs auth-verification + +# Test all jobs at once +bun run test-jobs all +``` + +### Production vs Development + +| Environment | Trigger | Frequency | Logging | +|-------------|---------|-----------|----------| +| **Development** | Watch mode | Every 5s | Quiet + heartbeat | +| **Production** |Cron | Every minute | Event-driven | + +### Available Job Types + +Current job implementations: + +- `email.password-reset` - Password reset emails +- `email.member-invite` - Member invitation emails +- `email.auth-verify` - Account verification emails +- `email.share-update` - Share update notifications +- `email.share-data-room` - Data room sharing emails +- `email.esign` - E-signature request emails +- `email.esign-confirmation` - E-signature confirmation emails +- `generate.esign-pdf` - PDF generation for e-signatures + +See `apps/captable/jobs/` for complete implementations. diff --git a/packages/queue/base-job.ts b/packages/queue/base-job.ts new file mode 100644 index 000000000..427cc4167 --- /dev/null +++ b/packages/queue/base-job.ts @@ -0,0 +1,83 @@ +import { logger } from "@captable/logger"; +import { addJob, addJobs, register } from "./queue"; +import type { JobOptions } from "./types"; + +const log = logger.child({ module: "base-job" }); + +export abstract class BaseJob> { + abstract readonly type: string; + protected readonly options: JobOptions = { + maxAttempts: 3, + retryDelay: 1000, + priority: 0, + }; + + /** + * Register this job with the queue + * Call this method after instantiating the job + */ + register(): void { + register({ + type: this.type, + process: this.work.bind(this), + }); + + log.info(`Registered job: ${this.type}`); + } + + /** + * Process the job payload + */ + abstract work(payload: T): Promise; + + /** + * Emit a single job + */ + emit(payload: T, options?: JobOptions): Promise { + return addJob(this.type, payload, { + ...this.options, + ...options, + }); + } + + /** + * Emit multiple jobs in bulk + */ + bulkEmit(payloads: T[], options?: JobOptions): Promise { + const jobs = payloads.map((payload) => ({ + type: this.type, + payload, + options: { ...this.options, ...options }, + })); + + return addJobs(jobs); + } + + /** + * Emit a job with delay + */ + emitDelayed( + payload: T, + delayInSeconds: number, + options?: JobOptions, + ): Promise { + return this.emit(payload, { + ...options, + delay: delayInSeconds, + }); + } + + /** + * Emit a high priority job + */ + emitPriority( + payload: T, + priority: number, + options?: JobOptions, + ): Promise { + return this.emit(payload, { + ...options, + priority, + }); + } +} diff --git a/packages/queue/index.ts b/packages/queue/index.ts new file mode 100644 index 000000000..fc33d0ab8 --- /dev/null +++ b/packages/queue/index.ts @@ -0,0 +1,20 @@ +export { + register, + addJob, + addJobs, + processJobs, + getStats, + cleanupJobs, + getRegisteredProcessors, + clearProcessors, +} from "./queue"; +export { BaseJob } from "./base-job"; +export type { + JobOptions, + JobProcessor, + JobStats, + BulkJobInput, +} from "./types"; + +// Re-export database types for convenience +export type { JobQueue, NewJobQueue } from "@captable/db/schema"; diff --git a/packages/queue/package.json b/packages/queue/package.json new file mode 100644 index 000000000..693f341bc --- /dev/null +++ b/packages/queue/package.json @@ -0,0 +1,38 @@ +{ + "name": "@captable/queue", + "module": "index.ts", + "type": "module", + "exports": { + ".": { + "import": "./index.ts", + "require": "./index.ts", + "types": "./index.ts" + }, + "./base-job": { + "import": "./base-job.ts", + "types": "./base-job.ts" + }, + "./types": { + "import": "./types.ts", + "types": "./types.ts" + } + }, + "sideEffects": false, + "scripts": { + "lint": "biome check", + "format": "biome format --write", + "type-check": "tsc --noEmit" + }, + "devDependencies": { + "@biomejs/biome": "1.9.4", + "@types/bun": "latest" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "dependencies": { + "@captable/db": "workspace:*", + "@captable/logger": "workspace:*", + "drizzle-orm": "^0.43.1" + } +} diff --git a/packages/queue/queue.ts b/packages/queue/queue.ts new file mode 100644 index 000000000..c85b3a3ac --- /dev/null +++ b/packages/queue/queue.ts @@ -0,0 +1,310 @@ +import { db } from "@captable/db"; +import { jobQueue } from "@captable/db/schema"; +import type { JobQueue } from "@captable/db/schema"; +import { logger } from "@captable/logger"; +import { and, asc, desc, eq, lt, lte, sql } from "drizzle-orm"; +import type { BulkJobInput, JobOptions, JobProcessor, JobStats } from "./types"; + +const log = logger.child({ module: "queue" }); + +// Module-level processors map +const processors = new Map>>(); + +/** + * Register a job processor + */ +export function register>( + processor: JobProcessor, +) { + processors.set( + processor.type, + processor as JobProcessor>, + ); + log.info(`Registered job processor: ${processor.type}`); +} + +/** + * Add a single job to the queue + */ +export async function addJob>( + type: string, + payload: T, + options: JobOptions = {}, +): Promise { + const { + delay = 0, + maxAttempts = 3, + priority = 0, + retryDelay = 1000, + } = options; + + const scheduledFor = new Date(Date.now() + delay * 1000); + + const [job] = await db + .insert(jobQueue) + .values({ + type, + payload, + maxAttempts, + priority, + scheduledFor, + retryDelay, + }) + .returning(); + + if (!job) { + throw new Error("Failed to create job"); + } + + log.info( + { + jobId: job.id, + type, + scheduledFor, + priority, + }, + `Job queued: ${type}`, + ); + + return job.id; +} + +/** + * Add multiple jobs to the queue in bulk + */ +export async function addJobs>( + jobs: Array>, +): Promise { + const jobsToInsert = jobs.map(({ type, payload, options = {} }) => { + const { + delay = 0, + maxAttempts = 3, + priority = 0, + retryDelay = 1000, + } = options; + + return { + type, + payload, + maxAttempts, + priority, + scheduledFor: new Date(Date.now() + delay * 1000), + retryDelay, + }; + }); + + const insertedJobs = await db + .insert(jobQueue) + .values(jobsToInsert) + .returning(); + + log.info(`Bulk queued ${insertedJobs.length} jobs`); + return insertedJobs.map((job) => job.id); +} + +/** + * Process pending jobs + */ +export async function processJobs(limit = 10): Promise { + const startTime = Date.now(); + + // Get jobs ordered by priority (desc) then created date (asc) + const jobs = await db + .select() + .from(jobQueue) + .where( + and( + eq(jobQueue.status, "pending"), + lte(jobQueue.scheduledFor, new Date()), + lt(jobQueue.attempts, jobQueue.maxAttempts), + ), + ) + .orderBy(desc(jobQueue.priority), asc(jobQueue.createdAt)) + .limit(limit); + + if (jobs.length === 0) { + return 0; + } + + log.info(`Processing ${jobs.length} jobs`); + + let processedCount = 0; + + for (const job of jobs) { + try { + await executeJob(job); + processedCount++; + } catch (error) { + log.error( + { + jobId: job.id, + type: job.type, + error: error instanceof Error ? error.message : String(error), + }, + `Job execution failed: ${job.type}`, + ); + } + } + + const duration = Date.now() - startTime; + log.info( + { + processed: processedCount, + total: jobs.length, + duration, + }, + `Job processing completed in ${duration}ms`, + ); + + return processedCount; +} + +/** + * Execute a single job + */ +async function executeJob(job: JobQueue): Promise { + const jobStartTime = Date.now(); + + // Update job status to processing + await db + .update(jobQueue) + .set({ + status: "processing", + updatedAt: new Date(), + }) + .where(eq(jobQueue.id, job.id)); + + const processor = processors.get(job.type); + if (!processor) { + throw new Error(`No processor registered for job type: ${job.type}`); + } + + try { + await processor.process(job.payload as Record); + + // Mark as completed + await db + .update(jobQueue) + .set({ + status: "completed", + processedAt: new Date(), + updatedAt: new Date(), + }) + .where(eq(jobQueue.id, job.id)); + + const duration = Date.now() - jobStartTime; + log.info( + { + jobId: job.id, + type: job.type, + duration, + }, + `Job completed: ${job.type}`, + ); + } catch (error) { + const newAttempts = job.attempts + 1; + const isLastAttempt = newAttempts >= job.maxAttempts; + const errorMessage = error instanceof Error ? error.message : String(error); + + // Calculate next retry time with exponential backoff + const nextRetryDelay = job.retryDelay * 2 ** job.attempts; + const nextScheduledFor = new Date(Date.now() + nextRetryDelay); + + await db + .update(jobQueue) + .set({ + attempts: newAttempts, + status: isLastAttempt ? "failed" : "pending", + error: errorMessage, + failedAt: isLastAttempt ? new Date() : undefined, + scheduledFor: isLastAttempt ? job.scheduledFor : nextScheduledFor, + updatedAt: new Date(), + }) + .where(eq(jobQueue.id, job.id)); + + if (isLastAttempt) { + log.error( + { + jobId: job.id, + type: job.type, + attempts: newAttempts, + error: errorMessage, + }, + `Job failed permanently: ${job.type}`, + ); + } else { + log.warn( + { + jobId: job.id, + type: job.type, + attempts: newAttempts, + nextRetry: nextScheduledFor, + error: errorMessage, + }, + `Job failed, will retry: ${job.type}`, + ); + } + + throw error; + } +} + +/** + * Get job statistics + */ +export async function getStats(): Promise { + const stats = await db + .select({ + status: jobQueue.status, + count: sql`count(*)`, + }) + .from(jobQueue) + .groupBy(jobQueue.status); + + return stats.reduce( + (acc, stat) => { + acc[stat.status as keyof JobStats] = stat.count; + return acc; + }, + { + pending: 0, + processing: 0, + completed: 0, + failed: 0, + } as JobStats, + ); +} + +/** + * Clean up old completed jobs + */ +export async function cleanupJobs(olderThanDays = 7): Promise { + const cutoffDate = new Date(Date.now() - olderThanDays * 24 * 60 * 60 * 1000); + + const result = await db + .delete(jobQueue) + .where( + and( + lte(jobQueue.createdAt, cutoffDate), + eq(jobQueue.status, "completed"), + ), + ); + + const deletedCount = result.length || 0; + log.info(`Cleaned up ${deletedCount} old completed jobs`); + return deletedCount; +} + +/** + * Get all processors types + */ +export function getRegisteredProcessors(): string[] { + return Array.from(processors.keys()); +} + +/** + * Clear all processors (useful for testing) + */ +export function clearProcessors(): void { + processors.clear(); +} diff --git a/packages/queue/types.ts b/packages/queue/types.ts new file mode 100644 index 000000000..5ce349cba --- /dev/null +++ b/packages/queue/types.ts @@ -0,0 +1,24 @@ +export interface JobOptions { + delay?: number; // seconds + maxAttempts?: number; + priority?: number; + retryDelay?: number; // milliseconds +} + +export interface JobProcessor> { + type: string; + process: (payload: T) => Promise; +} + +export interface JobStats { + pending: number; + processing: number; + completed: number; + failed: number; +} + +export interface BulkJobInput> { + type: string; + payload: T; + options?: JobOptions; +} diff --git a/turbo.jsonc b/turbo.jsonc index 98369b88b..fa9e495f5 100644 --- a/turbo.jsonc +++ b/turbo.jsonc @@ -61,6 +61,14 @@ "inputs": [] }, + // Jobs development - continuous job processing + "jobs:dev": { + "cache": false, + "persistent": true, + "dependsOn": [], + "inputs": [] + }, + // Formatting is independent "format": { "inputs": ["**/*.{ts,tsx,js,jsx,json,md}", "biome.json", ".biomejs.json"],