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:
1 parent
d8199ea1e4
commit
ba82789166
47 files changed
+365
-2787
No files matched your search
@@ -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 ?? "",
|
||||||
},
|
},
|
||||||
|
|||||||
Whitespace-only changes.
@@ -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`.
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+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