chore(db): finish Prisma cutover to Drizzle Kit tooling
Move CMS SQL to drizzle/migrations, drop prisma packages/schema, wire drizzle-kit scripts, and regenerate schema names from src/db/schema.ts. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -43,7 +43,7 @@ jobs:
|
|||||||
export REDIS_URL="redis://127.0.0.1:6379?connect_timeout=1"
|
export REDIS_URL="redis://127.0.0.1:6379?connect_timeout=1"
|
||||||
export BCRYPT_ROUNDS=4
|
export BCRYPT_ROUNDS=4
|
||||||
pnpm install --frozen-lockfile
|
pnpm install --frozen-lockfile
|
||||||
# Types come from the committed Drizzle schema (src/db/schema.ts); no prisma:generate.
|
# Types come from the committed Drizzle schema (src/db/schema.ts).
|
||||||
pnpm biome:lint
|
pnpm biome:lint
|
||||||
pnpm typecheck
|
pnpm typecheck
|
||||||
pnpm test
|
pnpm test
|
||||||
@@ -139,7 +139,7 @@ jobs:
|
|||||||
echo "STAGE DATABASE_POOL_SIZE=${DATABASE_POOL_SIZE}"
|
echo "STAGE DATABASE_POOL_SIZE=${DATABASE_POOL_SIZE}"
|
||||||
|
|
||||||
pnpm install --frozen-lockfile
|
pnpm install --frozen-lockfile
|
||||||
# Types come from the committed Drizzle schema (src/db/schema.ts); no prisma:generate.
|
# Types come from the committed Drizzle schema (src/db/schema.ts).
|
||||||
export BCRYPT_ROUNDS=4
|
export BCRYPT_ROUNDS=4
|
||||||
pnpm typecheck
|
pnpm typecheck
|
||||||
# Validate production env (AUTH_SECRET, DATABASE_URL, …) during build.
|
# Validate production env (AUTH_SECRET, DATABASE_URL, …) during build.
|
||||||
@@ -292,7 +292,7 @@ jobs:
|
|||||||
export NODE_ENV=production
|
export NODE_ENV=production
|
||||||
export SKIP_ENV_VALIDATION=1
|
export SKIP_ENV_VALIDATION=1
|
||||||
pnpm install --frozen-lockfile
|
pnpm install --frozen-lockfile
|
||||||
# Types come from the committed Drizzle schema (src/db/schema.ts); no prisma:generate.
|
# Types come from the committed Drizzle schema (src/db/schema.ts).
|
||||||
# Tag releases must apply CMS SQL migrations against the live DB
|
# Tag releases must apply CMS SQL migrations against the live DB
|
||||||
# (same path as push-to-main deploy), using the production .env.
|
# (same path as push-to-main deploy), using the production .env.
|
||||||
LIVE="/var/www/atom-nexst"
|
LIVE="/var/www/atom-nexst"
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ node_modules/
|
|||||||
next-env.d.ts
|
next-env.d.ts
|
||||||
.env
|
.env
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
# Prisma client is generated by `prisma generate`
|
|
||||||
src/generated/
|
src/generated/
|
||||||
# Runtime avatar/badge imaging disk cache
|
# Runtime avatar/badge imaging disk cache
|
||||||
public/cache/
|
public/cache/
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ CREATE DATABASE IF NOT EXISTS epicnext_cms CHARACTER SET utf8mb4 COLLATE utf8mb4
|
|||||||
|
|
||||||
The CMS reads emulator-owned tables (`users`, `items`, `rooms`, `bans`, etc.) directly. It never creates, alters, or drops them. The Drizzle schema in `src/db/schema.ts` is generated from the existing database structure and does not modify it.
|
The CMS reads emulator-owned tables (`users`, `items`, `rooms`, `bans`, etc.) directly. It never creates, alters, or drops them. The Drizzle schema in `src/db/schema.ts` is generated from the existing database structure and does not modify it.
|
||||||
|
|
||||||
> **Note:** The CMS does **not** own the database schema — it maps to tables that are managed by the emulator. All Drizzle schema definitions use `drizzle-orm`'s runtime mapping (no `drizzle-kit push/migrate` is ever run against the emulator schema). CMS-owned tables (`website_*`, `radio_*`, etc.) are created via idempotent SQL files in `prisma/migrations/`.
|
> **Note:** The CMS does **not** own the emulator schema — it maps to those tables via Drizzle. Never run `drizzle-kit push` / `migrate` against the shared DB. CMS-owned tables (`website_*`, `radio_*`, etc.) are created via idempotent SQL in `drizzle/migrations/` (`pnpm db:migrate`).
|
||||||
|
|
||||||
### 3. Configure Environment
|
### 3. Configure Environment
|
||||||
|
|
||||||
@@ -78,23 +78,22 @@ const found = await db.select()
|
|||||||
|
|
||||||
| Command | What it does |
|
| Command | What it does |
|
||||||
| ------- | ------------ |
|
| ------- | ------------ |
|
||||||
| `npx drizzle-kit generate --dialect mysql --schema src/db/schema.ts --out src/db/migrations` | Inspect the Drizzle schema and emit migration SQL |
|
| `pnpm db:generate` | Draft SQL from Drizzle schema into `drizzle/drafts/` (review + copy into `drizzle/migrations/`) |
|
||||||
| `npx drizzle-kit studio` | Open a local DB browser (dev only) |
|
| `pnpm db:studio` | Open Drizzle Studio (dev only) |
|
||||||
| `npx drizzle-kit introspect` | Reverse-engineer an existing DB into a Drizzle schema |
|
| `pnpm db:introspect` | Reverse-engineer an existing DB into a Drizzle schema draft |
|
||||||
|
| `pnpm db:schema:generate` | Regen committed `src/db/schema.ts` from previous schema names + live DB |
|
||||||
|
|
||||||
> The CMS does **not** use `drizzle-kit push` — the database is owned by the emulator and is never auto-migrated. CMS-owned tables are created via the SQL migration runner (step 5).
|
> The CMS does **not** use `drizzle-kit push` or `drizzle-kit migrate` — the database is shared with the emulator. Apply CMS DDL only via `pnpm db:migrate`.
|
||||||
|
|
||||||
Use `import { db } from "@/lib/db"` with table definitions from `src/db/schema.ts` for all database access. Types come from the committed Drizzle schema — no separate client code generation is required at build time.
|
Use `import { db } from "@/lib/db"` with table definitions from `src/db/schema.ts` for all database access. Types come from the committed Drizzle schema — no separate client code generation is required at build time.
|
||||||
|
|
||||||
`prisma/schema.prisma` remains as a reference model list used by `pnpm db:schema:generate` when regenerating the Drizzle schema from the live database. The Prisma client is not used at runtime.
|
|
||||||
|
|
||||||
### 5. Run CMS Migrations
|
### 5. Run CMS Migrations
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pnpm db:migrate
|
pnpm db:migrate
|
||||||
```
|
```
|
||||||
|
|
||||||
Creates all CMS-owned tables (`website_*`, `radio_*`, `acl_*`, `admin_audit_log`, etc.) via idempotent SQL files in `prisma/migrations/`. Emulator tables are never touched.
|
Creates all CMS-owned tables (`website_*`, `radio_*`, `acl_*`, `admin_audit_log`, etc.) via idempotent SQL files in `drizzle/migrations/`. Emulator tables are never touched.
|
||||||
|
|
||||||
Check migration status:
|
Check migration status:
|
||||||
|
|
||||||
@@ -154,17 +153,15 @@ The CMS runs behind an nginx reverse proxy on the default port 3000. Static asse
|
|||||||
| `pnpm test` | Run all tests (Vitest) |
|
| `pnpm test` | Run all tests (Vitest) |
|
||||||
| `pnpm db:migrate` | Apply pending SQL migrations |
|
| `pnpm db:migrate` | Apply pending SQL migrations |
|
||||||
| `pnpm db:migrate:status` | Show migration status |
|
| `pnpm db:migrate:status` | Show migration status |
|
||||||
| `pnpm db:schema:generate` | Regen `src/db/schema.ts` from Prisma + live DB (needs `DATABASE_URL`) |
|
| `pnpm db:schema:generate` | Regen `src/db/schema.ts` from prior schema + live DB |
|
||||||
|
| `pnpm db:generate` | Draft SQL via drizzle-kit → `drizzle/drafts/` |
|
||||||
|
| `pnpm db:studio` | Drizzle Studio (dev) |
|
||||||
|
| `pnpm db:introspect` | drizzle-kit introspect (draft) |
|
||||||
| `pnpm analyze` | Build + open bundle analyzer |
|
| `pnpm analyze` | Build + open bundle analyzer |
|
||||||
| `pnpm jobs:worker` | Start background task worker (systemd / PM2) |
|
| `pnpm jobs:worker` | Start background task worker (systemd / PM2) |
|
||||||
| `pnpm biome:check` | Lint and format code |
|
| `pnpm biome:check` | Lint and format code |
|
||||||
|
|
||||||
**Drizzle CLI (dev only, run with `npx`):**
|
**Drizzle Kit notes:** `db:generate` / `db:introspect` write drafts only. Reviewed SQL must be copied into `drizzle/migrations/` as a new numbered file, then applied with `pnpm db:migrate`. Never run `drizzle-kit push` or `drizzle-kit migrate` against production.
|
||||||
| Command | Description |
|
|
||||||
| ------- | ----------- |
|
|
||||||
| `drizzle-kit generate` | Generate migration SQL from Drizzle schema |
|
|
||||||
| `drizzle-kit studio` | Local Drizzle Studio database browser |
|
|
||||||
| `drizzle-kit introspect` | Reverse-engineer DB → Drizzle schema |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -192,18 +189,18 @@ The CMS runs behind an nginx reverse proxy on the default port 3000. Static asse
|
|||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
```
|
```
|
||||||
├── prisma/
|
├── drizzle/
|
||||||
│ ├── schema.prisma # Reference model list — input for db:schema:generate
|
│ ├── migrations/ # CMS SQL migrations (idempotent, tracked in cms_migrations)
|
||||||
│ └── migrations/ # 19 SQL migrations for CMS tables (idempotent, never re-run)
|
│ └── drafts/ # drizzle-kit generate output (never auto-applied)
|
||||||
├── scripts/
|
├── scripts/
|
||||||
│ ├── apply-migrations.ts # SQL migration runner (apply + status)
|
│ ├── apply-migrations.ts # SQL migration runner (apply + status)
|
||||||
│ ├── jobs-worker.ts # Background task scheduler
|
│ ├── jobs-worker.ts # Background task scheduler
|
||||||
│ ├── merge-config.cjs # Utility: merge split config files
|
│ ├── merge-config.cjs # Utility: merge split config files
|
||||||
│ └── generate-drizzle-schema.mjs # Generate src/db/schema.ts from schema.prisma + live DB
|
│ └── generate-drizzle-schema.mjs # Regen src/db/schema.ts from schema + live DB
|
||||||
├── src/
|
├── src/
|
||||||
│ ├── db/
|
│ ├── db/
|
||||||
│ │ ├── schema.ts # Drizzle ORM schema (176 tables — runtime data layer)
|
│ │ ├── schema.ts # Drizzle ORM schema (committed — runtime data layer)
|
||||||
│ │ └── migrations/ # Drizzle migration files (local dev only)
|
│ │ └── relations.ts # Drizzle relations
|
||||||
│ ├── app/ # Next.js App Router (pages & API routes)
|
│ ├── app/ # Next.js App Router (pages & API routes)
|
||||||
│ ├── actions/ # Server Actions
|
│ ├── actions/ # Server Actions
|
||||||
│ ├── components/ # UI components
|
│ ├── components/ # UI components
|
||||||
@@ -232,13 +229,14 @@ The CMS runs behind an nginx reverse proxy on the default port 3000. Static asse
|
|||||||
| Component | Type | Migrations |
|
| Component | Type | Migrations |
|
||||||
| ------------------------------------------------- | ----------------------- | ----------------------------------- |
|
| ------------------------------------------------- | ----------------------- | ----------------------------------- |
|
||||||
| Emulator tables (`users`, `items`, `rooms`, etc.) | Existing Polaris schema | None — CMS reads/writes only |
|
| Emulator tables (`users`, `items`, `rooms`, etc.) | Existing Polaris schema | None — CMS reads/writes only |
|
||||||
| CMS tables (`website_*`, `radio_*`, etc.) | CMS-owned | `prisma/migrations/*.sql` (19 files) |
|
| CMS tables (`website_*`, `radio_*`, etc.) | CMS-owned | `drizzle/migrations/*.sql` |
|
||||||
| Migration tracking | `cms_migrations` table | Auto-created by migration runner |
|
| Migration tracking | `cms_migrations` table | Auto-created by migration runner |
|
||||||
|
|
||||||
### ORM Architecture
|
### ORM Architecture
|
||||||
|
|
||||||
- **Runtime (Drizzle ORM)**: `@/lib/db` exposes a Drizzle singleton connected to the MySQL/MariaDB database. All application and script code uses this directly. The schema is defined in `src/db/schema.ts` with typed columns against the existing database.
|
- **Runtime (Drizzle ORM)**: `@/lib/db` exposes a Drizzle singleton. Schema lives in `src/db/schema.ts`.
|
||||||
- **Schema regeneration**: `pnpm db:schema:generate` reads `prisma/schema.prisma` (for camelCase field names) plus the live DB and rewrites `src/db/schema.ts`. Types for the app come from that committed Drizzle schema — no Prisma client is loaded at runtime.
|
- **Schema regeneration**: `pnpm db:schema:generate` reuses field/table names from the previous `src/db/schema.ts` and refreshes column types from the live DB.
|
||||||
|
- **Drizzle Kit**: studio / generate / introspect for local tooling; CMS apply path remains `pnpm db:migrate`.
|
||||||
|
|
||||||
Use `import { db } from "@/lib/db"` with queries built via `src/db/schema.ts`.
|
Use `import { db } from "@/lib/db"` with queries built via `src/db/schema.ts`.
|
||||||
|
|
||||||
@@ -304,7 +302,7 @@ pnpm biome:check # Lint and format
|
|||||||
1. Ensure typecheck and tests pass: `pnpm typecheck && pnpm test`
|
1. Ensure typecheck and tests pass: `pnpm typecheck && pnpm test`
|
||||||
2. Follow existing code conventions (Server Components where possible, minimal client boundaries)
|
2. Follow existing code conventions (Server Components where possible, minimal client boundaries)
|
||||||
3. Use the `src/lib/motion.ts` animation variants for consistent animations
|
3. Use the `src/lib/motion.ts` animation variants for consistent animations
|
||||||
4. SQL migrations in `prisma/migrations/` must be idempotent
|
4. SQL migrations in `drizzle/migrations/` must be idempotent
|
||||||
5. For new database code, use the Drizzle runtime directly (`import { db } from "@/lib/db"`) — see [ORM Setup](#4-orm-setup--type-generation)
|
5. For new database code, use the Drizzle runtime directly (`import { db } from "@/lib/db"`) — see [ORM Setup](#4-orm-setup--type-generation)
|
||||||
6. Avoid `any` — use `eslint-disable` or `biome-ignore` comments only when unavoidable
|
6. Avoid `any` — use `eslint-disable` or `biome-ignore` comments only when unavoidable
|
||||||
|
|
||||||
|
|||||||
+7
-4
@@ -1,13 +1,16 @@
|
|||||||
import "dotenv/config";
|
import "dotenv/config";
|
||||||
import { defineConfig } from "drizzle-kit";
|
import { defineConfig } from "drizzle-kit";
|
||||||
|
|
||||||
// The schema is generated by scripts/generate-drizzle-schema.mjs from
|
// Schema source of truth for the query builder: src/db/schema.ts
|
||||||
// prisma/schema.prisma + live DB introspection. This DB is shared live with the
|
// (regenerated via `pnpm db:schema:generate` from the previous schema + live DB).
|
||||||
// Arcturus emulator, so we NEVER run `drizzle-kit migrate`/`push` against it —
|
//
|
||||||
// CMS-only schema changes stay in prisma/migrations/*.sql via `pnpm db:migrate`.
|
// This DB is shared with the Arcturus emulator — NEVER run `drizzle-kit migrate`
|
||||||
|
// or `push` against it. CMS DDL stays in drizzle/migrations/*.sql applied by
|
||||||
|
// `pnpm db:migrate`. Use `pnpm db:generate` only for draft SQL under drizzle/drafts/.
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
dialect: "mysql",
|
dialect: "mysql",
|
||||||
schema: "./src/db/schema.ts",
|
schema: "./src/db/schema.ts",
|
||||||
|
out: "./drizzle/drafts",
|
||||||
dbCredentials: {
|
dbCredentials: {
|
||||||
url: process.env.DATABASE_URL ?? "",
|
url: process.env.DATABASE_URL ?? "",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
Draft SQL from `pnpm db:generate` (drizzle-kit). Never apply these automatically — copy reviewed statements into drizzle/migrations/ as numbered CMS migrations, then `pnpm db:migrate`.
|
||||||
+3
-3
@@ -10,7 +10,6 @@
|
|||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"prisma:generate": "prisma generate",
|
|
||||||
"typecheck": "tsc --noEmit --incremental",
|
"typecheck": "tsc --noEmit --incremental",
|
||||||
"biome:check": "biome check --write .",
|
"biome:check": "biome check --write .",
|
||||||
"biome:lint": "biome lint .",
|
"biome:lint": "biome lint .",
|
||||||
@@ -21,6 +20,9 @@
|
|||||||
"db:migrate": "tsx scripts/apply-migrations.ts",
|
"db:migrate": "tsx scripts/apply-migrations.ts",
|
||||||
"db:migrate:status": "tsx scripts/apply-migrations.ts --status",
|
"db:migrate:status": "tsx scripts/apply-migrations.ts --status",
|
||||||
"db:schema:generate": "node scripts/generate-drizzle-schema.mjs",
|
"db:schema:generate": "node scripts/generate-drizzle-schema.mjs",
|
||||||
|
"db:generate": "drizzle-kit generate",
|
||||||
|
"db:studio": "drizzle-kit studio",
|
||||||
|
"db:introspect": "drizzle-kit introspect",
|
||||||
"jobs:worker": "tsx scripts/jobs-worker.ts",
|
"jobs:worker": "tsx scripts/jobs-worker.ts",
|
||||||
"prepare": "husky"
|
"prepare": "husky"
|
||||||
},
|
},
|
||||||
@@ -76,7 +78,6 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "2.5.6",
|
"@biomejs/biome": "2.5.6",
|
||||||
"@next/bundle-analyzer": "^16.2.12",
|
"@next/bundle-analyzer": "^16.2.12",
|
||||||
"@prisma/client": "^7.9.1",
|
|
||||||
"@tailwindcss/forms": "^0.5.11",
|
"@tailwindcss/forms": "^0.5.11",
|
||||||
"@tailwindcss/postcss": "^4.3.3",
|
"@tailwindcss/postcss": "^4.3.3",
|
||||||
"@tailwindcss/typography": "^0.5.20",
|
"@tailwindcss/typography": "^0.5.20",
|
||||||
@@ -92,7 +93,6 @@
|
|||||||
"lint-staged": "^17.3.0",
|
"lint-staged": "^17.3.0",
|
||||||
"pino-pretty": "^13.1.3",
|
"pino-pretty": "^13.1.3",
|
||||||
"postcss": "^8.5.25",
|
"postcss": "^8.5.25",
|
||||||
"prisma": "^7.9.1",
|
|
||||||
"tailwindcss": "^4.3.3",
|
"tailwindcss": "^4.3.3",
|
||||||
"tsx": "^4.23.1",
|
"tsx": "^4.23.1",
|
||||||
"typescript": "^7.0.2",
|
"typescript": "^7.0.2",
|
||||||
|
|||||||
Generated
+154
-56
@@ -144,9 +144,6 @@ importers:
|
|||||||
'@next/bundle-analyzer':
|
'@next/bundle-analyzer':
|
||||||
specifier: ^16.2.12
|
specifier: ^16.2.12
|
||||||
version: 16.2.12
|
version: 16.2.12
|
||||||
'@prisma/client':
|
|
||||||
specifier: ^7.9.1
|
|
||||||
version: 7.9.1([email protected](@types/[email protected](@types/[email protected]))(@types/[email protected])([email protected])([email protected]([email protected]))([email protected])([email protected]))([email protected])
|
|
||||||
'@tailwindcss/forms':
|
'@tailwindcss/forms':
|
||||||
specifier: ^0.5.11
|
specifier: ^0.5.11
|
||||||
version: 0.5.11([email protected])
|
version: 0.5.11([email protected])
|
||||||
@@ -192,9 +189,6 @@ importers:
|
|||||||
postcss:
|
postcss:
|
||||||
specifier: ^8.5.25
|
specifier: ^8.5.25
|
||||||
version: 8.5.25
|
version: 8.5.25
|
||||||
prisma:
|
|
||||||
specifier: ^7.9.1
|
|
||||||
version: 7.9.1(@types/[email protected](@types/[email protected]))(@types/[email protected])([email protected])([email protected]([email protected]))([email protected])([email protected])
|
|
||||||
tailwindcss:
|
tailwindcss:
|
||||||
specifier: ^4.3.3
|
specifier: ^4.3.3
|
||||||
version: 4.3.3
|
version: 4.3.3
|
||||||
@@ -5275,12 +5269,15 @@ snapshots:
|
|||||||
'@electric-sql/[email protected](@electric-sql/[email protected])':
|
'@electric-sql/[email protected](@electric-sql/[email protected])':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@electric-sql/pglite': 0.4.3
|
'@electric-sql/pglite': 0.4.3
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@electric-sql/[email protected](@electric-sql/[email protected])':
|
'@electric-sql/[email protected](@electric-sql/[email protected])':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@electric-sql/pglite': 0.4.3
|
'@electric-sql/pglite': 0.4.3
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@electric-sql/[email protected]': {}
|
'@electric-sql/[email protected]':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@emnapi/[email protected]':
|
'@emnapi/[email protected]':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -6053,7 +6050,8 @@ snapshots:
|
|||||||
|
|
||||||
'@polka/[email protected]': {}
|
'@polka/[email protected]': {}
|
||||||
|
|
||||||
'@prisma/[email protected]': {}
|
'@prisma/[email protected]':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@prisma/[email protected]([email protected](@types/[email protected](@types/[email protected]))(@types/[email protected])([email protected])([email protected]([email protected]))([email protected])([email protected]))([email protected])':
|
'@prisma/[email protected]([email protected](@types/[email protected](@types/[email protected]))(@types/[email protected])([email protected])([email protected]([email protected]))([email protected])([email protected]))([email protected])':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -6061,6 +6059,7 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
prisma: 7.9.1(@types/[email protected](@types/[email protected]))(@types/[email protected])([email protected])([email protected]([email protected]))([email protected])([email protected])
|
prisma: 7.9.1(@types/[email protected](@types/[email protected]))(@types/[email protected])([email protected])([email protected]([email protected]))([email protected])([email protected])
|
||||||
typescript: 7.0.2
|
typescript: 7.0.2
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@prisma/[email protected]([email protected])':
|
'@prisma/[email protected]([email protected])':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -6070,10 +6069,13 @@ snapshots:
|
|||||||
empathic: 2.0.0
|
empathic: 2.0.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- magicast
|
- magicast
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@prisma/[email protected]': {}
|
'@prisma/[email protected]':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@prisma/[email protected]': {}
|
'@prisma/[email protected]':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@prisma/[email protected]([email protected])':
|
'@prisma/[email protected]([email protected])':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -6094,8 +6096,10 @@ snapshots:
|
|||||||
zeptomatch: 2.1.0
|
zeptomatch: 2.1.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- typescript
|
- typescript
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@prisma/[email protected]': {}
|
'@prisma/[email protected]':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@prisma/[email protected]':
|
'@prisma/[email protected]':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -6103,22 +6107,27 @@ snapshots:
|
|||||||
'@prisma/engines-version': 7.9.0-1.e922089b7d7502aff4249d5da3420f6fa55fc6ad
|
'@prisma/engines-version': 7.9.0-1.e922089b7d7502aff4249d5da3420f6fa55fc6ad
|
||||||
'@prisma/fetch-engine': 7.9.1
|
'@prisma/fetch-engine': 7.9.1
|
||||||
'@prisma/get-platform': 7.9.1
|
'@prisma/get-platform': 7.9.1
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@prisma/[email protected]':
|
'@prisma/[email protected]':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@prisma/debug': 7.9.1
|
'@prisma/debug': 7.9.1
|
||||||
'@prisma/engines-version': 7.9.0-1.e922089b7d7502aff4249d5da3420f6fa55fc6ad
|
'@prisma/engines-version': 7.9.0-1.e922089b7d7502aff4249d5da3420f6fa55fc6ad
|
||||||
'@prisma/get-platform': 7.9.1
|
'@prisma/get-platform': 7.9.1
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@prisma/[email protected]':
|
'@prisma/[email protected]':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@prisma/debug': 7.2.0
|
'@prisma/debug': 7.2.0
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@prisma/[email protected]':
|
'@prisma/[email protected]':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@prisma/debug': 7.9.1
|
'@prisma/debug': 7.9.1
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@prisma/[email protected]': {}
|
'@prisma/[email protected]':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@prisma/[email protected]':
|
'@prisma/[email protected]':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -6126,6 +6135,7 @@ snapshots:
|
|||||||
better-result: 2.10.0
|
better-result: 2.10.0
|
||||||
env-paths: 3.0.0
|
env-paths: 3.0.0
|
||||||
proper-lockfile: 4.1.2
|
proper-lockfile: 4.1.2
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@prisma/[email protected](@types/[email protected](@types/[email protected]))(@types/[email protected])([email protected]([email protected]))([email protected])':
|
'@prisma/[email protected](@types/[email protected](@types/[email protected]))(@types/[email protected])([email protected]([email protected]))([email protected])':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -6145,8 +6155,10 @@ snapshots:
|
|||||||
react-dom: 19.2.8([email protected])
|
react-dom: 19.2.8([email protected])
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@types/react-dom'
|
- '@types/react-dom'
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@radix-ui/[email protected]': {}
|
'@radix-ui/[email protected]':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@radix-ui/[email protected]': {}
|
'@radix-ui/[email protected]': {}
|
||||||
|
|
||||||
@@ -6155,6 +6167,7 @@ snapshots:
|
|||||||
react: 19.2.8
|
react: 19.2.8
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/react': 19.2.18
|
'@types/react': 19.2.18
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@radix-ui/[email protected](@types/[email protected])([email protected])':
|
'@radix-ui/[email protected](@types/[email protected])([email protected])':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -6264,6 +6277,7 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/react': 19.2.18
|
'@types/react': 19.2.18
|
||||||
'@types/react-dom': 19.2.4(@types/[email protected])
|
'@types/react-dom': 19.2.4(@types/[email protected])
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@radix-ui/[email protected](@types/[email protected])([email protected])':
|
'@radix-ui/[email protected](@types/[email protected])([email protected])':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -6271,6 +6285,7 @@ snapshots:
|
|||||||
react: 19.2.8
|
react: 19.2.8
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/react': 19.2.18
|
'@types/react': 19.2.18
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@radix-ui/[email protected](@types/[email protected])([email protected])':
|
'@radix-ui/[email protected](@types/[email protected])([email protected])':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -6289,6 +6304,7 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/react': 19.2.18
|
'@types/react': 19.2.18
|
||||||
'@types/react-dom': 19.2.4(@types/[email protected])
|
'@types/react-dom': 19.2.4(@types/[email protected])
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@radix-ui/[email protected](@types/[email protected])([email protected])':
|
'@radix-ui/[email protected](@types/[email protected])([email protected])':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -6303,6 +6319,7 @@ snapshots:
|
|||||||
react: 19.2.8
|
react: 19.2.8
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/react': 19.2.18
|
'@types/react': 19.2.18
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@radix-ui/[email protected](@types/[email protected])([email protected])':
|
'@radix-ui/[email protected](@types/[email protected])([email protected])':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -6319,6 +6336,7 @@ snapshots:
|
|||||||
react: 19.2.8
|
react: 19.2.8
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/react': 19.2.18
|
'@types/react': 19.2.18
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@radix-ui/[email protected](@types/[email protected])([email protected])':
|
'@radix-ui/[email protected](@types/[email protected])([email protected])':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -6332,6 +6350,7 @@ snapshots:
|
|||||||
react: 19.2.8
|
react: 19.2.8
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/react': 19.2.18
|
'@types/react': 19.2.18
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@radix-ui/[email protected](@types/[email protected])([email protected])':
|
'@radix-ui/[email protected](@types/[email protected])([email protected])':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -6871,45 +6890,58 @@ snapshots:
|
|||||||
'@types/deep-eql': 4.0.2
|
'@types/deep-eql': 4.0.2
|
||||||
assertion-error: 2.0.1
|
assertion-error: 2.0.1
|
||||||
|
|
||||||
'@types/[email protected]': {}
|
'@types/[email protected]':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@types/[email protected]': {}
|
'@types/[email protected]':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@types/[email protected]': {}
|
'@types/[email protected]':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@types/[email protected]': {}
|
'@types/[email protected]':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@types/[email protected]':
|
'@types/[email protected]':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/geojson': 7946.0.16
|
'@types/geojson': 7946.0.16
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@types/[email protected]':
|
'@types/[email protected]':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/d3-color': 3.1.0
|
'@types/d3-color': 3.1.0
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@types/[email protected]': {}
|
'@types/[email protected]':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@types/[email protected]':
|
'@types/[email protected]':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/d3-time': 3.0.0
|
'@types/d3-time': 3.0.0
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@types/[email protected]':
|
'@types/[email protected]':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/d3-path': 3.1.1
|
'@types/d3-path': 3.1.1
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@types/[email protected]': {}
|
'@types/[email protected]':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@types/[email protected]': {}
|
'@types/[email protected]':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@types/[email protected]': {}
|
'@types/[email protected]': {}
|
||||||
|
|
||||||
'@types/[email protected]': {}
|
'@types/[email protected]': {}
|
||||||
|
|
||||||
'@types/[email protected]': {}
|
'@types/[email protected]':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@types/[email protected]': {}
|
'@types/[email protected]': {}
|
||||||
|
|
||||||
'@types/[email protected]': {}
|
'@types/[email protected]':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@types/[email protected]':
|
'@types/[email protected]':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -6989,11 +7021,13 @@ snapshots:
|
|||||||
'@visx/[email protected]':
|
'@visx/[email protected]':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@visx/vendor': 4.0.0-alpha.0
|
'@visx/vendor': 4.0.0-alpha.0
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@visx/[email protected]':
|
'@visx/[email protected]':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/react': 19.2.18
|
'@types/react': 19.2.18
|
||||||
'@visx/point': 4.0.1-alpha.0
|
'@visx/point': 4.0.1-alpha.0
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@visx/[email protected]([email protected])':
|
'@visx/[email protected]([email protected])':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -7005,14 +7039,17 @@ snapshots:
|
|||||||
'@visx/shape': 4.0.1-alpha.0([email protected])
|
'@visx/shape': 4.0.1-alpha.0([email protected])
|
||||||
classnames: 2.5.1
|
classnames: 2.5.1
|
||||||
react: 19.2.8
|
react: 19.2.8
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@visx/[email protected]([email protected])':
|
'@visx/[email protected]([email protected])':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/react': 19.2.18
|
'@types/react': 19.2.18
|
||||||
classnames: 2.5.1
|
classnames: 2.5.1
|
||||||
react: 19.2.8
|
react: 19.2.8
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@visx/[email protected]': {}
|
'@visx/[email protected]':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@visx/[email protected]([email protected])':
|
'@visx/[email protected]([email protected])':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -7020,10 +7057,12 @@ snapshots:
|
|||||||
'@types/react': 19.2.18
|
'@types/react': 19.2.18
|
||||||
lodash: 4.18.1
|
lodash: 4.18.1
|
||||||
react: 19.2.8
|
react: 19.2.8
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@visx/[email protected]':
|
'@visx/[email protected]':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@visx/vendor': 4.0.0-alpha.0
|
'@visx/vendor': 4.0.0-alpha.0
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@visx/[email protected]([email protected])':
|
'@visx/[email protected]([email protected])':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -7036,6 +7075,7 @@ snapshots:
|
|||||||
classnames: 2.5.1
|
classnames: 2.5.1
|
||||||
lodash: 4.18.1
|
lodash: 4.18.1
|
||||||
react: 19.2.8
|
react: 19.2.8
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@visx/[email protected]':
|
'@visx/[email protected]':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -7062,6 +7102,7 @@ snapshots:
|
|||||||
d3-time: 3.1.0
|
d3-time: 3.1.0
|
||||||
d3-time-format: 4.1.0
|
d3-time-format: 4.1.0
|
||||||
internmap: 2.0.3
|
internmap: 2.0.3
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@vitest/[email protected]([email protected])':
|
'@vitest/[email protected]([email protected])':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -7262,7 +7303,8 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]:
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -7298,6 +7340,7 @@ snapshots:
|
|||||||
rc9: 3.0.1
|
rc9: 3.0.1
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
magicast: 0.5.3
|
magicast: 0.5.3
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
@@ -7306,6 +7349,7 @@ snapshots:
|
|||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
readdirp: 5.0.0
|
readdirp: 5.0.0
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
@@ -7315,7 +7359,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
clsx: 2.1.1
|
clsx: 2.1.1
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]:
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
@@ -7349,7 +7394,8 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]:
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
@@ -7377,28 +7423,36 @@ snapshots:
|
|||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
internmap: 2.0.3
|
internmap: 2.0.3
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
internmap: 2.0.3
|
internmap: 2.0.3
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]:
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
delaunator: 5.1.0
|
delaunator: 5.1.0
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]:
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
d3-array: 3.2.4
|
d3-array: 3.2.4
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
d3-color: 3.1.0
|
d3-color: 3.1.0
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]:
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -7407,18 +7461,22 @@ snapshots:
|
|||||||
d3-interpolate: 3.0.1
|
d3-interpolate: 3.0.1
|
||||||
d3-time: 3.1.0
|
d3-time: 3.1.0
|
||||||
d3-time-format: 4.1.0
|
d3-time-format: 4.1.0
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
d3-path: 3.1.0
|
d3-path: 3.1.0
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
d3-time: 3.1.0
|
d3-time: 3.1.0
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
d3-array: 3.2.4
|
d3-array: 3.2.4
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected](@noble/[email protected]):
|
[email protected](@noble/[email protected]):
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -7437,17 +7495,21 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]:
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]:
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
robust-predicates: 3.0.3
|
robust-predicates: 3.0.3
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]:
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
@@ -7485,16 +7547,19 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@standard-schema/spec': 1.1.0
|
'@standard-schema/spec': 1.1.0
|
||||||
fast-check: 3.23.2
|
fast-check: 3.23.2
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]:
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]:
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -7507,7 +7572,8 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]:
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
@@ -7625,21 +7691,25 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]:
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
pure-rand: 6.1.0
|
pure-rand: 6.1.0
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]:
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
fast-decode-uri-component: 1.0.1
|
fast-decode-uri-component: 1.0.1
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
@@ -7669,6 +7739,7 @@ snapshots:
|
|||||||
fast-deep-equal: 3.1.3
|
fast-deep-equal: 3.1.3
|
||||||
fast-querystring: 1.1.2
|
fast-querystring: 1.1.2
|
||||||
safe-regex2: 5.1.1
|
safe-regex2: 5.1.1
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -7707,13 +7778,15 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]:
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
resolve-pkg-maps: 1.0.0
|
resolve-pkg-maps: 1.0.0
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]:
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -7726,9 +7799,11 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]:
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]:
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -7777,7 +7852,8 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]:
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -8036,7 +8112,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
p-locate: 5.0.0
|
p-locate: 5.0.0
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]:
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
@@ -8147,6 +8224,7 @@ snapshots:
|
|||||||
named-placeholders: 1.1.6
|
named-placeholders: 1.1.6
|
||||||
seq-queue: 0.0.5
|
seq-queue: 0.0.5
|
||||||
sqlstring: 2.3.3
|
sqlstring: 2.3.3
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected](@types/[email protected]):
|
[email protected](@types/[email protected]):
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -8235,7 +8313,8 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]:
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
@@ -8328,7 +8407,8 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]:
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
@@ -8375,6 +8455,7 @@ snapshots:
|
|||||||
confbox: 0.2.4
|
confbox: 0.2.4
|
||||||
exsolve: 1.1.1
|
exsolve: 1.1.1
|
||||||
pathe: 2.0.3
|
pathe: 2.0.3
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
optional: true
|
optional: true
|
||||||
@@ -8401,7 +8482,8 @@ snapshots:
|
|||||||
picocolors: 1.1.1
|
picocolors: 1.1.1
|
||||||
source-map-js: 1.2.1
|
source-map-js: 1.2.1
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]:
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]([email protected]):
|
[email protected]([email protected]):
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -8425,6 +8507,7 @@ snapshots:
|
|||||||
- magicast
|
- magicast
|
||||||
- react
|
- react
|
||||||
- react-dom
|
- react-dom
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
@@ -8437,6 +8520,7 @@ snapshots:
|
|||||||
graceful-fs: 4.2.11
|
graceful-fs: 4.2.11
|
||||||
retry: 0.12.0
|
retry: 0.12.0
|
||||||
signal-exit: 3.0.7
|
signal-exit: 3.0.7
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
@@ -8447,7 +8531,8 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]:
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
@@ -8455,6 +8540,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
defu: 6.1.7
|
defu: 6.1.7
|
||||||
destr: 2.0.5
|
destr: 2.0.5
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]([email protected]):
|
[email protected]([email protected]):
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -8504,7 +8590,8 @@ snapshots:
|
|||||||
string_decoder: 1.1.1
|
string_decoder: 1.1.1
|
||||||
util-deprecate: 1.0.2
|
util-deprecate: 1.0.2
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]:
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
@@ -8516,7 +8603,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
redis-errors: 1.2.0
|
redis-errors: 1.2.0
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]:
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
@@ -8536,11 +8624,14 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]:
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]:
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]:
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -8599,6 +8690,7 @@ snapshots:
|
|||||||
[email protected]:
|
[email protected]:
|
||||||
dependencies:
|
dependencies:
|
||||||
ret: 0.5.0
|
ret: 0.5.0
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
@@ -8625,7 +8717,8 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]:
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
@@ -8672,7 +8765,8 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]:
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
@@ -8706,7 +8800,8 @@ snapshots:
|
|||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]:
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
@@ -8721,7 +8816,8 @@ snapshots:
|
|||||||
'@stablelib/base64': 1.0.1
|
'@stablelib/base64': 1.0.1
|
||||||
fast-sha256: 1.3.0
|
fast-sha256: 1.3.0
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]:
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|
||||||
@@ -8909,6 +9005,7 @@ snapshots:
|
|||||||
[email protected]([email protected]):
|
[email protected]([email protected]):
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
typescript: 7.0.2
|
typescript: 7.0.2
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected]):
|
[email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected]):
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -9091,5 +9188,6 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
grammex: 3.1.13
|
grammex: 3.1.13
|
||||||
graphmatch: 1.1.1
|
graphmatch: 1.1.1
|
||||||
|
optional: true
|
||||||
|
|
||||||
[email protected]: {}
|
[email protected]: {}
|
||||||
|
|||||||
@@ -3,9 +3,6 @@
|
|||||||
# pnpm v11 vervanger voor onlyBuiltDependencies
|
# pnpm v11 vervanger voor onlyBuiltDependencies
|
||||||
allowBuilds:
|
allowBuilds:
|
||||||
esbuild: true
|
esbuild: true
|
||||||
prisma: true
|
|
||||||
"@prisma/client": true
|
|
||||||
"@prisma/engines": true
|
|
||||||
sharp: true
|
sharp: true
|
||||||
"@parcel/watcher": true
|
"@parcel/watcher": true
|
||||||
"@swc/core": true
|
"@swc/core": true
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
import "dotenv/config";
|
|
||||||
import { defineConfig, env } from "prisma/config";
|
|
||||||
|
|
||||||
// Prisma 7 config. The datasource URL lives here (not in schema.prisma).
|
|
||||||
// We NEVER run `prisma migrate`/`db push` against this database — it is shared
|
|
||||||
// live with the Arcturus emulator. CMS-only schema changes go in
|
|
||||||
// prisma/migrations/*.sql (idempotent) applied via `pnpm db:migrate`.
|
|
||||||
export default defineConfig({
|
|
||||||
schema: "prisma/schema.prisma",
|
|
||||||
migrations: {
|
|
||||||
path: "prisma/migrations",
|
|
||||||
},
|
|
||||||
datasource: {
|
|
||||||
url: env("DATABASE_URL"),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@ import { mysqlConnectionUrl } from "./db-url";
|
|||||||
import { splitSqlStatements } from "./sql-statements";
|
import { splitSqlStatements } from "./sql-statements";
|
||||||
|
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
const MIGRATIONS_DIR = resolve(__dirname, "../prisma/migrations");
|
const MIGRATIONS_DIR = resolve(__dirname, "../drizzle/migrations");
|
||||||
const TRACKING_TABLE = "cms_migrations";
|
const TRACKING_TABLE = "cms_migrations";
|
||||||
|
|
||||||
interface MigrationFile {
|
interface MigrationFile {
|
||||||
|
|||||||
+140
-195
@@ -1,10 +1,11 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
// Generates src/db/schema.ts from:
|
// Generates src/db/schema.ts from:
|
||||||
// 1. prisma/schema.prisma -> TS field names (camelCase) + @map column names + table names
|
// 1. existing src/db/schema.ts -> TS export names, camelCase fields, column maps, keys
|
||||||
// 2. live MySQL introspection -> real column types (DB is authoritative for DDL)
|
// 2. live MySQL introspection -> real column types (DB is authoritative for DDL)
|
||||||
//
|
//
|
||||||
// The DB is owned by the Arcturus emulator; we never run drizzle-kit migrate/push.
|
// The DB is owned by the Arcturus emulator; we never run drizzle-kit migrate/push.
|
||||||
// This schema is only used for the query builder + TypeScript types.
|
// CMS DDL lands in drizzle/migrations/*.sql via `pnpm db:migrate`.
|
||||||
|
// drizzle-kit (`pnpm db:generate` / studio / introspect) is draft/browse tooling only.
|
||||||
//
|
//
|
||||||
// Usage: pnpm db:schema:generate
|
// Usage: pnpm db:schema:generate
|
||||||
import "dotenv/config";
|
import "dotenv/config";
|
||||||
@@ -27,84 +28,124 @@ function mysqlConnectionUrl(value) {
|
|||||||
|
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
const ROOT = resolve(__dirname, "..");
|
const ROOT = resolve(__dirname, "..");
|
||||||
const SCHEMA_PRISMA = resolve(ROOT, "prisma/schema.prisma");
|
const SCHEMA_TS = resolve(ROOT, "src/db/schema.ts");
|
||||||
const OUT = resolve(ROOT, "src/db/schema.ts");
|
const OUT = SCHEMA_TS;
|
||||||
|
|
||||||
const prismaSource = readFileSync(SCHEMA_PRISMA, "utf-8");
|
// ---------- Parse existing Drizzle schema (naming source of truth) ----------
|
||||||
|
const schemaSource = readFileSync(SCHEMA_TS, "utf-8");
|
||||||
|
|
||||||
// ---------- Parse prisma/schema.prisma ----------
|
/**
|
||||||
const modelBlocks = [
|
* @returns {{ name: string, table: string, fields: object[], ids: string[]|null, uniques: string[][] }}
|
||||||
...prismaSource.matchAll(/^model\s+(\w+)\s*\{([\s\S]*?)^\}/gm),
|
*/
|
||||||
];
|
function parseDrizzleTables(source) {
|
||||||
const modelNames = new Set(modelBlocks.map((m) => m[1]));
|
const models = [];
|
||||||
|
const re2 =
|
||||||
|
/^export const (\w+) = mysqlTable\(\s*"([^"]+)"\s*,\s*\{([\s\S]*?)\n\}(?:,\s*\(t\)\s*=>\s*\[([\s\S]*?)\])?\s*\);/gm;
|
||||||
|
|
||||||
/** parse a model block -> { name, table, fields, ids, uniques, maps } */
|
let match;
|
||||||
function parseModel(block) {
|
const seen = new Set();
|
||||||
const [_full, name, body] = block;
|
while ((match = re2.exec(source)) !== null) {
|
||||||
const table =
|
const [, name, table, body, extras] = match;
|
||||||
body.match(/@@map\(\s*"([^"]+)"\s*\)/)?.[1] ?? name.toLowerCase();
|
if (seen.has(name)) continue;
|
||||||
|
seen.add(name);
|
||||||
|
models.push(parseTableBody(name, table, body, extras ?? ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (models.length === 0) {
|
||||||
|
throw new Error(
|
||||||
|
`[schema-gen] Failed to parse any mysqlTable exports from ${SCHEMA_TS}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return models;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseTableBody(name, table, body, extras) {
|
||||||
const fields = [];
|
const fields = [];
|
||||||
for (const line of body.split("\n")) {
|
for (const line of body.split("\n")) {
|
||||||
const trimmed = line.trim();
|
const trimmed = line.trim();
|
||||||
if (
|
if (!trimmed || trimmed.startsWith("//")) continue;
|
||||||
!trimmed ||
|
const m = trimmed.match(/^(\w+)\s*:\s*(.+?),?\s*$/);
|
||||||
trimmed.startsWith("//") ||
|
|
||||||
trimmed.startsWith("@@") ||
|
|
||||||
trimmed.startsWith("///")
|
|
||||||
) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const m = trimmed.match(/^(\w+)\s+(.+)$/);
|
|
||||||
if (!m) continue;
|
if (!m) continue;
|
||||||
const [fieldName, rest] = [m[1], m[2]];
|
const fieldName = m[1];
|
||||||
const typeMatch = rest.match(/^([^\s]+)/);
|
const expr = m[2];
|
||||||
const prismaType = typeMatch[1];
|
const colMatch = expr.match(/\(\s*"([^"]+)"/);
|
||||||
const baseType = prismaType.replace(/\?$/, "").replace(/\[\]$/, "");
|
if (!colMatch) continue;
|
||||||
const isList = prismaType.endsWith("[]");
|
const column = colMatch[1];
|
||||||
// relation field (points at another model) -> skip
|
|
||||||
if (modelNames.has(baseType)) continue;
|
const isBoolean = /\bboolean\s*\(/.test(expr);
|
||||||
|
const enumMatch = expr.match(/mysqlEnum\s*\(\s*"[^"]+"\s*,\s*(\[[^\]]*\])/);
|
||||||
|
let enumValues = null;
|
||||||
|
if (enumMatch) {
|
||||||
|
try {
|
||||||
|
enumValues = JSON.parse(enumMatch[1].replace(/'/g, '"'));
|
||||||
|
} catch {
|
||||||
|
enumValues = [...enumMatch[1].matchAll(/"([^"]+)"/g)].map((x) => x[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let defaultContent = null;
|
||||||
|
const defIdx = expr.indexOf(".default(");
|
||||||
|
if (defIdx >= 0) {
|
||||||
|
const start = defIdx + ".default(".length;
|
||||||
|
let depth = 0;
|
||||||
|
for (let i = start; i < expr.length; i++) {
|
||||||
|
const ch = expr[i];
|
||||||
|
if (ch === "(") depth++;
|
||||||
|
else if (ch === ")") {
|
||||||
|
if (depth === 0) {
|
||||||
|
defaultContent = expr.slice(start, i).trim();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
depth--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const attrs = rest;
|
|
||||||
const optional = prismaType.endsWith("?");
|
|
||||||
const map = attrs.match(/@map\(\s*"([^"]+)"\s*\)/)?.[1] ?? fieldName;
|
|
||||||
const isId = /@id\b/.test(attrs);
|
|
||||||
const isUnique = /@unique\b/.test(attrs);
|
|
||||||
const autoIncrement = /@default\(autoincrement\(\)\)/.test(attrs);
|
|
||||||
const updatedAt = /@updatedAt\b/.test(attrs);
|
|
||||||
const dbHint = attrs.match(/@db\.(\w+)(?:\((\d+)(?:\s*,\s*(\d+))?\))?/);
|
|
||||||
fields.push({
|
fields.push({
|
||||||
fieldName,
|
fieldName,
|
||||||
column: map,
|
column,
|
||||||
prismaType: baseType,
|
optional: !/\.notNull\s*\(/.test(expr),
|
||||||
optional,
|
isId: /\.primaryKey\s*\(/.test(expr),
|
||||||
isList,
|
isUnique: /\.unique\s*\(/.test(expr),
|
||||||
isId,
|
autoIncrement: /\.autoincrement\s*\(/.test(expr),
|
||||||
isUnique,
|
isBoolean,
|
||||||
autoIncrement,
|
enumValues,
|
||||||
updatedAt,
|
defaultContent,
|
||||||
attrs,
|
// legacy shape used by columnExpr / fallback
|
||||||
dbHint: dbHint
|
prismaType: isBoolean
|
||||||
? { type: dbHint[1], param1: dbHint[2], param2: dbHint[3] }
|
? "Boolean"
|
||||||
: null,
|
: enumValues
|
||||||
|
? fieldName === "gender"
|
||||||
|
? "users_gender"
|
||||||
|
: fieldName === "type" && table === "bans"
|
||||||
|
? "bans_type"
|
||||||
|
: "String"
|
||||||
|
: "String",
|
||||||
|
attrs: defaultContent ? `@default(${defaultContent})` : "",
|
||||||
|
dbHint: null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// model-level keys
|
let ids = null;
|
||||||
const compIds = body.match(/@@id\(\s*\[([^\]]+)\]\s*\)/)?.[1];
|
|
||||||
const ids = compIds
|
|
||||||
? compIds.split(",").map((s) => s.trim().replace(/`/g, ""))
|
|
||||||
: null;
|
|
||||||
|
|
||||||
const uniques = [];
|
const uniques = [];
|
||||||
for (const u of body.matchAll(/@@unique\(\s*\[([^\]]+)\]\s*/g)) {
|
if (extras) {
|
||||||
uniques.push(u[1].split(",").map((s) => s.trim().replace(/`/g, "")));
|
const pk = extras.match(
|
||||||
|
/primaryKey\(\s*\{\s*columns:\s*\[([^\]]+)\]\s*\}\s*\)/,
|
||||||
|
);
|
||||||
|
if (pk) {
|
||||||
|
ids = [...pk[1].matchAll(/t\.(\w+)/g)].map((m) => m[1]);
|
||||||
|
}
|
||||||
|
for (const u of extras.matchAll(
|
||||||
|
/uniqueIndex\([^)]*\)\.on\(([^)]+)\)/g,
|
||||||
|
)) {
|
||||||
|
uniques.push([...u[1].matchAll(/t\.(\w+)/g)].map((m) => m[1]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { name, table, fields, ids, uniques };
|
return { name, table, fields, ids, uniques };
|
||||||
}
|
}
|
||||||
|
|
||||||
const models = modelBlocks.map(parseModel);
|
const models = parseDrizzleTables(schemaSource);
|
||||||
|
|
||||||
// ---------- Introspect live MySQL ----------
|
// ---------- Introspect live MySQL ----------
|
||||||
let url;
|
let url;
|
||||||
@@ -152,21 +193,20 @@ function quote(v) {
|
|||||||
/** Map a DB column row to a drizzle column expression string. */
|
/** Map a DB column row to a drizzle column expression string. */
|
||||||
function columnExpr(field, dbCol) {
|
function columnExpr(field, dbCol) {
|
||||||
const col = field.column;
|
const col = field.column;
|
||||||
let expr = "";
|
|
||||||
let type = "";
|
let type = "";
|
||||||
|
|
||||||
const unsigned = /unsigned/.test(dbCol?.column_type ?? "");
|
const unsigned = /unsigned/.test(dbCol?.column_type ?? "");
|
||||||
const decimalMatch = dbCol?.column_type?.match(/decimal\((\d+),(\d+)\)/);
|
const decimalMatch = dbCol?.column_type?.match(/decimal\((\d+),(\d+)\)/);
|
||||||
const enumMatch = dbCol?.column_type?.match(/^enum\((.+)\)$/);
|
const enumMatch = dbCol?.column_type?.match(/^enum\((.+)\)$/);
|
||||||
|
|
||||||
// Prisma enum types -> mysqlEnum
|
|
||||||
if (field.prismaType === "users_gender" || field.prismaType === "bans_type") {
|
if (field.prismaType === "users_gender" || field.prismaType === "bans_type") {
|
||||||
use("mysqlEnum");
|
use("mysqlEnum");
|
||||||
const values = enumMatch
|
const values = enumMatch
|
||||||
? [...enumMatch[1].matchAll(/'([^']+)'/g)].map((m) => m[1])
|
? [...enumMatch[1].matchAll(/'([^']+)'/g)].map((m) => m[1])
|
||||||
: field.prismaType === "users_gender"
|
: (field.enumValues ??
|
||||||
? ["M", "F"]
|
(field.prismaType === "users_gender"
|
||||||
: ["account", "ip", "machine", "super"];
|
? ["M", "F"]
|
||||||
|
: ["account", "ip", "machine", "super"]));
|
||||||
return `mysqlEnum(${quote(col)}, ${JSON.stringify(values)})`;
|
return `mysqlEnum(${quote(col)}, ${JSON.stringify(values)})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,7 +219,8 @@ function columnExpr(field, dbCol) {
|
|||||||
break;
|
break;
|
||||||
case "tinyint": {
|
case "tinyint": {
|
||||||
const isBool =
|
const isBool =
|
||||||
dbCol.column_type === "tinyint(1)" && field.prismaType === "Boolean";
|
dbCol.column_type === "tinyint(1)" &&
|
||||||
|
(field.isBoolean || field.prismaType === "Boolean");
|
||||||
if (isBool) {
|
if (isBool) {
|
||||||
use("boolean");
|
use("boolean");
|
||||||
type = `boolean(${quote(col)})`;
|
type = `boolean(${quote(col)})`;
|
||||||
@@ -209,8 +250,6 @@ function columnExpr(field, dbCol) {
|
|||||||
break;
|
break;
|
||||||
case "varchar":
|
case "varchar":
|
||||||
case "enum": {
|
case "enum": {
|
||||||
// DB enums become plain varchar in the schema: TS contract is `string`
|
|
||||||
// (only users_gender / bans_type are typed as mysqlEnum above).
|
|
||||||
use("varchar");
|
use("varchar");
|
||||||
const maxLen = enumMatch
|
const maxLen = enumMatch
|
||||||
? Math.max(
|
? Math.max(
|
||||||
@@ -284,14 +323,13 @@ function columnExpr(field, dbCol) {
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.warn(
|
console.warn(
|
||||||
`[schema-gen] WARN unhandled DB type "${dbCol?.data_type}" for ${col} (prisma:${field.prismaType})`,
|
`[schema-gen] WARN unhandled DB type "${dbCol?.data_type}" for ${col}`,
|
||||||
);
|
);
|
||||||
use("varchar");
|
use("varchar");
|
||||||
type = `varchar(${quote(col)}, { length: 255 })`;
|
type = `varchar(${quote(col)}, { length: 255 })`;
|
||||||
}
|
}
|
||||||
expr += type;
|
|
||||||
|
|
||||||
return expr;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
function modifiers(field, dbCol) {
|
function modifiers(field, dbCol) {
|
||||||
@@ -306,55 +344,35 @@ function modifiers(field, dbCol) {
|
|||||||
} else if (field.isUnique) {
|
} else if (field.isUnique) {
|
||||||
expr += `.unique()`;
|
expr += `.unique()`;
|
||||||
}
|
}
|
||||||
// Mirror the Prisma TS contract: required fields (no `?`) are not-null on
|
|
||||||
// select, and fields with `@default` are optional on insert.
|
|
||||||
if (!field.optional) {
|
if (!field.optional) {
|
||||||
expr += `.notNull()`;
|
expr += `.notNull()`;
|
||||||
}
|
}
|
||||||
expr += parseDefault(field, dbCol);
|
expr += emitDefault(field, dbCol);
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Extract `@default(...)` and emit a drizzle `.default(...)` (or ""). */
|
function emitDefault(field, dbCol) {
|
||||||
function parseDefault(field, dbCol) {
|
const content = field.defaultContent;
|
||||||
const m = field.attrs.match(/@default\(/);
|
|
||||||
if (!m) return "";
|
|
||||||
const start = m.index + "@default(".length;
|
|
||||||
let depth = 0;
|
|
||||||
let content = "";
|
|
||||||
for (let i = start; i < field.attrs.length; i++) {
|
|
||||||
const ch = field.attrs[i];
|
|
||||||
if (ch === "(") {
|
|
||||||
depth++;
|
|
||||||
} else if (ch === ")") {
|
|
||||||
if (depth === 0) {
|
|
||||||
content = field.attrs.slice(start, i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
depth--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!content) return "";
|
if (!content) return "";
|
||||||
if (content === "now()") {
|
if (content === "sql`CURRENT_TIMESTAMP`" || content.includes("CURRENT_TIMESTAMP")) {
|
||||||
markSqlUsed();
|
markSqlUsed();
|
||||||
return `.default(sql\`CURRENT_TIMESTAMP\`)`;
|
return `.default(sql\`CURRENT_TIMESTAMP\`)`;
|
||||||
}
|
}
|
||||||
if (content === "autoincrement()" || content.startsWith("dbgenerated("))
|
|
||||||
return "";
|
|
||||||
if (content === "true" || content === "false") return `.default(${content})`;
|
if (content === "true" || content === "false") return `.default(${content})`;
|
||||||
|
if (/^-?\d+n$/.test(content)) return `.default(${content})`;
|
||||||
if (/^-?\d+$/.test(content)) {
|
if (/^-?\d+$/.test(content)) {
|
||||||
// integer literal; bigint64 data type is `bigint`, others are `number`
|
|
||||||
return dbCol?.data_type === "bigint"
|
return dbCol?.data_type === "bigint"
|
||||||
? `.default(${content}n)`
|
? `.default(${content}n)`
|
||||||
: `.default(${content})`;
|
: `.default(${content})`;
|
||||||
}
|
}
|
||||||
if (/^-?\d+\.\d+$/.test(content)) return `.default(${content})`;
|
if (/^-?\d+\.\d+$/.test(content)) return `.default(${content})`;
|
||||||
// quoted string or bare enum/string identifier (e.g. @default(M))
|
if (
|
||||||
const s =
|
(content.startsWith('"') && content.endsWith('"')) ||
|
||||||
content.startsWith('"') && content.endsWith('"')
|
(content.startsWith("'") && content.endsWith("'"))
|
||||||
? content.slice(1, -1)
|
) {
|
||||||
: content;
|
return `.default(${JSON.stringify(content.slice(1, -1))})`;
|
||||||
return `.default(${JSON.stringify(s)})`;
|
}
|
||||||
|
return `.default(${content})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function modelTable(model) {
|
function modelTable(model) {
|
||||||
@@ -362,8 +380,6 @@ function modelTable(model) {
|
|||||||
for (const f of model.fields) {
|
for (const f of model.fields) {
|
||||||
const dbCol = colByTable.get(`${model.table}.${f.column}`);
|
const dbCol = colByTable.get(`${model.table}.${f.column}`);
|
||||||
if (!dbCol) {
|
if (!dbCol) {
|
||||||
// Column not found in live DB. Prisma schema may be ahead of the DB.
|
|
||||||
// Fall back to a best-effort type from the Prisma @db hint / base type.
|
|
||||||
const fallback = fallbackColumn(f) + modifiers(f, null);
|
const fallback = fallbackColumn(f) + modifiers(f, null);
|
||||||
rows.push(`\t${f.fieldName}: ${fallback},`);
|
rows.push(`\t${f.fieldName}: ${fallback},`);
|
||||||
console.warn(
|
console.warn(
|
||||||
@@ -407,96 +423,23 @@ ${uniqueRows.join("\n")}
|
|||||||
],
|
],
|
||||||
);`;
|
);`;
|
||||||
}
|
}
|
||||||
return `export const ${model.name} = mysqlTable(
|
return `export const ${model.name} = mysqlTable("${model.table}", {
|
||||||
"${model.table}",
|
|
||||||
{
|
|
||||||
${rows.join("\n")}
|
${rows.join("\n")}
|
||||||
},
|
});`;
|
||||||
);`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function fallbackColumn(field) {
|
function fallbackColumn(field) {
|
||||||
const hint = field.dbHint;
|
|
||||||
const name = field.column;
|
const name = field.column;
|
||||||
switch (hint?.type) {
|
if (field.enumValues) {
|
||||||
case "VarChar":
|
use("mysqlEnum");
|
||||||
use("varchar");
|
return `mysqlEnum(${quote(name)}, ${JSON.stringify(field.enumValues)})`;
|
||||||
return `varchar(${quote(name)}, { length: ${Number(hint.param1 ?? 191)} })`;
|
|
||||||
case "Char":
|
|
||||||
use("char");
|
|
||||||
return `char(${quote(name)}, { length: ${Number(hint.param1 ?? 8)} })`;
|
|
||||||
case "Text":
|
|
||||||
use("text");
|
|
||||||
return `text(${quote(name)})`;
|
|
||||||
case "MediumText":
|
|
||||||
use("mediumtext");
|
|
||||||
return `mediumtext(${quote(name)})`;
|
|
||||||
case "LongText":
|
|
||||||
use("longtext");
|
|
||||||
return `longtext(${quote(name)})`;
|
|
||||||
case "Decimal":
|
|
||||||
use("decimal");
|
|
||||||
return `decimal(${quote(name)}, { precision: ${Number(hint.param1 ?? 10)}, scale: ${Number(hint.param2 ?? 0)}, mode: "number" })`;
|
|
||||||
case "UnsignedBigInt":
|
|
||||||
use("bigint");
|
|
||||||
return `bigint(${quote(name)}, { mode: "bigint", unsigned: true })`;
|
|
||||||
case "UnsignedInt":
|
|
||||||
use("int");
|
|
||||||
return `int(${quote(name)}, { unsigned: true })`;
|
|
||||||
case "Double":
|
|
||||||
use("double");
|
|
||||||
return `double(${quote(name)})`;
|
|
||||||
case "Float":
|
|
||||||
use("float");
|
|
||||||
return `float(${quote(name)})`;
|
|
||||||
case "Json":
|
|
||||||
use("json");
|
|
||||||
return `json(${quote(name)})`;
|
|
||||||
case "Timestamp":
|
|
||||||
use("timestamp");
|
|
||||||
return `timestamp(${quote(name)})`;
|
|
||||||
case "Time":
|
|
||||||
return `timeAsDate(${quote(name)})`;
|
|
||||||
case "Date":
|
|
||||||
return `dateAsDate(${quote(name)})`;
|
|
||||||
case "TinyInt":
|
|
||||||
use("tinyint");
|
|
||||||
return `tinyint(${quote(name)})`;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
switch (field.prismaType) {
|
if (field.isBoolean || field.prismaType === "Boolean") {
|
||||||
case "Int":
|
use("boolean");
|
||||||
use("int");
|
return `boolean(${quote(name)})`;
|
||||||
return `int(${quote(name)})`;
|
|
||||||
case "BigInt":
|
|
||||||
use("bigint");
|
|
||||||
return `bigint(${quote(name)}, { mode: "bigint" })`;
|
|
||||||
case "Boolean":
|
|
||||||
use("boolean");
|
|
||||||
return `boolean(${quote(name)})`;
|
|
||||||
case "DateTime":
|
|
||||||
use("datetime");
|
|
||||||
return `datetime(${quote(name)})`;
|
|
||||||
case "String":
|
|
||||||
use("varchar");
|
|
||||||
return `varchar(${quote(name)}, { length: 255 })`;
|
|
||||||
case "Float":
|
|
||||||
use("double");
|
|
||||||
return `double(${quote(name)})`;
|
|
||||||
case "Decimal":
|
|
||||||
use("decimal");
|
|
||||||
return `decimal(${quote(name)}, { precision: 10, scale: 2, mode: "number" })`;
|
|
||||||
case "Json":
|
|
||||||
use("json");
|
|
||||||
return `json(${quote(name)})`;
|
|
||||||
case "Bytes":
|
|
||||||
use("binary");
|
|
||||||
return `binary(${quote(name)})`;
|
|
||||||
default:
|
|
||||||
use("varchar");
|
|
||||||
return `varchar(${quote(name)}, { length: 255 })`;
|
|
||||||
}
|
}
|
||||||
|
use("varchar");
|
||||||
|
return `varchar(${quote(name)}, { length: 255 })`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- Generate file ----------
|
// ---------- Generate file ----------
|
||||||
@@ -532,8 +475,8 @@ const IMPORTABLE = [
|
|||||||
const importList = IMPORTABLE.filter((b) => used.has(b));
|
const importList = IMPORTABLE.filter((b) => used.has(b));
|
||||||
|
|
||||||
const helpers = [
|
const helpers = [
|
||||||
"// MySQL TIME / DATE columns are hydrated by Prisma as JS Date (epoch 1970-01-01",
|
"// MySQL TIME / DATE columns are hydrated as JS Date (epoch 1970-01-01",
|
||||||
"// for TIME). Mirror that so existing call sites keep working unchanged.",
|
"// for TIME). Keep this so existing call sites stay unchanged.",
|
||||||
"const timeAsDate = customType<{ data: Date; driverData: string }>({",
|
"const timeAsDate = customType<{ data: Date; driverData: string }>({",
|
||||||
" dataType() {",
|
" dataType() {",
|
||||||
' return "time";',
|
' return "time";',
|
||||||
@@ -563,13 +506,15 @@ const helpers = [
|
|||||||
"",
|
"",
|
||||||
].join("\n");
|
].join("\n");
|
||||||
|
|
||||||
|
const sqlImport = usedSql ? 'import { sql } from "drizzle-orm";\n' : "";
|
||||||
|
|
||||||
const out = `// AUTO-GENERATED by scripts/generate-drizzle-schema.mjs — DO NOT EDIT.
|
const out = `// AUTO-GENERATED by scripts/generate-drizzle-schema.mjs — DO NOT EDIT.
|
||||||
// TS field names mirror prisma/schema.prisma (camelCase); column names are the
|
// TS field names come from the previous src/db/schema.ts; column types from the
|
||||||
// real MySQL columns. Run \`pnpm db:schema:generate\` after schema changes.
|
// live MySQL DB. Run \`pnpm db:schema:generate\` after schema/map changes.
|
||||||
import {
|
${sqlImport}import {
|
||||||
${importList.map((b) => `\t${b},`).join("\n")}
|
${importList.map((b) => `\t${b},`).join("\n")}
|
||||||
} from "drizzle-orm/mysql-core";
|
} from "drizzle-orm/mysql-core";
|
||||||
${usedSql ? `import { sql } from "drizzle-orm";\n` : ""}
|
|
||||||
${helpers}${body}
|
${helpers}${body}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { describe, expect, it } from "vitest";
|
|||||||
describe("radio columns migration", () => {
|
describe("radio columns migration", () => {
|
||||||
it("adds every column idempotently for partially migrated databases", () => {
|
it("adds every column idempotently for partially migrated databases", () => {
|
||||||
const sql = readFileSync(
|
const sql = readFileSync(
|
||||||
resolve("prisma/migrations/0009_radio_contests_giveaways_columns.sql"),
|
resolve("drizzle/migrations/0009_radio_contests_giveaways_columns.sql"),
|
||||||
"utf8",
|
"utf8",
|
||||||
);
|
);
|
||||||
const additions = sql.match(/ADD COLUMN(?! IF NOT EXISTS)/gi) ?? [];
|
const additions = sql.match(/ADD COLUMN(?! IF NOT EXISTS)/gi) ?? [];
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export const revalidate = 300;
|
|||||||
|
|
||||||
export const metadata = { title: "Leaderboard" };
|
export const metadata = { title: "Leaderboard" };
|
||||||
|
|
||||||
// Currency type ids (see prisma/schema.prisma UsersCurrency):
|
// Currency type ids (see UsersCurrency in src/db/schema.ts):
|
||||||
// Credits = -1 (lives on users.credits), Duckets = 0, Diamonds = 5.
|
// Credits = -1 (lives on users.credits), Duckets = 0, Diamonds = 5.
|
||||||
const TABS = [
|
const TABS = [
|
||||||
{ key: "credits", label: "Credits", coin: "credits" },
|
{ key: "credits", label: "Credits", coin: "credits" },
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export default async function PhotosPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Pre-shape for the client lightbox: translate captions server-side so the
|
// Pre-shape for the client lightbox: translate captions server-side so the
|
||||||
// client component stays free of i18n/prisma dependencies.
|
// client component stays free of i18n/db dependencies.
|
||||||
const items: LightboxPhoto[] = photos.map((p) => ({
|
const items: LightboxPhoto[] = photos.map((p) => ({
|
||||||
id: String(p.id),
|
id: String(p.id),
|
||||||
url: p.url,
|
url: p.url,
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { useCallback, useEffect, useState } from "react";
|
|||||||
* toggled by state — closes on backdrop click, on the ✕ button, or with Esc.
|
* toggled by state — closes on backdrop click, on the ✕ button, or with Esc.
|
||||||
*
|
*
|
||||||
* Photos are pre-shaped on the server (caption strings already translated) so
|
* Photos are pre-shaped on the server (caption strings already translated) so
|
||||||
* this component carries no prisma/i18n dependency.
|
* this component carries no db/i18n dependency.
|
||||||
*/
|
*/
|
||||||
export type LightboxPhoto = {
|
export type LightboxPhoto = {
|
||||||
id: string;
|
id: string;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { useEventSource } from "@/lib/use-event-source";
|
|||||||
* radio via a hidden <audio> element, with play/pause, a volume slider, the
|
* radio via a hidden <audio> element, with play/pause, a volume slider, the
|
||||||
* current DJ / now-playing text, and a live listener count.
|
* current DJ / now-playing text, and a live listener count.
|
||||||
*
|
*
|
||||||
* It is a CLIENT component and must NOT import prisma / siteSettings.
|
* It is a CLIENT component and must NOT import db / siteSettings.
|
||||||
* Config is fetched once on mount; now-playing + listeners come via SSE
|
* Config is fetched once on mount; now-playing + listeners come via SSE
|
||||||
* (/api/radio/stream) for real-time updates.
|
* (/api/radio/stream) for real-time updates.
|
||||||
*
|
*
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
// AUTO-GENERATED by scripts/generate-drizzle-schema.mjs — DO NOT EDIT.
|
// AUTO-GENERATED by scripts/generate-drizzle-schema.mjs — DO NOT EDIT.
|
||||||
// TS field names mirror prisma/schema.prisma (camelCase); column names are the
|
// TS field names come from the previous src/db/schema.ts; column types from the
|
||||||
// real MySQL columns. Run `pnpm db:schema:generate` after schema changes.
|
// live MySQL DB. Run `pnpm db:schema:generate` after schema/map changes.
|
||||||
|
|
||||||
import { sql } from "drizzle-orm";
|
import { sql } from "drizzle-orm";
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -19,15 +19,17 @@ const tables = [
|
|||||||
|
|
||||||
describe("admin content module port", () => {
|
describe("admin content module port", () => {
|
||||||
it("owns every required table through schema and migration contracts", () => {
|
it("owns every required table through schema and migration contracts", () => {
|
||||||
const schema = readFileSync(resolve("prisma/schema.prisma"), "utf8");
|
const schema = readFileSync(resolve("src/db/schema.ts"), "utf8");
|
||||||
const migration = readFileSync(
|
const migration = readFileSync(
|
||||||
resolve("prisma/migrations/0013_admin_events_polls_banners_prefixes.sql"),
|
resolve(
|
||||||
|
"drizzle/migrations/0013_admin_events_polls_banners_prefixes.sql",
|
||||||
|
),
|
||||||
"utf8",
|
"utf8",
|
||||||
);
|
);
|
||||||
for (const table of tables) {
|
for (const table of tables) {
|
||||||
expect(migration).toContain(`CREATE TABLE IF NOT EXISTS \`${table}\``);
|
expect(migration).toContain(`CREATE TABLE IF NOT EXISTS \`${table}\``);
|
||||||
if (!table.startsWith("custom_prefix"))
|
if (!table.startsWith("custom_prefix"))
|
||||||
expect(schema).toContain(`@@map("${table}")`);
|
expect(schema).toContain(`"${table}"`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ describe("ACL management contract", () => {
|
|||||||
|
|
||||||
it("ships an idempotent ACL completion migration", () => {
|
it("ships an idempotent ACL completion migration", () => {
|
||||||
const sql = readFileSync(
|
const sql = readFileSync(
|
||||||
"prisma/migrations/0014_complete_acl_and_import_permissions.sql",
|
"drizzle/migrations/0014_complete_acl_and_import_permissions.sql",
|
||||||
"utf8",
|
"utf8",
|
||||||
);
|
);
|
||||||
expect(sql).toContain("admin.assets.import");
|
expect(sql).toContain("admin.assets.import");
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { describe, expect, it } from "vitest";
|
|||||||
import { PERMS } from "@/lib/permission-slugs";
|
import { PERMS } from "@/lib/permission-slugs";
|
||||||
|
|
||||||
describe("production ACL migration", () => {
|
describe("production ACL migration", () => {
|
||||||
const migrationsDir = resolve(process.cwd(), "prisma/migrations");
|
const migrationsDir = resolve(process.cwd(), "drizzle/migrations");
|
||||||
const allSql = readdirSync(migrationsDir)
|
const allSql = readdirSync(migrationsDir)
|
||||||
.filter((name) => name.endsWith(".sql"))
|
.filter((name) => name.endsWith(".sql"))
|
||||||
.map((name) => readFileSync(resolve(migrationsDir, name), "utf8"))
|
.map((name) => readFileSync(resolve(migrationsDir, name), "utf8"))
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ describe("CI workflow", () => {
|
|||||||
const release = workflow.slice(workflow.indexOf("\n release:"));
|
const release = workflow.slice(workflow.indexOf("\n release:"));
|
||||||
expect(release).toContain("pnpm db:migrate");
|
expect(release).toContain("pnpm db:migrate");
|
||||||
expect(release).not.toContain("pnpm prisma:generate");
|
expect(release).not.toContain("pnpm prisma:generate");
|
||||||
|
expect(release).toContain("pnpm db:migrate");
|
||||||
expect(release).toContain("src/db/schema.ts");
|
expect(release).toContain("src/db/schema.ts");
|
||||||
const migrateAt = release.indexOf("pnpm db:migrate");
|
const migrateAt = release.indexOf("pnpm db:migrate");
|
||||||
const buildAt = release.indexOf("pnpm build");
|
const buildAt = release.indexOf("pnpm build");
|
||||||
|
|||||||
@@ -249,14 +249,6 @@ export function handleActionError(error: unknown): ActionFailure {
|
|||||||
if (error instanceof Error && error.name === "ZodError") {
|
if (error instanceof Error && error.name === "ZodError") {
|
||||||
return fail("Validation failed");
|
return fail("Validation failed");
|
||||||
}
|
}
|
||||||
if (
|
|
||||||
error instanceof Error &&
|
|
||||||
(error.constructor.name === "PrismaClientKnownRequestError" ||
|
|
||||||
error.name === "PrismaClientKnownRequestError") &&
|
|
||||||
(error as Error & { code?: string }).code === "P2025"
|
|
||||||
) {
|
|
||||||
return fail("Not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
reportError(error, "Action error");
|
reportError(error, "Action error");
|
||||||
return fail("Internal server error");
|
return fail("Internal server error");
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// ── Permission Slug Constants ───────────────────────────────────────
|
// ── Permission Slug Constants ───────────────────────────────────────
|
||||||
// Extracted to its own file so it can be imported by client components
|
// Extracted to its own file so it can be imported by client components
|
||||||
// (modules.ts → sidebar.tsx) without pulling in prisma/server-only deps.
|
// (modules.ts → sidebar.tsx) without pulling in db/server-only deps.
|
||||||
|
|
||||||
export const PERMS = {
|
export const PERMS = {
|
||||||
ADMIN_DASHBOARD: "admin.dashboard",
|
ADMIN_DASHBOARD: "admin.dashboard",
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ describe("proxy authentication boundary", () => {
|
|||||||
expect(proxy).toContain('from "@/lib/proxy-auth"');
|
expect(proxy).toContain('from "@/lib/proxy-auth"');
|
||||||
expect(proxy).not.toContain('from "@/lib/auth"');
|
expect(proxy).not.toContain('from "@/lib/auth"');
|
||||||
expect(proxyAuth).not.toMatch(
|
expect(proxyAuth).not.toMatch(
|
||||||
/from\s+["'][^"']*(prisma|site-settings|credentials)[^"']*["']/i,
|
/from\s+["'][^"']*(db|site-settings|credentials|prisma)[^"']*["']/i,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export interface UploadParams {
|
|||||||
|
|
||||||
const MIGRATIONS_DIR = path.resolve(
|
const MIGRATIONS_DIR = path.resolve(
|
||||||
/*turbopackIgnore: true*/ process.cwd(),
|
/*turbopackIgnore: true*/ process.cwd(),
|
||||||
"prisma/migrations",
|
"drizzle/migrations",
|
||||||
);
|
);
|
||||||
|
|
||||||
let itemsBaseIdAllocChain: Promise<unknown> = Promise.resolve();
|
let itemsBaseIdAllocChain: Promise<unknown> = Promise.resolve();
|
||||||
|
|||||||
@@ -49,6 +49,21 @@ describe("staff smoke contract", () => {
|
|||||||
).toContain("UserSanctionTimeline");
|
).toContain("UserSanctionTimeline");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("applies CMS SQL from drizzle/migrations (not Prisma)", () => {
|
||||||
|
expect(existsSync("drizzle/migrations")).toBe(true);
|
||||||
|
expect(existsSync("prisma")).toBe(false);
|
||||||
|
expect(readFileSync("scripts/apply-migrations.ts", "utf8")).toContain(
|
||||||
|
"drizzle/migrations",
|
||||||
|
);
|
||||||
|
expect(readFileSync("package.json", "utf8")).not.toContain('"prisma"');
|
||||||
|
expect(readFileSync("package.json", "utf8")).not.toContain(
|
||||||
|
'"@prisma/client"',
|
||||||
|
);
|
||||||
|
expect(readFileSync("drizzle.config.ts", "utf8")).toContain(
|
||||||
|
"./drizzle/drafts",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("ships shared ops + ticket queue helpers", () => {
|
it("ships shared ops + ticket queue helpers", () => {
|
||||||
expect(existsSync("src/lib/admin/ops-health.ts")).toBe(true);
|
expect(existsSync("src/lib/admin/ops-health.ts")).toBe(true);
|
||||||
const ops = readFileSync("src/lib/admin/ops-health.ts", "utf8");
|
const ops = readFileSync("src/lib/admin/ops-health.ts", "utf8");
|
||||||
|
|||||||
Reference in New Issue
Block a user