From 9deda5f1256f2c0ef748d4f8fb8365bf72faea0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ullrich=20Sch=C3=A4fer?= Date: Sun, 22 Mar 2026 23:35:50 +0100 Subject: [PATCH] Add Drizzle ORM with shared db package (#10) --- apps/journal/package.json | 2 + apps/planner/app/lib/db.ts | 27 +- apps/planner/app/lib/sessions.ts | 112 +++--- apps/planner/package.json | 3 +- package.json | 4 + packages/db/drizzle.config.ts | 10 + packages/db/package.json | 16 + packages/db/src/index.ts | 17 + packages/db/src/schema/journal.ts | 86 +++++ packages/db/src/schema/planner.ts | 19 + packages/db/tsconfig.json | 8 + pnpm-lock.yaml | 584 ++++++++++++++++++++++++++++-- pnpm-workspace.yaml | 4 + 13 files changed, 783 insertions(+), 109 deletions(-) create mode 100644 packages/db/drizzle.config.ts create mode 100644 packages/db/package.json create mode 100644 packages/db/src/index.ts create mode 100644 packages/db/src/schema/journal.ts create mode 100644 packages/db/src/schema/planner.ts create mode 100644 packages/db/tsconfig.json diff --git a/apps/journal/package.json b/apps/journal/package.json index 4102d19..26db5b3 100644 --- a/apps/journal/package.json +++ b/apps/journal/package.json @@ -11,11 +11,13 @@ "lint": "eslint ." }, "dependencies": { + "@trails-cool/db": "workspace:*", "@trails-cool/ui": "workspace:*", "@trails-cool/types": "workspace:*", "@trails-cool/map": "workspace:*", "@trails-cool/gpx": "workspace:*", "@trails-cool/i18n": "workspace:*", + "drizzle-orm": "catalog:", "react": "catalog:", "react-dom": "catalog:", "react-router": "catalog:", diff --git a/apps/planner/app/lib/db.ts b/apps/planner/app/lib/db.ts index 6ced90f..8fafd8c 100644 --- a/apps/planner/app/lib/db.ts +++ b/apps/planner/app/lib/db.ts @@ -1,23 +1,10 @@ -import postgres from "postgres"; +import { createDb, type Database } from "@trails-cool/db"; -const connectionString = - process.env.DATABASE_URL ?? "postgres://trails:trails@localhost:5432/trails"; +let _db: Database | null = null; -export const sql = postgres(connectionString); - -export async function initDb() { - await sql` - CREATE SCHEMA IF NOT EXISTS planner - `; - await sql` - CREATE TABLE IF NOT EXISTS planner.sessions ( - id TEXT PRIMARY KEY, - yjs_state BYTEA, - callback_url TEXT, - callback_token TEXT, - created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), - last_activity TIMESTAMPTZ NOT NULL DEFAULT NOW(), - closed BOOLEAN NOT NULL DEFAULT FALSE - ) - `; +export function getDb(): Database { + if (!_db) { + _db = createDb(); + } + return _db; } diff --git a/apps/planner/app/lib/sessions.ts b/apps/planner/app/lib/sessions.ts index 7fd4b02..9f66764 100644 --- a/apps/planner/app/lib/sessions.ts +++ b/apps/planner/app/lib/sessions.ts @@ -1,16 +1,11 @@ import { randomUUID } from "node:crypto"; import * as Y from "yjs"; +import { eq, and, desc, lt } from "drizzle-orm"; import { getOrCreateDoc, deleteDoc } from "./yjs-server"; -import { sql } from "./db"; +import { getDb } from "./db"; +import { sessions } from "@trails-cool/db/schema/planner"; -export interface SessionMetadata { - id: string; - createdAt: Date; - lastActivity: Date; - callbackUrl?: string; - callbackToken?: string; - closed: boolean; -} +export type SessionMetadata = typeof sessions.$inferSelect; export async function createSession(options?: { callbackUrl?: string; @@ -19,52 +14,60 @@ export async function createSession(options?: { const id = randomUUID(); const doc = getOrCreateDoc(id); - const [row] = await sql` - INSERT INTO planner.sessions (id, yjs_state, callback_url, callback_token) - VALUES (${id}, ${Buffer.from(Y.encodeStateAsUpdate(doc))}, ${options?.callbackUrl ?? null}, ${options?.callbackToken ?? null}) - RETURNING id, created_at, last_activity, callback_url, callback_token, closed - `; + const [row] = await getDb() + .insert(sessions) + .values({ + id, + yjsState: Buffer.from(Y.encodeStateAsUpdate(doc)), + callbackUrl: options?.callbackUrl, + callbackToken: options?.callbackToken, + }) + .returning(); - return mapRow(row); + return row!; } export async function getSession(id: string): Promise { - const [row] = await sql` - SELECT id, created_at, last_activity, callback_url, callback_token, closed - FROM planner.sessions - WHERE id = ${id} AND closed = FALSE - `; - return row ? mapRow(row) : undefined; + const [row] = await getDb() + .select() + .from(sessions) + .where(and(eq(sessions.id, id), eq(sessions.closed, false))); + + return row; } export async function touchSession(id: string): Promise { - await sql` - UPDATE planner.sessions SET last_activity = NOW() WHERE id = ${id} - `; + await getDb() + .update(sessions) + .set({ lastActivity: new Date() }) + .where(eq(sessions.id, id)); } export async function saveSessionState(id: string): Promise { const doc = getOrCreateDoc(id); const state = Y.encodeStateAsUpdate(doc); - await sql` - UPDATE planner.sessions - SET yjs_state = ${Buffer.from(state)}, last_activity = NOW() - WHERE id = ${id} - `; + await getDb() + .update(sessions) + .set({ yjsState: Buffer.from(state), lastActivity: new Date() }) + .where(eq(sessions.id, id)); } export async function loadSessionState(id: string): Promise { - const [row] = await sql` - SELECT yjs_state FROM planner.sessions WHERE id = ${id} - `; - return row?.yjs_state ? new Uint8Array(row.yjs_state) : null; + const [row] = await getDb() + .select({ yjsState: sessions.yjsState }) + .from(sessions) + .where(eq(sessions.id, id)); + + return row?.yjsState ? new Uint8Array(row.yjsState) : null; } export async function closeSession(id: string): Promise { - const result = await sql` - UPDATE planner.sessions SET closed = TRUE WHERE id = ${id} AND closed = FALSE - RETURNING id - `; + const result = await getDb() + .update(sessions) + .set({ closed: true }) + .where(and(eq(sessions.id, id), eq(sessions.closed, false))) + .returning({ id: sessions.id }); + deleteDoc(id); return result.length > 0; } @@ -88,35 +91,22 @@ export function initializeSessionWithWaypoints( } export async function listSessions(): Promise { - const rows = await sql` - SELECT id, created_at, last_activity, callback_url, callback_token, closed - FROM planner.sessions - WHERE closed = FALSE - ORDER BY last_activity DESC - `; - return rows.map(mapRow); + return getDb() + .select() + .from(sessions) + .where(eq(sessions.closed, false)) + .orderBy(desc(sessions.lastActivity)); } export async function expireSessions(maxAgeDays: number = 7): Promise { - const result = await sql` - DELETE FROM planner.sessions - WHERE last_activity < NOW() - INTERVAL '1 day' * ${maxAgeDays} - RETURNING id - `; + const cutoff = new Date(Date.now() - maxAgeDays * 24 * 60 * 60 * 1000); + const result = await getDb() + .delete(sessions) + .where(lt(sessions.lastActivity, cutoff)) + .returning({ id: sessions.id }); + for (const row of result) { deleteDoc(row.id); } return result.length; } - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function mapRow(row: any): SessionMetadata { - return { - id: row.id as string, - createdAt: row.created_at as Date, - lastActivity: row.last_activity as Date, - callbackUrl: (row.callback_url as string) ?? undefined, - callbackToken: (row.callback_token as string) ?? undefined, - closed: row.closed as boolean, - }; -} diff --git a/apps/planner/package.json b/apps/planner/package.json index e6d7816..fb2a791 100644 --- a/apps/planner/package.json +++ b/apps/planner/package.json @@ -13,13 +13,14 @@ "dependencies": { "@react-router/node": "catalog:", "@react-router/serve": "catalog:", + "@trails-cool/db": "workspace:*", "@trails-cool/gpx": "workspace:*", "@trails-cool/i18n": "workspace:*", "@trails-cool/map": "workspace:*", "@trails-cool/types": "workspace:*", "@trails-cool/ui": "workspace:*", + "drizzle-orm": "catalog:", "isbot": "^5.1.0", - "postgres": "^3.4.8", "react": "catalog:", "react-dom": "catalog:", "react-router": "catalog:", diff --git a/package.json b/package.json index 738b560..6127dff 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,9 @@ "@types/leaflet": "^1.9.21", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", + "drizzle-kit": "^0.31.10", + "drizzle-orm": "^0.45.1", + "drizzle-postgis": "^1.1.1", "eslint": "^10.1.0", "eslint-config-prettier": "^10.1.8", "i18next": "^25.10.4", @@ -37,6 +40,7 @@ "jsdom": "^29.0.1", "leaflet": "^1.9.4", "playwright": "^1.58.2", + "postgres": "^3.4.8", "prettier": "^3.8.1", "react": "^19.2.4", "react-dom": "^19.2.4", diff --git a/packages/db/drizzle.config.ts b/packages/db/drizzle.config.ts new file mode 100644 index 0000000..700e8a4 --- /dev/null +++ b/packages/db/drizzle.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from "drizzle-kit"; + +export default defineConfig({ + schema: ["./src/schema/planner.ts", "./src/schema/journal.ts"], + out: "./migrations", + dialect: "postgresql", + dbCredentials: { + url: process.env.DATABASE_URL ?? "postgres://trails:trails@localhost:5432/trails", + }, +}); diff --git a/packages/db/package.json b/packages/db/package.json new file mode 100644 index 0000000..e77ea9a --- /dev/null +++ b/packages/db/package.json @@ -0,0 +1,16 @@ +{ + "name": "@trails-cool/db", + "version": "0.0.1", + "type": "module", + "exports": { + ".": "./src/index.ts", + "./schema/planner": "./src/schema/planner.ts", + "./schema/journal": "./src/schema/journal.ts" + }, + "main": "./src/index.ts", + "types": "./src/index.ts", + "dependencies": { + "drizzle-orm": "catalog:", + "postgres": "catalog:" + } +} diff --git a/packages/db/src/index.ts b/packages/db/src/index.ts new file mode 100644 index 0000000..b051bfd --- /dev/null +++ b/packages/db/src/index.ts @@ -0,0 +1,17 @@ +import { drizzle } from "drizzle-orm/postgres-js"; +import postgres from "postgres"; +import * as plannerSchema from "./schema/planner"; +import * as journalSchema from "./schema/journal"; + +export function createDb(connectionString?: string) { + const client = postgres( + connectionString ?? process.env.DATABASE_URL ?? "postgres://trails:trails@localhost:5432/trails", + ); + return drizzle(client, { + schema: { ...plannerSchema, ...journalSchema }, + }); +} + +export type Database = ReturnType; + +export { plannerSchema, journalSchema }; diff --git a/packages/db/src/schema/journal.ts b/packages/db/src/schema/journal.ts new file mode 100644 index 0000000..2fed404 --- /dev/null +++ b/packages/db/src/schema/journal.ts @@ -0,0 +1,86 @@ +import { + pgSchema, + text, + timestamp, + integer, + real, + jsonb, + customType, +} from "drizzle-orm/pg-core"; + +const bytea = customType<{ data: Buffer }>({ + dataType() { + return "bytea"; + }, +}); + +const lineString = customType<{ data: string }>({ + dataType() { + return "geometry(LineString, 4326)"; + }, +}); + +export const journalSchema = pgSchema("journal"); + +export const users = journalSchema.table("users", { + id: text("id").primaryKey(), + email: text("email").notNull().unique(), + passwordHash: text("password_hash").notNull(), + username: text("username").notNull().unique(), + displayName: text("display_name"), + bio: text("bio"), + domain: text("domain").notNull(), + createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), +}); + +export const routes = journalSchema.table("routes", { + id: text("id").primaryKey(), + ownerId: text("owner_id") + .notNull() + .references(() => users.id), + name: text("name").notNull(), + description: text("description").default(""), + gpx: text("gpx"), + geom: lineString("geom"), + routingProfile: text("routing_profile"), + distance: real("distance"), + elevationGain: real("elevation_gain"), + elevationLoss: real("elevation_loss"), + dayBreaks: jsonb("day_breaks").$type(), + tags: jsonb("tags").$type(), + plannerState: bytea("planner_state"), + createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), + updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(), +}); + +export const routeVersions = journalSchema.table("route_versions", { + id: text("id").primaryKey(), + routeId: text("route_id") + .notNull() + .references(() => routes.id, { onDelete: "cascade" }), + version: integer("version").notNull(), + gpx: text("gpx").notNull(), + createdBy: text("created_by").references(() => users.id), + changeDescription: text("change_description"), + createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), +}); + +export const activities = journalSchema.table("activities", { + id: text("id").primaryKey(), + ownerId: text("owner_id") + .notNull() + .references(() => users.id), + routeId: text("route_id").references(() => routes.id), + name: text("name").notNull(), + description: text("description").default(""), + gpx: text("gpx"), + geom: lineString("geom"), + startedAt: timestamp("started_at", { withTimezone: true }), + duration: integer("duration"), + distance: real("distance"), + elevationGain: real("elevation_gain"), + elevationLoss: real("elevation_loss"), + photos: jsonb("photos").$type(), + participants: jsonb("participants").$type(), + createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), +}); diff --git a/packages/db/src/schema/planner.ts b/packages/db/src/schema/planner.ts new file mode 100644 index 0000000..d789283 --- /dev/null +++ b/packages/db/src/schema/planner.ts @@ -0,0 +1,19 @@ +import { pgSchema, text, timestamp, boolean, customType } from "drizzle-orm/pg-core"; + +const bytea = customType<{ data: Buffer }>({ + dataType() { + return "bytea"; + }, +}); + +export const plannerSchema = pgSchema("planner"); + +export const sessions = plannerSchema.table("sessions", { + id: text("id").primaryKey(), + yjsState: bytea("yjs_state"), + callbackUrl: text("callback_url"), + callbackToken: text("callback_token"), + createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), + lastActivity: timestamp("last_activity", { withTimezone: true }).notNull().defaultNow(), + closed: boolean("closed").notNull().default(false), +}); diff --git a/packages/db/tsconfig.json b/packages/db/tsconfig.json new file mode 100644 index 0000000..5a24989 --- /dev/null +++ b/packages/db/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src" + }, + "include": ["src"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 53139b0..9604aa9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -24,6 +24,12 @@ catalogs: '@types/react-dom': specifier: ^19.1.5 version: 19.2.3 + drizzle-orm: + specifier: ^0.44.0 + version: 0.44.7 + postgres: + specifier: ^3.4.0 + version: 3.4.8 react: specifier: ^19.1.0 version: 19.2.4 @@ -55,7 +61,7 @@ importers: version: 1.58.2 '@react-router/dev': specifier: ^7.13.1 - version: 7.13.1(@react-router/serve@7.13.1(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3))(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)) + version: 7.13.1(@react-router/serve@7.13.1(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3))(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(tsx@4.21.0)(typescript@5.9.3)(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)) '@react-router/node': specifier: ^7.13.1 version: 7.13.1(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3) @@ -64,7 +70,7 @@ importers: version: 7.13.1(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3) '@tailwindcss/vite': specifier: ^4.2.2 - version: 4.2.2(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)) + version: 4.2.2(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)) '@testing-library/jest-dom': specifier: ^6.9.1 version: 6.9.1 @@ -80,6 +86,15 @@ importers: '@types/react-dom': specifier: ^19.2.3 version: 19.2.3(@types/react@19.2.14) + drizzle-kit: + specifier: ^0.31.10 + version: 0.31.10 + drizzle-orm: + specifier: ^0.45.1 + version: 0.45.1(postgres@3.4.8) + drizzle-postgis: + specifier: ^1.1.1 + version: 1.1.1 eslint: specifier: ^10.1.0 version: 10.1.0(jiti@2.6.1) @@ -101,6 +116,9 @@ importers: playwright: specifier: ^1.58.2 version: 1.58.2 + postgres: + specifier: ^3.4.8 + version: 3.4.8 prettier: specifier: ^3.8.1 version: 3.8.1 @@ -130,10 +148,10 @@ importers: version: 8.57.1(eslint@10.1.0(jiti@2.6.1))(typescript@5.9.3) vite: specifier: 'catalog:' - version: 6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0) + version: 6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0) vitest: specifier: ^4.1.0 - version: 4.1.0(@types/node@25.5.0)(jsdom@29.0.1)(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)) + version: 4.1.0(@types/node@25.5.0)(jsdom@29.0.1)(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)) apps/journal: dependencies: @@ -143,6 +161,9 @@ importers: '@react-router/serve': specifier: 'catalog:' version: 7.13.1(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3) + '@trails-cool/db': + specifier: workspace:* + version: link:../../packages/db '@trails-cool/gpx': specifier: workspace:* version: link:../../packages/gpx @@ -158,6 +179,9 @@ importers: '@trails-cool/ui': specifier: workspace:* version: link:../../packages/ui + drizzle-orm: + specifier: 'catalog:' + version: 0.44.7(postgres@3.4.8) isbot: specifier: ^5.1.0 version: 5.1.36 @@ -173,10 +197,10 @@ importers: devDependencies: '@react-router/dev': specifier: 'catalog:' - version: 7.13.1(@react-router/serve@7.13.1(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3))(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)) + version: 7.13.1(@react-router/serve@7.13.1(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3))(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(tsx@4.21.0)(typescript@5.9.3)(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)) '@tailwindcss/vite': specifier: 'catalog:' - version: 4.2.2(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)) + version: 4.2.2(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)) '@types/react': specifier: 'catalog:' version: 19.2.14 @@ -191,7 +215,7 @@ importers: version: 5.9.3 vite: specifier: 'catalog:' - version: 6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0) + version: 6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0) apps/planner: dependencies: @@ -201,6 +225,9 @@ importers: '@react-router/serve': specifier: 'catalog:' version: 7.13.1(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3) + '@trails-cool/db': + specifier: workspace:* + version: link:../../packages/db '@trails-cool/gpx': specifier: workspace:* version: link:../../packages/gpx @@ -216,12 +243,12 @@ importers: '@trails-cool/ui': specifier: workspace:* version: link:../../packages/ui + drizzle-orm: + specifier: 'catalog:' + version: 0.44.7(postgres@3.4.8) isbot: specifier: ^5.1.0 version: 5.1.36 - postgres: - specifier: ^3.4.8 - version: 3.4.8 react: specifier: 'catalog:' version: 19.2.4 @@ -243,10 +270,10 @@ importers: devDependencies: '@react-router/dev': specifier: 'catalog:' - version: 7.13.1(@react-router/serve@7.13.1(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3))(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)) + version: 7.13.1(@react-router/serve@7.13.1(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3))(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(tsx@4.21.0)(typescript@5.9.3)(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)) '@tailwindcss/vite': specifier: 'catalog:' - version: 4.2.2(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)) + version: 4.2.2(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)) '@types/react': specifier: 'catalog:' version: 19.2.14 @@ -264,7 +291,16 @@ importers: version: 5.9.3 vite: specifier: 'catalog:' - version: 6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0) + version: 6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0) + + packages/db: + dependencies: + drizzle-orm: + specifier: 'catalog:' + version: 0.44.7(postgres@3.4.8) + postgres: + specifier: 'catalog:' + version: 3.4.8 packages/gpx: dependencies: @@ -492,6 +528,17 @@ packages: resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} engines: {node: '>=20.19.0'} + '@drizzle-team/brocli@0.10.2': + resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==} + + '@esbuild-kit/core-utils@3.3.2': + resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==} + deprecated: 'Merged into tsx: https://tsx.is' + + '@esbuild-kit/esm-loader@2.6.5': + resolution: {integrity: sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==} + deprecated: 'Merged into tsx: https://tsx.is' + '@esbuild/aix-ppc64@0.25.12': resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} engines: {node: '>=18'} @@ -504,6 +551,12 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/android-arm64@0.18.20': + resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm64@0.25.12': resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} engines: {node: '>=18'} @@ -516,6 +569,12 @@ packages: cpu: [arm64] os: [android] + '@esbuild/android-arm@0.18.20': + resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + '@esbuild/android-arm@0.25.12': resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} engines: {node: '>=18'} @@ -528,6 +587,12 @@ packages: cpu: [arm] os: [android] + '@esbuild/android-x64@0.18.20': + resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + '@esbuild/android-x64@0.25.12': resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} engines: {node: '>=18'} @@ -540,6 +605,12 @@ packages: cpu: [x64] os: [android] + '@esbuild/darwin-arm64@0.18.20': + resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-arm64@0.25.12': resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} engines: {node: '>=18'} @@ -552,6 +623,12 @@ packages: cpu: [arm64] os: [darwin] + '@esbuild/darwin-x64@0.18.20': + resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + '@esbuild/darwin-x64@0.25.12': resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} engines: {node: '>=18'} @@ -564,6 +641,12 @@ packages: cpu: [x64] os: [darwin] + '@esbuild/freebsd-arm64@0.18.20': + resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-arm64@0.25.12': resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} engines: {node: '>=18'} @@ -576,6 +659,12 @@ packages: cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-x64@0.18.20': + resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + '@esbuild/freebsd-x64@0.25.12': resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} engines: {node: '>=18'} @@ -588,6 +677,12 @@ packages: cpu: [x64] os: [freebsd] + '@esbuild/linux-arm64@0.18.20': + resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm64@0.25.12': resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} engines: {node: '>=18'} @@ -600,6 +695,12 @@ packages: cpu: [arm64] os: [linux] + '@esbuild/linux-arm@0.18.20': + resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + '@esbuild/linux-arm@0.25.12': resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} engines: {node: '>=18'} @@ -612,6 +713,12 @@ packages: cpu: [arm] os: [linux] + '@esbuild/linux-ia32@0.18.20': + resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-ia32@0.25.12': resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} engines: {node: '>=18'} @@ -624,6 +731,12 @@ packages: cpu: [ia32] os: [linux] + '@esbuild/linux-loong64@0.18.20': + resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-loong64@0.25.12': resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} engines: {node: '>=18'} @@ -636,6 +749,12 @@ packages: cpu: [loong64] os: [linux] + '@esbuild/linux-mips64el@0.18.20': + resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-mips64el@0.25.12': resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} engines: {node: '>=18'} @@ -648,6 +767,12 @@ packages: cpu: [mips64el] os: [linux] + '@esbuild/linux-ppc64@0.18.20': + resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-ppc64@0.25.12': resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} engines: {node: '>=18'} @@ -660,6 +785,12 @@ packages: cpu: [ppc64] os: [linux] + '@esbuild/linux-riscv64@0.18.20': + resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-riscv64@0.25.12': resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} engines: {node: '>=18'} @@ -672,6 +803,12 @@ packages: cpu: [riscv64] os: [linux] + '@esbuild/linux-s390x@0.18.20': + resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-s390x@0.25.12': resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} engines: {node: '>=18'} @@ -684,6 +821,12 @@ packages: cpu: [s390x] os: [linux] + '@esbuild/linux-x64@0.18.20': + resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + '@esbuild/linux-x64@0.25.12': resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} engines: {node: '>=18'} @@ -708,6 +851,12 @@ packages: cpu: [arm64] os: [netbsd] + '@esbuild/netbsd-x64@0.18.20': + resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + '@esbuild/netbsd-x64@0.25.12': resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} engines: {node: '>=18'} @@ -732,6 +881,12 @@ packages: cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-x64@0.18.20': + resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + '@esbuild/openbsd-x64@0.25.12': resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} engines: {node: '>=18'} @@ -756,6 +911,12 @@ packages: cpu: [arm64] os: [openharmony] + '@esbuild/sunos-x64@0.18.20': + resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + '@esbuild/sunos-x64@0.25.12': resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} engines: {node: '>=18'} @@ -768,6 +929,12 @@ packages: cpu: [x64] os: [sunos] + '@esbuild/win32-arm64@0.18.20': + resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-arm64@0.25.12': resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} engines: {node: '>=18'} @@ -780,6 +947,12 @@ packages: cpu: [arm64] os: [win32] + '@esbuild/win32-ia32@0.18.20': + resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-ia32@0.25.12': resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} engines: {node: '>=18'} @@ -792,6 +965,12 @@ packages: cpu: [ia32] os: [win32] + '@esbuild/win32-x64@0.18.20': + resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + '@esbuild/win32-x64@0.25.12': resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} engines: {node: '>=18'} @@ -1559,6 +1738,197 @@ packages: dom-accessibility-api@0.6.3: resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + drizzle-kit@0.31.10: + resolution: {integrity: sha512-7OZcmQUrdGI+DUNNsKBn1aW8qSoKuTH7d0mYgSP8bAzdFzKoovxEFnoGQp2dVs82EOJeYycqRtciopszwUf8bw==} + hasBin: true + + drizzle-orm@0.44.7: + resolution: {integrity: sha512-quIpnYznjU9lHshEOAYLoZ9s3jweleHlZIAWR/jX9gAWNg/JhQ1wj0KGRf7/Zm+obRrYd9GjPVJg790QY9N5AQ==} + peerDependencies: + '@aws-sdk/client-rds-data': '>=3' + '@cloudflare/workers-types': '>=4' + '@electric-sql/pglite': '>=0.2.0' + '@libsql/client': '>=0.10.0' + '@libsql/client-wasm': '>=0.10.0' + '@neondatabase/serverless': '>=0.10.0' + '@op-engineering/op-sqlite': '>=2' + '@opentelemetry/api': ^1.4.1 + '@planetscale/database': '>=1.13' + '@prisma/client': '*' + '@tidbcloud/serverless': '*' + '@types/better-sqlite3': '*' + '@types/pg': '*' + '@types/sql.js': '*' + '@upstash/redis': '>=1.34.7' + '@vercel/postgres': '>=0.8.0' + '@xata.io/client': '*' + better-sqlite3: '>=7' + bun-types: '*' + expo-sqlite: '>=14.0.0' + gel: '>=2' + knex: '*' + kysely: '*' + mysql2: '>=2' + pg: '>=8' + postgres: '>=3' + prisma: '*' + sql.js: '>=1' + sqlite3: '>=5' + peerDependenciesMeta: + '@aws-sdk/client-rds-data': + optional: true + '@cloudflare/workers-types': + optional: true + '@electric-sql/pglite': + optional: true + '@libsql/client': + optional: true + '@libsql/client-wasm': + optional: true + '@neondatabase/serverless': + optional: true + '@op-engineering/op-sqlite': + optional: true + '@opentelemetry/api': + optional: true + '@planetscale/database': + optional: true + '@prisma/client': + optional: true + '@tidbcloud/serverless': + optional: true + '@types/better-sqlite3': + optional: true + '@types/pg': + optional: true + '@types/sql.js': + optional: true + '@upstash/redis': + optional: true + '@vercel/postgres': + optional: true + '@xata.io/client': + optional: true + better-sqlite3: + optional: true + bun-types: + optional: true + expo-sqlite: + optional: true + gel: + optional: true + knex: + optional: true + kysely: + optional: true + mysql2: + optional: true + pg: + optional: true + postgres: + optional: true + prisma: + optional: true + sql.js: + optional: true + sqlite3: + optional: true + + drizzle-orm@0.45.1: + resolution: {integrity: sha512-Te0FOdKIistGNPMq2jscdqngBRfBpC8uMFVwqjf6gtTVJHIQ/dosgV/CLBU2N4ZJBsXL5savCba9b0YJskKdcA==} + peerDependencies: + '@aws-sdk/client-rds-data': '>=3' + '@cloudflare/workers-types': '>=4' + '@electric-sql/pglite': '>=0.2.0' + '@libsql/client': '>=0.10.0' + '@libsql/client-wasm': '>=0.10.0' + '@neondatabase/serverless': '>=0.10.0' + '@op-engineering/op-sqlite': '>=2' + '@opentelemetry/api': ^1.4.1 + '@planetscale/database': '>=1.13' + '@prisma/client': '*' + '@tidbcloud/serverless': '*' + '@types/better-sqlite3': '*' + '@types/pg': '*' + '@types/sql.js': '*' + '@upstash/redis': '>=1.34.7' + '@vercel/postgres': '>=0.8.0' + '@xata.io/client': '*' + better-sqlite3: '>=7' + bun-types: '*' + expo-sqlite: '>=14.0.0' + gel: '>=2' + knex: '*' + kysely: '*' + mysql2: '>=2' + pg: '>=8' + postgres: '>=3' + prisma: '*' + sql.js: '>=1' + sqlite3: '>=5' + peerDependenciesMeta: + '@aws-sdk/client-rds-data': + optional: true + '@cloudflare/workers-types': + optional: true + '@electric-sql/pglite': + optional: true + '@libsql/client': + optional: true + '@libsql/client-wasm': + optional: true + '@neondatabase/serverless': + optional: true + '@op-engineering/op-sqlite': + optional: true + '@opentelemetry/api': + optional: true + '@planetscale/database': + optional: true + '@prisma/client': + optional: true + '@tidbcloud/serverless': + optional: true + '@types/better-sqlite3': + optional: true + '@types/pg': + optional: true + '@types/sql.js': + optional: true + '@upstash/redis': + optional: true + '@vercel/postgres': + optional: true + '@xata.io/client': + optional: true + better-sqlite3: + optional: true + bun-types: + optional: true + expo-sqlite: + optional: true + gel: + optional: true + knex: + optional: true + kysely: + optional: true + mysql2: + optional: true + pg: + optional: true + postgres: + optional: true + prisma: + optional: true + sql.js: + optional: true + sqlite3: + optional: true + + drizzle-postgis@1.1.1: + resolution: {integrity: sha512-weHOTZrtyuGRsWSDuq7ZvbCMKue/aJ1dZ8ktbdnRDopq+REfpYuhnQKyrJ8Wb/sY6J47ZHB2pHE8CN7I0xYv6w==} + dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} @@ -1599,6 +1969,11 @@ packages: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} + esbuild@0.18.20: + resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} + engines: {node: '>=12'} + hasBin: true + esbuild@0.25.12: resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} engines: {node: '>=18'} @@ -1764,6 +2139,9 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} + get-tsconfig@4.13.7: + resolution: {integrity: sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==} + glob-parent@6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} @@ -2247,6 +2625,9 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + rollup@4.60.0: resolution: {integrity: sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -2393,6 +2774,11 @@ packages: peerDependencies: typescript: '>=4.8.4' + tsx@4.21.0: + resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} + engines: {node: '>=18.0.0'} + hasBin: true + turbo@2.8.20: resolution: {integrity: sha512-Rb4qk5YT8RUwwdXtkLpkVhNEe/lor6+WV7S5tTlLpxSz6MjV5Qi8jGNn4gS6NAvrYGA/rNrE6YUQM85sCZUDbQ==} hasBin: true @@ -2608,6 +2994,9 @@ packages: engines: {node: '>=8'} hasBin: true + wkx@0.5.0: + resolution: {integrity: sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==} + word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} @@ -2892,102 +3281,162 @@ snapshots: '@csstools/css-tokenizer@4.0.0': {} + '@drizzle-team/brocli@0.10.2': {} + + '@esbuild-kit/core-utils@3.3.2': + dependencies: + esbuild: 0.18.20 + source-map-support: 0.5.21 + + '@esbuild-kit/esm-loader@2.6.5': + dependencies: + '@esbuild-kit/core-utils': 3.3.2 + get-tsconfig: 4.13.7 + '@esbuild/aix-ppc64@0.25.12': optional: true '@esbuild/aix-ppc64@0.27.4': optional: true + '@esbuild/android-arm64@0.18.20': + optional: true + '@esbuild/android-arm64@0.25.12': optional: true '@esbuild/android-arm64@0.27.4': optional: true + '@esbuild/android-arm@0.18.20': + optional: true + '@esbuild/android-arm@0.25.12': optional: true '@esbuild/android-arm@0.27.4': optional: true + '@esbuild/android-x64@0.18.20': + optional: true + '@esbuild/android-x64@0.25.12': optional: true '@esbuild/android-x64@0.27.4': optional: true + '@esbuild/darwin-arm64@0.18.20': + optional: true + '@esbuild/darwin-arm64@0.25.12': optional: true '@esbuild/darwin-arm64@0.27.4': optional: true + '@esbuild/darwin-x64@0.18.20': + optional: true + '@esbuild/darwin-x64@0.25.12': optional: true '@esbuild/darwin-x64@0.27.4': optional: true + '@esbuild/freebsd-arm64@0.18.20': + optional: true + '@esbuild/freebsd-arm64@0.25.12': optional: true '@esbuild/freebsd-arm64@0.27.4': optional: true + '@esbuild/freebsd-x64@0.18.20': + optional: true + '@esbuild/freebsd-x64@0.25.12': optional: true '@esbuild/freebsd-x64@0.27.4': optional: true + '@esbuild/linux-arm64@0.18.20': + optional: true + '@esbuild/linux-arm64@0.25.12': optional: true '@esbuild/linux-arm64@0.27.4': optional: true + '@esbuild/linux-arm@0.18.20': + optional: true + '@esbuild/linux-arm@0.25.12': optional: true '@esbuild/linux-arm@0.27.4': optional: true + '@esbuild/linux-ia32@0.18.20': + optional: true + '@esbuild/linux-ia32@0.25.12': optional: true '@esbuild/linux-ia32@0.27.4': optional: true + '@esbuild/linux-loong64@0.18.20': + optional: true + '@esbuild/linux-loong64@0.25.12': optional: true '@esbuild/linux-loong64@0.27.4': optional: true + '@esbuild/linux-mips64el@0.18.20': + optional: true + '@esbuild/linux-mips64el@0.25.12': optional: true '@esbuild/linux-mips64el@0.27.4': optional: true + '@esbuild/linux-ppc64@0.18.20': + optional: true + '@esbuild/linux-ppc64@0.25.12': optional: true '@esbuild/linux-ppc64@0.27.4': optional: true + '@esbuild/linux-riscv64@0.18.20': + optional: true + '@esbuild/linux-riscv64@0.25.12': optional: true '@esbuild/linux-riscv64@0.27.4': optional: true + '@esbuild/linux-s390x@0.18.20': + optional: true + '@esbuild/linux-s390x@0.25.12': optional: true '@esbuild/linux-s390x@0.27.4': optional: true + '@esbuild/linux-x64@0.18.20': + optional: true + '@esbuild/linux-x64@0.25.12': optional: true @@ -3000,6 +3449,9 @@ snapshots: '@esbuild/netbsd-arm64@0.27.4': optional: true + '@esbuild/netbsd-x64@0.18.20': + optional: true + '@esbuild/netbsd-x64@0.25.12': optional: true @@ -3012,6 +3464,9 @@ snapshots: '@esbuild/openbsd-arm64@0.27.4': optional: true + '@esbuild/openbsd-x64@0.18.20': + optional: true + '@esbuild/openbsd-x64@0.25.12': optional: true @@ -3024,24 +3479,36 @@ snapshots: '@esbuild/openharmony-arm64@0.27.4': optional: true + '@esbuild/sunos-x64@0.18.20': + optional: true + '@esbuild/sunos-x64@0.25.12': optional: true '@esbuild/sunos-x64@0.27.4': optional: true + '@esbuild/win32-arm64@0.18.20': + optional: true + '@esbuild/win32-arm64@0.25.12': optional: true '@esbuild/win32-arm64@0.27.4': optional: true + '@esbuild/win32-ia32@0.18.20': + optional: true + '@esbuild/win32-ia32@0.25.12': optional: true '@esbuild/win32-ia32@0.27.4': optional: true + '@esbuild/win32-x64@0.18.20': + optional: true + '@esbuild/win32-x64@0.25.12': optional: true @@ -3126,7 +3593,7 @@ snapshots: react: 19.2.4 react-dom: 19.2.4(react@19.2.4) - '@react-router/dev@7.13.1(@react-router/serve@7.13.1(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3))(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0))': + '@react-router/dev@7.13.1(@react-router/serve@7.13.1(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3))(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(tsx@4.21.0)(typescript@5.9.3)(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0))': dependencies: '@babel/core': 7.29.0 '@babel/generator': 7.29.1 @@ -3156,8 +3623,8 @@ snapshots: semver: 7.7.4 tinyglobby: 0.2.15 valibot: 1.3.1(typescript@5.9.3) - vite: 6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0) - vite-node: 3.2.4(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0) + vite: 6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0) + vite-node: 3.2.4(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0) optionalDependencies: '@react-router/serve': 7.13.1(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3) typescript: 5.9.3 @@ -3346,12 +3813,12 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.2.2 '@tailwindcss/oxide-win32-x64-msvc': 4.2.2 - '@tailwindcss/vite@4.2.2(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0))': + '@tailwindcss/vite@4.2.2(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0))': dependencies: '@tailwindcss/node': 4.2.2 '@tailwindcss/oxide': 4.2.2 tailwindcss: 4.2.2 - vite: 6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0) + vite: 6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0) '@testing-library/dom@10.4.1': dependencies: @@ -3538,13 +4005,13 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.0(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0))': + '@vitest/mocker@4.1.0(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0))': dependencies: '@vitest/spy': 4.1.0 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0) + vite: 6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0) '@vitest/pretty-format@4.1.0': dependencies: @@ -3758,6 +4225,25 @@ snapshots: dom-accessibility-api@0.6.3: {} + drizzle-kit@0.31.10: + dependencies: + '@drizzle-team/brocli': 0.10.2 + '@esbuild-kit/esm-loader': 2.6.5 + esbuild: 0.25.12 + tsx: 4.21.0 + + drizzle-orm@0.44.7(postgres@3.4.8): + optionalDependencies: + postgres: 3.4.8 + + drizzle-orm@0.45.1(postgres@3.4.8): + optionalDependencies: + postgres: 3.4.8 + + drizzle-postgis@1.1.1: + dependencies: + wkx: 0.5.0 + dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 @@ -3789,6 +4275,31 @@ snapshots: dependencies: es-errors: 1.3.0 + esbuild@0.18.20: + optionalDependencies: + '@esbuild/android-arm': 0.18.20 + '@esbuild/android-arm64': 0.18.20 + '@esbuild/android-x64': 0.18.20 + '@esbuild/darwin-arm64': 0.18.20 + '@esbuild/darwin-x64': 0.18.20 + '@esbuild/freebsd-arm64': 0.18.20 + '@esbuild/freebsd-x64': 0.18.20 + '@esbuild/linux-arm': 0.18.20 + '@esbuild/linux-arm64': 0.18.20 + '@esbuild/linux-ia32': 0.18.20 + '@esbuild/linux-loong64': 0.18.20 + '@esbuild/linux-mips64el': 0.18.20 + '@esbuild/linux-ppc64': 0.18.20 + '@esbuild/linux-riscv64': 0.18.20 + '@esbuild/linux-s390x': 0.18.20 + '@esbuild/linux-x64': 0.18.20 + '@esbuild/netbsd-x64': 0.18.20 + '@esbuild/openbsd-x64': 0.18.20 + '@esbuild/sunos-x64': 0.18.20 + '@esbuild/win32-arm64': 0.18.20 + '@esbuild/win32-ia32': 0.18.20 + '@esbuild/win32-x64': 0.18.20 + esbuild@0.25.12: optionalDependencies: '@esbuild/aix-ppc64': 0.25.12 @@ -4043,6 +4554,10 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 + get-tsconfig@4.13.7: + dependencies: + resolve-pkg-maps: 1.0.0 + glob-parent@6.0.2: dependencies: is-glob: 4.0.3 @@ -4438,6 +4953,8 @@ snapshots: require-from-string@2.0.2: {} + resolve-pkg-maps@1.0.0: {} + rollup@4.60.0: dependencies: '@types/estree': 1.0.8 @@ -4608,6 +5125,13 @@ snapshots: dependencies: typescript: 5.9.3 + tsx@4.21.0: + dependencies: + esbuild: 0.27.4 + get-tsconfig: 4.13.7 + optionalDependencies: + fsevents: 2.3.3 + turbo@2.8.20: optionalDependencies: '@turbo/darwin-64': 2.8.20 @@ -4667,13 +5191,13 @@ snapshots: vary@1.1.2: {} - vite-node@3.2.4(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0): + vite-node@3.2.4(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0): dependencies: cac: 6.7.14 debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0) + vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0) transitivePeerDependencies: - '@types/node' - jiti @@ -4688,7 +5212,7 @@ snapshots: - tsx - yaml - vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0): + vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.3) @@ -4701,8 +5225,9 @@ snapshots: fsevents: 2.3.3 jiti: 2.6.1 lightningcss: 1.32.0 + tsx: 4.21.0 - vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0): + vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0): dependencies: esbuild: 0.27.4 fdir: 6.5.0(picomatch@4.0.3) @@ -4715,11 +5240,12 @@ snapshots: fsevents: 2.3.3 jiti: 2.6.1 lightningcss: 1.32.0 + tsx: 4.21.0 - vitest@4.1.0(@types/node@25.5.0)(jsdom@29.0.1)(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)): + vitest@4.1.0(@types/node@25.5.0)(jsdom@29.0.1)(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)): dependencies: '@vitest/expect': 4.1.0 - '@vitest/mocker': 4.1.0(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)) + '@vitest/mocker': 4.1.0(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)) '@vitest/pretty-format': 4.1.0 '@vitest/runner': 4.1.0 '@vitest/snapshot': 4.1.0 @@ -4736,7 +5262,7 @@ snapshots: tinyexec: 1.0.4 tinyglobby: 0.2.15 tinyrainbow: 3.1.0 - vite: 6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0) + vite: 6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 25.5.0 @@ -4771,6 +5297,10 @@ snapshots: siginfo: 2.0.0 stackback: 0.0.2 + wkx@0.5.0: + dependencies: + '@types/node': 25.5.0 + word-wrap@1.2.5: {} ws@8.20.0: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index ef231eb..1581c20 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -15,3 +15,7 @@ catalog: "@tailwindcss/vite": ^4.1.7 typescript: ^5.8.3 vite: ^6.0.0 + drizzle-orm: ^0.44.0 + drizzle-kit: ^0.31.0 + drizzle-postgis: ^1.1.0 + postgres: ^3.4.0