diff --git a/packages/database/.env.example b/packages/database/.env.example new file mode 100644 index 0000000..96db092 --- /dev/null +++ b/packages/database/.env.example @@ -0,0 +1,4 @@ +DATABASE_URL="postgresql://postgres:postgres@localhost:5432/stellar_pay" +SHADOW_DATABASE_URL="postgresql://postgres:postgres@localhost:5432/stellar_pay_shadow" +TEST_DATABASE_URL="postgresql://postgres:postgres@localhost:5433/stellar_pay_test" +TEST_SHADOW_DATABASE_URL="postgresql://postgres:postgres@localhost:5433/stellar_pay_test_shadow" diff --git a/packages/database/migrations/deploy.sh b/packages/database/migrations/deploy.sh new file mode 100755 index 0000000..21559a1 --- /dev/null +++ b/packages/database/migrations/deploy.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [[ -z "${DATABASE_URL:-}" ]]; then + echo "DATABASE_URL is required for migration deployment." >&2 + exit 1 +fi + +pnpm exec prisma migrate deploy --schema ./migrations/schema.prisma +pnpm exec prisma generate --schema ./migrations/schema.prisma diff --git a/packages/database/migrations/migration_lock.toml b/packages/database/migrations/migration_lock.toml new file mode 100644 index 0000000..2fe25d8 --- /dev/null +++ b/packages/database/migrations/migration_lock.toml @@ -0,0 +1 @@ +provider = "postgresql" diff --git a/packages/database/migrations/migrations/202603240001_initial_schema/migration.sql b/packages/database/migrations/migrations/202603240001_initial_schema/migration.sql new file mode 100644 index 0000000..4203521 --- /dev/null +++ b/packages/database/migrations/migrations/202603240001_initial_schema/migration.sql @@ -0,0 +1,195 @@ +-- Create enums +CREATE TYPE "MerchantStatus" AS ENUM ('ACTIVE', 'DISABLED'); +CREATE TYPE "UserRole" AS ENUM ('OWNER', 'ADMIN', 'MEMBER'); +CREATE TYPE "PaymentIntentStatus" AS ENUM ('REQUIRES_PAYMENT_METHOD', 'PROCESSING', 'SUCCEEDED', 'CANCELED', 'FAILED'); +CREATE TYPE "EscrowStatus" AS ENUM ('PENDING', 'FUNDED', 'RELEASED', 'REFUNDED', 'FAILED'); +CREATE TYPE "SubscriptionInterval" AS ENUM ('DAY', 'WEEK', 'MONTH', 'YEAR'); +CREATE TYPE "SubscriptionStatus" AS ENUM ('TRIALING', 'ACTIVE', 'PAST_DUE', 'CANCELED', 'ENDED'); +CREATE TYPE "InvoiceStatus" AS ENUM ('PENDING', 'PAID', 'VOID', 'FAILED'); +CREATE TYPE "PayoutStatus" AS ENUM ('REQUESTED', 'PROCESSING', 'COMPLETED', 'FAILED', 'CANCELED'); +CREATE TYPE "WebhookStatus" AS ENUM ('RECEIVED', 'PROCESSED', 'FAILED'); + +-- Create tables +CREATE TABLE "Merchant" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "legalName" TEXT, + "email" TEXT, + "status" "MerchantStatus" NOT NULL DEFAULT 'ACTIVE', + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "Merchant_pkey" PRIMARY KEY ("id") +); + +CREATE TABLE "MerchantUser" ( + "id" TEXT NOT NULL, + "merchantId" TEXT NOT NULL, + "email" TEXT NOT NULL, + "fullName" TEXT, + "role" "UserRole" NOT NULL DEFAULT 'MEMBER', + "isActive" BOOLEAN NOT NULL DEFAULT true, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "MerchantUser_pkey" PRIMARY KEY ("id") +); + +CREATE TABLE "Customer" ( + "id" TEXT NOT NULL, + "merchantId" TEXT NOT NULL, + "externalRef" TEXT, + "email" TEXT, + "name" TEXT, + "metadata" JSONB, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "Customer_pkey" PRIMARY KEY ("id") +); + +CREATE TABLE "PaymentIntent" ( + "id" TEXT NOT NULL, + "merchantId" TEXT NOT NULL, + "customerId" TEXT, + "amountMinor" INTEGER NOT NULL, + "currency" VARCHAR(3) NOT NULL, + "status" "PaymentIntentStatus" NOT NULL DEFAULT 'REQUIRES_PAYMENT_METHOD', + "paymentMethod" TEXT, + "description" TEXT, + "clientSecret" TEXT NOT NULL DEFAULT gen_random_uuid()::text, + "idempotencyKey" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "PaymentIntent_pkey" PRIMARY KEY ("id") +); + +CREATE TABLE "EscrowTransaction" ( + "id" TEXT NOT NULL, + "paymentIntentId" TEXT NOT NULL, + "network" TEXT NOT NULL DEFAULT 'stellar', + "escrowAddress" TEXT, + "txHash" TEXT, + "status" "EscrowStatus" NOT NULL DEFAULT 'PENDING', + "fundedAt" TIMESTAMP(3), + "releasedAt" TIMESTAMP(3), + "refundedAt" TIMESTAMP(3), + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "EscrowTransaction_pkey" PRIMARY KEY ("id") +); + +CREATE TABLE "Subscription" ( + "id" TEXT NOT NULL, + "merchantId" TEXT NOT NULL, + "customerId" TEXT NOT NULL, + "planCode" TEXT NOT NULL, + "amountMinor" INTEGER NOT NULL, + "currency" VARCHAR(3) NOT NULL, + "interval" "SubscriptionInterval" NOT NULL, + "status" "SubscriptionStatus" NOT NULL DEFAULT 'TRIALING', + "currentPeriodStart" TIMESTAMP(3) NOT NULL, + "currentPeriodEnd" TIMESTAMP(3) NOT NULL, + "cancelAtPeriodEnd" BOOLEAN NOT NULL DEFAULT false, + "canceledAt" TIMESTAMP(3), + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "Subscription_pkey" PRIMARY KEY ("id") +); + +CREATE TABLE "SubscriptionInvoice" ( + "id" TEXT NOT NULL, + "subscriptionId" TEXT NOT NULL, + "externalPaymentIntentId" TEXT, + "amountMinor" INTEGER NOT NULL, + "currency" VARCHAR(3) NOT NULL, + "status" "InvoiceStatus" NOT NULL DEFAULT 'PENDING', + "dueAt" TIMESTAMP(3) NOT NULL, + "paidAt" TIMESTAMP(3), + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "SubscriptionInvoice_pkey" PRIMARY KEY ("id") +); + +CREATE TABLE "TreasuryWallet" ( + "id" TEXT NOT NULL, + "merchantId" TEXT NOT NULL, + "network" TEXT NOT NULL DEFAULT 'stellar', + "publicKey" TEXT NOT NULL, + "encryptedSecret" TEXT NOT NULL, + "isPrimary" BOOLEAN NOT NULL DEFAULT false, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "TreasuryWallet_pkey" PRIMARY KEY ("id") +); + +CREATE TABLE "Payout" ( + "id" TEXT NOT NULL, + "merchantId" TEXT NOT NULL, + "amountMinor" INTEGER NOT NULL, + "currency" VARCHAR(3) NOT NULL, + "destination" TEXT NOT NULL, + "status" "PayoutStatus" NOT NULL DEFAULT 'REQUESTED', + "requestedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "processedAt" TIMESTAMP(3), + "txHash" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "Payout_pkey" PRIMARY KEY ("id") +); + +CREATE TABLE "WebhookEvent" ( + "id" TEXT NOT NULL, + "merchantId" TEXT, + "provider" TEXT NOT NULL, + "eventType" TEXT NOT NULL, + "payload" JSONB NOT NULL, + "status" "WebhookStatus" NOT NULL DEFAULT 'RECEIVED', + "processedAt" TIMESTAMP(3), + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT "WebhookEvent_pkey" PRIMARY KEY ("id") +); + +CREATE TABLE "AuditLog" ( + "id" BIGSERIAL NOT NULL, + "actorType" TEXT NOT NULL, + "actorId" TEXT, + "action" TEXT NOT NULL, + "entityType" TEXT NOT NULL, + "entityId" TEXT, + "metadata" JSONB, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT "AuditLog_pkey" PRIMARY KEY ("id") +); + +-- Indexes and unique constraints +CREATE UNIQUE INDEX "Merchant_email_key" ON "Merchant"("email"); +CREATE UNIQUE INDEX "MerchantUser_email_key" ON "MerchantUser"("email"); +CREATE INDEX "MerchantUser_merchantId_idx" ON "MerchantUser"("merchantId"); +CREATE UNIQUE INDEX "Customer_merchantId_externalRef_key" ON "Customer"("merchantId", "externalRef"); +CREATE INDEX "Customer_merchantId_idx" ON "Customer"("merchantId"); +CREATE UNIQUE INDEX "PaymentIntent_clientSecret_key" ON "PaymentIntent"("clientSecret"); +CREATE UNIQUE INDEX "PaymentIntent_merchantId_idempotencyKey_key" ON "PaymentIntent"("merchantId", "idempotencyKey"); +CREATE INDEX "PaymentIntent_merchantId_idx" ON "PaymentIntent"("merchantId"); +CREATE INDEX "PaymentIntent_customerId_idx" ON "PaymentIntent"("customerId"); +CREATE UNIQUE INDEX "EscrowTransaction_paymentIntentId_key" ON "EscrowTransaction"("paymentIntentId"); +CREATE INDEX "Subscription_merchantId_idx" ON "Subscription"("merchantId"); +CREATE INDEX "Subscription_customerId_idx" ON "Subscription"("customerId"); +CREATE UNIQUE INDEX "SubscriptionInvoice_externalPaymentIntentId_key" ON "SubscriptionInvoice"("externalPaymentIntentId"); +CREATE INDEX "SubscriptionInvoice_subscriptionId_idx" ON "SubscriptionInvoice"("subscriptionId"); +CREATE UNIQUE INDEX "TreasuryWallet_publicKey_key" ON "TreasuryWallet"("publicKey"); +CREATE INDEX "TreasuryWallet_merchantId_idx" ON "TreasuryWallet"("merchantId"); +CREATE INDEX "Payout_merchantId_idx" ON "Payout"("merchantId"); +CREATE INDEX "WebhookEvent_merchantId_idx" ON "WebhookEvent"("merchantId"); +CREATE INDEX "AuditLog_entityType_entityId_idx" ON "AuditLog"("entityType", "entityId"); + +-- Foreign keys +ALTER TABLE "MerchantUser" ADD CONSTRAINT "MerchantUser_merchantId_fkey" FOREIGN KEY ("merchantId") REFERENCES "Merchant"("id") ON DELETE CASCADE ON UPDATE CASCADE; +ALTER TABLE "Customer" ADD CONSTRAINT "Customer_merchantId_fkey" FOREIGN KEY ("merchantId") REFERENCES "Merchant"("id") ON DELETE CASCADE ON UPDATE CASCADE; +ALTER TABLE "PaymentIntent" ADD CONSTRAINT "PaymentIntent_merchantId_fkey" FOREIGN KEY ("merchantId") REFERENCES "Merchant"("id") ON DELETE CASCADE ON UPDATE CASCADE; +ALTER TABLE "PaymentIntent" ADD CONSTRAINT "PaymentIntent_customerId_fkey" FOREIGN KEY ("customerId") REFERENCES "Customer"("id") ON DELETE SET NULL ON UPDATE CASCADE; +ALTER TABLE "EscrowTransaction" ADD CONSTRAINT "EscrowTransaction_paymentIntentId_fkey" FOREIGN KEY ("paymentIntentId") REFERENCES "PaymentIntent"("id") ON DELETE CASCADE ON UPDATE CASCADE; +ALTER TABLE "Subscription" ADD CONSTRAINT "Subscription_merchantId_fkey" FOREIGN KEY ("merchantId") REFERENCES "Merchant"("id") ON DELETE CASCADE ON UPDATE CASCADE; +ALTER TABLE "Subscription" ADD CONSTRAINT "Subscription_customerId_fkey" FOREIGN KEY ("customerId") REFERENCES "Customer"("id") ON DELETE CASCADE ON UPDATE CASCADE; +ALTER TABLE "SubscriptionInvoice" ADD CONSTRAINT "SubscriptionInvoice_subscriptionId_fkey" FOREIGN KEY ("subscriptionId") REFERENCES "Subscription"("id") ON DELETE CASCADE ON UPDATE CASCADE; +ALTER TABLE "SubscriptionInvoice" ADD CONSTRAINT "SubscriptionInvoice_externalPaymentIntentId_fkey" FOREIGN KEY ("externalPaymentIntentId") REFERENCES "PaymentIntent"("id") ON DELETE SET NULL ON UPDATE CASCADE; +ALTER TABLE "TreasuryWallet" ADD CONSTRAINT "TreasuryWallet_merchantId_fkey" FOREIGN KEY ("merchantId") REFERENCES "Merchant"("id") ON DELETE CASCADE ON UPDATE CASCADE; +ALTER TABLE "Payout" ADD CONSTRAINT "Payout_merchantId_fkey" FOREIGN KEY ("merchantId") REFERENCES "Merchant"("id") ON DELETE CASCADE ON UPDATE CASCADE; +ALTER TABLE "WebhookEvent" ADD CONSTRAINT "WebhookEvent_merchantId_fkey" FOREIGN KEY ("merchantId") REFERENCES "Merchant"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/packages/database/migrations/schema.prisma b/packages/database/migrations/schema.prisma new file mode 100644 index 0000000..6176a8d --- /dev/null +++ b/packages/database/migrations/schema.prisma @@ -0,0 +1,255 @@ +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +enum MerchantStatus { + ACTIVE + DISABLED +} + +enum UserRole { + OWNER + ADMIN + MEMBER +} + +enum PaymentIntentStatus { + REQUIRES_PAYMENT_METHOD + PROCESSING + SUCCEEDED + CANCELED + FAILED +} + +enum EscrowStatus { + PENDING + FUNDED + RELEASED + REFUNDED + FAILED +} + +enum SubscriptionInterval { + DAY + WEEK + MONTH + YEAR +} + +enum SubscriptionStatus { + TRIALING + ACTIVE + PAST_DUE + CANCELED + ENDED +} + +enum InvoiceStatus { + PENDING + PAID + VOID + FAILED +} + +enum PayoutStatus { + REQUESTED + PROCESSING + COMPLETED + FAILED + CANCELED +} + +enum WebhookStatus { + RECEIVED + PROCESSED + FAILED +} + +model Merchant { + id String @id @default(cuid()) + name String + legalName String? + email String? @unique + status MerchantStatus @default(ACTIVE) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + users MerchantUser[] + customers Customer[] + paymentIntents PaymentIntent[] + subscriptions Subscription[] + treasuryWallets TreasuryWallet[] + payouts Payout[] + webhookEvents WebhookEvent[] +} + +model MerchantUser { + id String @id @default(cuid()) + merchantId String + email String @unique + fullName String? + role UserRole @default(MEMBER) + isActive Boolean @default(true) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + merchant Merchant @relation(fields: [merchantId], references: [id], onDelete: Cascade) + + @@index([merchantId]) +} + +model Customer { + id String @id @default(cuid()) + merchantId String + externalRef String? + email String? + name String? + metadata Json? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + merchant Merchant @relation(fields: [merchantId], references: [id], onDelete: Cascade) + paymentIntents PaymentIntent[] + subscriptions Subscription[] + + @@unique([merchantId, externalRef]) + @@index([merchantId]) +} + +model PaymentIntent { + id String @id @default(cuid()) + merchantId String + customerId String? + amountMinor Int + currency String @db.VarChar(3) + status PaymentIntentStatus @default(REQUIRES_PAYMENT_METHOD) + paymentMethod String? + description String? + clientSecret String @default(uuid()) @unique + idempotencyKey String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + merchant Merchant @relation(fields: [merchantId], references: [id], onDelete: Cascade) + customer Customer? @relation(fields: [customerId], references: [id], onDelete: SetNull) + escrowTransaction EscrowTransaction? + subscriptionInvoices SubscriptionInvoice[] + + @@unique([merchantId, idempotencyKey]) + @@index([merchantId]) + @@index([customerId]) +} + +model EscrowTransaction { + id String @id @default(cuid()) + paymentIntentId String @unique + network String @default("stellar") + escrowAddress String? + txHash String? + status EscrowStatus @default(PENDING) + fundedAt DateTime? + releasedAt DateTime? + refundedAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + paymentIntent PaymentIntent @relation(fields: [paymentIntentId], references: [id], onDelete: Cascade) +} + +model Subscription { + id String @id @default(cuid()) + merchantId String + customerId String + planCode String + amountMinor Int + currency String @db.VarChar(3) + interval SubscriptionInterval + status SubscriptionStatus @default(TRIALING) + currentPeriodStart DateTime + currentPeriodEnd DateTime + cancelAtPeriodEnd Boolean @default(false) + canceledAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + merchant Merchant @relation(fields: [merchantId], references: [id], onDelete: Cascade) + customer Customer @relation(fields: [customerId], references: [id], onDelete: Cascade) + invoices SubscriptionInvoice[] + + @@index([merchantId]) + @@index([customerId]) +} + +model SubscriptionInvoice { + id String @id @default(cuid()) + subscriptionId String + externalPaymentIntentId String? @unique + amountMinor Int + currency String @db.VarChar(3) + status InvoiceStatus @default(PENDING) + dueAt DateTime + paidAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + subscription Subscription @relation(fields: [subscriptionId], references: [id], onDelete: Cascade) + paymentIntent PaymentIntent? @relation(fields: [externalPaymentIntentId], references: [id], onDelete: SetNull) + + @@index([subscriptionId]) +} + +model TreasuryWallet { + id String @id @default(cuid()) + merchantId String + network String @default("stellar") + publicKey String @unique + encryptedSecret String + isPrimary Boolean @default(false) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + merchant Merchant @relation(fields: [merchantId], references: [id], onDelete: Cascade) + + @@index([merchantId]) +} + +model Payout { + id String @id @default(cuid()) + merchantId String + amountMinor Int + currency String @db.VarChar(3) + destination String + status PayoutStatus @default(REQUESTED) + requestedAt DateTime @default(now()) + processedAt DateTime? + txHash String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + merchant Merchant @relation(fields: [merchantId], references: [id], onDelete: Cascade) + + @@index([merchantId]) +} + +model WebhookEvent { + id String @id @default(cuid()) + merchantId String? + provider String + eventType String + payload Json + status WebhookStatus @default(RECEIVED) + processedAt DateTime? + createdAt DateTime @default(now()) + merchant Merchant? @relation(fields: [merchantId], references: [id], onDelete: SetNull) + + @@index([merchantId]) +} + +model AuditLog { + id BigInt @id @default(autoincrement()) + actorType String + actorId String? + action String + entityType String + entityId String? + metadata Json? + createdAt DateTime @default(now()) + + @@index([entityType, entityId]) +} diff --git a/packages/database/migrations/seed.js b/packages/database/migrations/seed.js new file mode 100644 index 0000000..f5ffbc7 --- /dev/null +++ b/packages/database/migrations/seed.js @@ -0,0 +1,140 @@ +let prisma; + +async function main() { + const { PrismaClient } = await import('@prisma/client'); + prisma = new PrismaClient(); + const merchant = await prisma.merchant.upsert({ + where: { email: 'dev-merchant@stellar-pay.local' }, + update: { name: 'Stellar Pay Dev Merchant' }, + create: { + name: 'Stellar Pay Dev Merchant', + legalName: 'Stellar Pay Development LLC', + email: 'dev-merchant@stellar-pay.local', + users: { + create: { + email: 'owner@stellar-pay.local', + fullName: 'Dev Owner', + role: 'OWNER', + }, + }, + treasuryWallets: { + create: { + network: 'stellar', + publicKey: 'GDXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', + encryptedSecret: 'encrypted-dev-secret', + isPrimary: true, + }, + }, + }, + }); + + const customer = await prisma.customer.upsert({ + where: { + merchantId_externalRef: { + merchantId: merchant.id, + externalRef: 'cust_dev_001', + }, + }, + update: { email: 'customer@stellar-pay.local' }, + create: { + merchantId: merchant.id, + externalRef: 'cust_dev_001', + email: 'customer@stellar-pay.local', + name: 'Dev Customer', + metadata: { source: 'seed' }, + }, + }); + + const paymentIntent = await prisma.paymentIntent.create({ + data: { + merchantId: merchant.id, + customerId: customer.id, + amountMinor: 2500, + currency: 'USD', + description: 'Seeded payment intent', + idempotencyKey: `seed-${Date.now()}`, + status: 'PROCESSING', + }, + }); + + await prisma.escrowTransaction.create({ + data: { + paymentIntentId: paymentIntent.id, + network: 'stellar', + status: 'FUNDED', + }, + }); + + const now = new Date(); + const periodEnd = new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000); + + const subscription = await prisma.subscription.create({ + data: { + merchantId: merchant.id, + customerId: customer.id, + planCode: 'starter-monthly', + amountMinor: 1999, + currency: 'USD', + interval: 'MONTH', + status: 'ACTIVE', + currentPeriodStart: now, + currentPeriodEnd: periodEnd, + }, + }); + + await prisma.subscriptionInvoice.create({ + data: { + subscriptionId: subscription.id, + externalPaymentIntentId: paymentIntent.id, + amountMinor: 1999, + currency: 'USD', + status: 'PENDING', + dueAt: periodEnd, + }, + }); + + await prisma.payout.create({ + data: { + merchantId: merchant.id, + amountMinor: 1000, + currency: 'USD', + destination: 'GAXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', + status: 'REQUESTED', + }, + }); + + await prisma.webhookEvent.create({ + data: { + merchantId: merchant.id, + provider: 'stripe', + eventType: 'payment_intent.succeeded', + payload: { demo: true }, + status: 'RECEIVED', + }, + }); + + await prisma.auditLog.create({ + data: { + actorType: 'system', + action: 'seed.completed', + entityType: 'Merchant', + entityId: merchant.id, + metadata: { seededAt: now.toISOString() }, + }, + }); +} + +main() + .then(async () => { + if (prisma) { + await prisma.$disconnect(); + } + globalThis.console.log('Database seed completed.'); + }) + .catch(async (error) => { + if (prisma) { + await prisma.$disconnect(); + } + globalThis.console.error(error); + globalThis.process.exit(1); + }); diff --git a/packages/database/migrations/test-migrate.sh b/packages/database/migrations/test-migrate.sh new file mode 100755 index 0000000..6614911 --- /dev/null +++ b/packages/database/migrations/test-migrate.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [[ -z "${TEST_DATABASE_URL:-}" ]]; then + echo "TEST_DATABASE_URL is required for test migrations." >&2 + exit 1 +fi + +export DATABASE_URL="${TEST_DATABASE_URL}" +export SHADOW_DATABASE_URL="${TEST_SHADOW_DATABASE_URL:-$TEST_DATABASE_URL}" + +pnpm exec prisma migrate deploy --schema ./migrations/schema.prisma +pnpm exec prisma generate --schema ./migrations/schema.prisma +pnpm exec prisma db seed --schema ./migrations/schema.prisma diff --git a/packages/database/package.json b/packages/database/package.json new file mode 100644 index 0000000..531f475 --- /dev/null +++ b/packages/database/package.json @@ -0,0 +1,25 @@ +{ + "name": "@stellar-pay/database", + "version": "0.1.0", + "private": true, + "main": "./src/index.ts", + "types": "./src/index.ts", + "scripts": { + "db:generate": "prisma generate --schema ./migrations/schema.prisma", + "db:migrate:dev": "prisma migrate dev --schema ./migrations/schema.prisma", + "db:migrate:status": "prisma migrate status --schema ./migrations/schema.prisma", + "db:migrate:deploy": "bash ./migrations/deploy.sh", + "db:seed": "prisma db seed --schema ./migrations/schema.prisma", + "db:push": "prisma db push --schema ./migrations/schema.prisma", + "db:test:migrate": "bash ./migrations/test-migrate.sh" + }, + "prisma": { + "seed": "node ./migrations/seed.js" + }, + "dependencies": { + "@prisma/client": "^6.6.0" + }, + "devDependencies": { + "prisma": "^6.6.0" + } +} diff --git a/packages/database/src/index.ts b/packages/database/src/index.ts new file mode 100644 index 0000000..acebf64 --- /dev/null +++ b/packages/database/src/index.ts @@ -0,0 +1 @@ +export { PrismaClient } from '@prisma/client'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c6c0e90..ae3d655 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -100,7 +100,7 @@ importers: version: 11.2.6(@nestjs/common@11.1.14(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)(reflect-metadata@0.2.2) '@nestjs/terminus': specifier: ^11.1.1 - version: 11.1.1(@nestjs/common@11.1.14(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)(reflect-metadata@0.2.2)(rxjs@7.8.2) + version: 11.1.1(@nestjs/common@11.1.14(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)(@prisma/client@6.19.2(prisma@6.19.2(typescript@5.9.3))(typescript@5.9.3))(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/throttler': specifier: ^6.5.0 version: 6.5.0(@nestjs/common@11.1.14(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)(reflect-metadata@0.2.2) @@ -428,6 +428,16 @@ importers: specifier: ^5.0.0 version: 5.9.3 + packages/database: + dependencies: + '@prisma/client': + specifier: ^6.6.0 + version: 6.19.2(prisma@6.19.2(typescript@5.9.3))(typescript@5.9.3) + devDependencies: + prisma: + specifier: ^6.6.0 + version: 6.19.2(typescript@5.9.3) + packages/escrow: devDependencies: tsup: @@ -2547,6 +2557,57 @@ packages: integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==, } + '@prisma/client@6.19.2': + resolution: + { + integrity: sha512-gR2EMvfK/aTxsuooaDA32D8v+us/8AAet+C3J1cc04SW35FPdZYgLF+iN4NDLUgAaUGTKdAB0CYenu1TAgGdMg==, + } + engines: { node: '>=18.18' } + peerDependencies: + prisma: '*' + typescript: '>=5.1.0' + peerDependenciesMeta: + prisma: + optional: true + typescript: + optional: true + + '@prisma/config@6.19.2': + resolution: + { + integrity: sha512-kadBGDl+aUswv/zZMk9Mx0C8UZs1kjao8H9/JpI4Wh4SHZaM7zkTwiKn/iFLfRg+XtOAo/Z/c6pAYhijKl0nzQ==, + } + + '@prisma/debug@6.19.2': + resolution: + { + integrity: sha512-lFnEZsLdFLmEVCVNdskLDCL8Uup41GDfU0LUfquw+ercJC8ODTuL0WNKgOKmYxCJVvFwf0OuZBzW99DuWmoH2A==, + } + + '@prisma/engines-version@7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7': + resolution: + { + integrity: sha512-03bgb1VD5gvuumNf+7fVGBzfpJPjmqV423l/WxsWk2cNQ42JD0/SsFBPhN6z8iAvdHs07/7ei77SKu7aZfq8bA==, + } + + '@prisma/engines@6.19.2': + resolution: + { + integrity: sha512-TTkJ8r+uk/uqczX40wb+ODG0E0icVsMgwCTyTHXehaEfb0uo80M9g1aW1tEJrxmFHeOZFXdI2sTA1j1AgcHi4A==, + } + + '@prisma/fetch-engine@6.19.2': + resolution: + { + integrity: sha512-h4Ff4Pho+SR1S8XerMCC12X//oY2bG3Iug/fUnudfcXEUnIeRiBdXHFdGlGOgQ3HqKgosTEhkZMvGM9tWtYC+Q==, + } + + '@prisma/get-platform@6.19.2': + resolution: + { + integrity: sha512-PGLr06JUSTqIvztJtAzIxOwtWKtJm5WwOG6xpsgD37Rc84FpfUBGLKz65YpJBGtkRQGXTYEFie7pYALocC3MtA==, + } + '@radix-ui/number@1.1.0': resolution: { @@ -4456,6 +4517,12 @@ packages: integrity: sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==, } + '@standard-schema/spec@1.1.0': + resolution: + { + integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==, + } + '@stellar/js-xdr@3.1.2': resolution: { @@ -5881,6 +5948,17 @@ packages: } engines: { node: '>= 0.8' } + c12@3.1.0: + resolution: + { + integrity: sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==, + } + peerDependencies: + magicast: ^0.3.5 + peerDependenciesMeta: + magicast: + optional: true + cac@6.7.14: resolution: { @@ -5991,6 +6069,18 @@ packages: } engines: { node: '>=8' } + citty@0.1.6: + resolution: + { + integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==, + } + + citty@0.2.1: + resolution: + { + integrity: sha512-kEV95lFBhQgtogAPlQfJJ0WGVSokvLr/UEoFPiKKOXF7pl98HfUVUD0ejsuTCld/9xH9vogSywZ5KqHzXrZpqg==, + } + cjs-module-lexer@2.2.0: resolution: { @@ -6198,6 +6288,12 @@ packages: integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==, } + confbox@0.2.4: + resolution: + { + integrity: sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==, + } + consola@3.4.2: resolution: { @@ -6492,6 +6588,13 @@ packages: integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==, } + deepmerge-ts@7.1.5: + resolution: + { + integrity: sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==, + } + engines: { node: '>=16.0.0' } + deepmerge@4.3.1: resolution: { @@ -6540,6 +6643,12 @@ packages: } engines: { node: '>= 0.4' } + defu@6.1.4: + resolution: + { + integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==, + } + delayed-stream@1.0.0: resolution: { @@ -6554,6 +6663,12 @@ packages: } engines: { node: '>= 0.8' } + destr@2.0.5: + resolution: + { + integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==, + } + detect-libc@2.1.2: resolution: { @@ -6613,6 +6728,13 @@ packages: integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==, } + dotenv@16.6.1: + resolution: + { + integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==, + } + engines: { node: '>=12' } + dotenv@17.3.1: resolution: { @@ -6652,6 +6774,12 @@ packages: integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==, } + effect@3.18.4: + resolution: + { + integrity: sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA==, + } + electron-to-chromium@1.5.302: resolution: { @@ -6705,6 +6833,13 @@ packages: integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==, } + empathic@2.0.0: + resolution: + { + integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==, + } + engines: { node: '>=14' } + encodeurl@2.0.0: resolution: { @@ -7149,6 +7284,19 @@ packages: } engines: { node: '>= 18' } + exsolve@1.0.8: + resolution: + { + integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==, + } + + fast-check@3.23.2: + resolution: + { + integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==, + } + engines: { node: '>=8.0.0' } + fast-deep-equal@3.1.3: resolution: { @@ -7560,6 +7708,13 @@ packages: integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==, } + giget@2.0.0: + resolution: + { + integrity: sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==, + } + hasBin: true + glob-parent@5.1.2: resolution: { @@ -9328,6 +9483,12 @@ packages: } engines: { node: '>= 0.4' } + node-fetch-native@1.6.7: + resolution: + { + integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==, + } + node-fetch@3.3.2: resolution: { @@ -9368,6 +9529,14 @@ packages: } engines: { node: '>=18' } + nypm@0.6.5: + resolution: + { + integrity: sha512-K6AJy1GMVyfyMXRVB88700BJqNUkByijGJM8kEHpLdcAt+vSQAVfkWWHYzuRXHSY6xA2sNc5RjTj0p9rE2izVQ==, + } + engines: { node: '>=18' } + hasBin: true + object-assign@4.1.1: resolution: { @@ -9431,6 +9600,12 @@ packages: } engines: { node: '>= 0.4' } + ohash@2.0.11: + resolution: + { + integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==, + } + on-finished@2.4.1: resolution: { @@ -9679,6 +9854,12 @@ packages: integrity: sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==, } + perfect-debounce@1.0.0: + resolution: + { + integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==, + } + picocolors@1.1.1: resolution: { @@ -9741,6 +9922,12 @@ packages: integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==, } + pkg-types@2.3.0: + resolution: + { + integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==, + } + pluralize@8.0.0: resolution: { @@ -9840,6 +10027,19 @@ packages: } engines: { node: '>=18' } + prisma@6.19.2: + resolution: + { + integrity: sha512-XTKeKxtQElcq3U9/jHyxSPgiRgeYDKxWTPOf6NkXA0dNj5j40MfEsZkMbyNpwDWCUv7YBFUl7I2VK/6ALbmhEg==, + } + engines: { node: '>=18.18' } + hasBin: true + peerDependencies: + typescript: '>=5.1.0' + peerDependenciesMeta: + typescript: + optional: true + prompts@2.4.2: resolution: { @@ -9873,6 +10073,12 @@ packages: } engines: { node: '>=6' } + pure-rand@6.1.0: + resolution: + { + integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==, + } + pure-rand@7.0.1: resolution: { @@ -9936,6 +10142,12 @@ packages: } engines: { node: '>= 0.10' } + rc9@2.1.2: + resolution: + { + integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==, + } + react-day-picker@8.10.1: resolution: { @@ -13150,7 +13362,7 @@ snapshots: reflect-metadata: 0.2.2 swagger-ui-dist: 5.31.0 - '@nestjs/terminus@11.1.1(@nestjs/common@11.1.14(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)(reflect-metadata@0.2.2)(rxjs@7.8.2)': + '@nestjs/terminus@11.1.1(@nestjs/common@11.1.14(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)(@prisma/client@6.19.2(prisma@6.19.2(typescript@5.9.3))(typescript@5.9.3))(reflect-metadata@0.2.2)(rxjs@7.8.2)': dependencies: '@nestjs/common': 11.1.14(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/core': 11.1.14(@nestjs/common@11.1.14(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.14)(reflect-metadata@0.2.2)(rxjs@7.8.2) @@ -13158,6 +13370,8 @@ snapshots: check-disk-space: 3.4.0 reflect-metadata: 0.2.2 rxjs: 7.8.2 + optionalDependencies: + '@prisma/client': 6.19.2(prisma@6.19.2(typescript@5.9.3))(typescript@5.9.3) '@nestjs/testing@11.1.14(@nestjs/common@11.1.14(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)(@nestjs/platform-express@11.1.14)': dependencies: @@ -13249,6 +13463,41 @@ snapshots: '@popperjs/core@2.11.8': {} + '@prisma/client@6.19.2(prisma@6.19.2(typescript@5.9.3))(typescript@5.9.3)': + optionalDependencies: + prisma: 6.19.2(typescript@5.9.3) + typescript: 5.9.3 + + '@prisma/config@6.19.2': + dependencies: + c12: 3.1.0 + deepmerge-ts: 7.1.5 + effect: 3.18.4 + empathic: 2.0.0 + transitivePeerDependencies: + - magicast + + '@prisma/debug@6.19.2': {} + + '@prisma/engines-version@7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7': {} + + '@prisma/engines@6.19.2': + dependencies: + '@prisma/debug': 6.19.2 + '@prisma/engines-version': 7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7 + '@prisma/fetch-engine': 6.19.2 + '@prisma/get-platform': 6.19.2 + + '@prisma/fetch-engine@6.19.2': + dependencies: + '@prisma/debug': 6.19.2 + '@prisma/engines-version': 7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7 + '@prisma/get-platform': 6.19.2 + + '@prisma/get-platform@6.19.2': + dependencies: + '@prisma/debug': 6.19.2 + '@radix-ui/number@1.1.0': {} '@radix-ui/number@1.1.1': {} @@ -14748,6 +14997,8 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.1 + '@standard-schema/spec@1.1.0': {} + '@stellar/js-xdr@3.1.2': {} '@stellar/stellar-base@14.1.0': @@ -15666,6 +15917,21 @@ snapshots: bytes@3.1.2: {} + c12@3.1.0: + dependencies: + chokidar: 4.0.3 + confbox: 0.2.4 + defu: 6.1.4 + dotenv: 16.6.1 + exsolve: 1.0.8 + giget: 2.0.0 + jiti: 2.6.1 + ohash: 2.0.11 + pathe: 2.0.3 + perfect-debounce: 1.0.0 + pkg-types: 2.3.0 + rc9: 2.1.2 + cac@6.7.14: {} call-bind-apply-helpers@1.0.2: @@ -15714,6 +15980,12 @@ snapshots: ci-info@4.4.0: {} + citty@0.1.6: + dependencies: + consola: 3.4.2 + + citty@0.2.1: {} + cjs-module-lexer@2.2.0: {} class-variance-authority@0.7.1: @@ -15816,6 +16088,8 @@ snapshots: confbox@0.1.8: {} + confbox@0.2.4: {} + consola@3.4.2: {} content-disposition@1.0.1: {} @@ -15957,6 +16231,8 @@ snapshots: deep-is@0.1.4: {} + deepmerge-ts@7.1.5: {} + deepmerge@4.3.1: {} default-browser-id@5.0.1: {} @@ -15984,10 +16260,14 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 + defu@6.1.4: {} + delayed-stream@1.0.0: {} depd@2.0.0: {} + destr@2.0.5: {} + detect-libc@2.1.2: {} detect-newline@3.1.0: {} @@ -16018,6 +16298,8 @@ snapshots: '@babel/runtime': 7.28.6 csstype: 3.2.3 + dotenv@16.6.1: {} + dotenv@17.3.1: {} dunder-proto@1.0.1: @@ -16041,6 +16323,11 @@ snapshots: ee-first@1.1.1: {} + effect@3.18.4: + dependencies: + '@standard-schema/spec': 1.1.0 + fast-check: 3.23.2 + electron-to-chromium@1.5.302: {} embla-carousel-react@8.6.0(react@19.2.3): @@ -16063,6 +16350,8 @@ snapshots: emoji-regex@9.2.2: {} + empathic@2.0.0: {} + encodeurl@2.0.0: {} enhanced-resolve@5.20.0: @@ -16538,6 +16827,12 @@ snapshots: transitivePeerDependencies: - supports-color + exsolve@1.0.8: {} + + fast-check@3.23.2: + dependencies: + pure-rand: 6.1.0 + fast-deep-equal@3.1.3: {} fast-diff@1.3.0: {} @@ -16791,6 +17086,15 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 + giget@2.0.0: + dependencies: + citty: 0.1.6 + consola: 3.4.2 + defu: 6.1.4 + node-fetch-native: 1.6.7 + nypm: 0.6.5 + pathe: 2.0.3 + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -17933,6 +18237,8 @@ snapshots: object.entries: 1.1.9 semver: 6.3.1 + node-fetch-native@1.6.7: {} + node-fetch@3.3.2: dependencies: data-uri-to-buffer: 4.0.1 @@ -17954,6 +18260,12 @@ snapshots: path-key: 4.0.0 unicorn-magic: 0.3.0 + nypm@0.6.5: + dependencies: + citty: 0.2.1 + pathe: 2.0.3 + tinyexec: 1.0.2 + object-assign@4.1.1: {} object-inspect@1.13.4: {} @@ -17998,6 +18310,8 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 + ohash@2.0.11: {} + on-finished@2.4.1: dependencies: ee-first: 1.1.1 @@ -18146,6 +18460,8 @@ snapshots: pause@0.0.1: {} + perfect-debounce@1.0.0: {} + picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -18170,6 +18486,12 @@ snapshots: mlly: 1.8.2 pathe: 2.0.3 + pkg-types@2.3.0: + dependencies: + confbox: 0.2.4 + exsolve: 1.0.8 + pathe: 2.0.3 + pluralize@8.0.0: {} possible-typed-array-names@1.1.0: {} @@ -18219,6 +18541,15 @@ snapshots: dependencies: parse-ms: 4.0.0 + prisma@6.19.2(typescript@5.9.3): + dependencies: + '@prisma/config': 6.19.2 + '@prisma/engines': 6.19.2 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - magicast + prompts@2.4.2: dependencies: kleur: 3.0.3 @@ -18239,6 +18570,8 @@ snapshots: punycode@2.3.1: {} + pure-rand@6.1.0: {} + pure-rand@7.0.1: {} qrcode.react@4.2.0(react@19.2.3): @@ -18327,6 +18660,11 @@ snapshots: iconv-lite: 0.7.2 unpipe: 1.0.0 + rc9@2.1.2: + dependencies: + defu: 6.1.4 + destr: 2.0.5 + react-day-picker@8.10.1(date-fns@3.6.0)(react@19.2.3): dependencies: date-fns: 3.6.0