feat: set up Drizzle ORM with SQLite database

Phase 1.1 Complete: Database & Auth Infrastructure

- Configure Drizzle with SQLite and better-sqlite3
- Create schema for users, players, and user_stats tables
- Set up database client with foreign keys and WAL mode enabled
- Add migration runner and package.json scripts
- Generate initial migration (0000_third_carnage.sql)

Database Features:
- Users table with guestId for guest sessions
- Players table with userId FK (cascade delete)
- UserStats table with userId FK (cascade delete)
- Indexes on foreign keys for performance
- Type-safe schema with Drizzle ORM

Testing:
- 20 unit + e2e tests all passing
- Schema validation tests
- Migration idempotency tests
- Foreign key constraint tests
- Cascade delete tests

Scripts added:
- pnpm db:generate - Generate migration from schema
- pnpm db:migrate - Run pending migrations
- pnpm db:push - Push schema directly (dev)
- pnpm db:studio - Visual DB browser
- pnpm db:drop - Drop migration (dev)

User tests verified:
 Migration runs successfully
 Database tables created with correct schema
 Migration is idempotent (can run multiple times)
This commit is contained in:
Thomas Hallock 2025-10-05 17:01:27 -05:00
parent dd0df8c274
commit 5d5afd4e68
15 changed files with 1075 additions and 24 deletions

View File

@ -0,0 +1,12 @@
import type { Config } from 'drizzle-kit'
export default {
schema: './src/db/schema/index.ts',
out: './drizzle',
dialect: 'sqlite',
dbCredentials: {
url: process.env.DATABASE_URL || './data/sqlite.db',
},
verbose: true,
strict: true,
} satisfies Config

View File

@ -0,0 +1,32 @@
CREATE TABLE `users` (
`id` text PRIMARY KEY NOT NULL,
`guest_id` text NOT NULL,
`created_at` integer NOT NULL,
`upgraded_at` integer,
`email` text,
`name` text
);
--> statement-breakpoint
CREATE UNIQUE INDEX `users_guest_id_unique` ON `users` (`guest_id`);--> statement-breakpoint
CREATE UNIQUE INDEX `users_email_unique` ON `users` (`email`);--> statement-breakpoint
CREATE TABLE `players` (
`id` text PRIMARY KEY NOT NULL,
`user_id` text NOT NULL,
`name` text NOT NULL,
`emoji` text NOT NULL,
`color` text NOT NULL,
`is_active` integer DEFAULT false NOT NULL,
`created_at` integer NOT NULL,
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE INDEX `players_user_id_idx` ON `players` (`user_id`);--> statement-breakpoint
CREATE TABLE `user_stats` (
`user_id` text PRIMARY KEY NOT NULL,
`games_played` integer DEFAULT 0 NOT NULL,
`total_wins` integer DEFAULT 0 NOT NULL,
`favorite_game_type` text,
`best_time` integer,
`highest_accuracy` real DEFAULT 0 NOT NULL,
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
);

View File

@ -0,0 +1,236 @@
{
"version": "6",
"dialect": "sqlite",
"id": "949424bf-1933-497c-af2d-cab6ee81083d",
"prevId": "00000000-0000-0000-0000-000000000000",
"tables": {
"users": {
"name": "users",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"guest_id": {
"name": "guest_id",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"created_at": {
"name": "created_at",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"upgraded_at": {
"name": "upgraded_at",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"users_guest_id_unique": {
"name": "users_guest_id_unique",
"columns": [
"guest_id"
],
"isUnique": true
},
"users_email_unique": {
"name": "users_email_unique",
"columns": [
"email"
],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"players": {
"name": "players",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"user_id": {
"name": "user_id",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"emoji": {
"name": "emoji",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"color": {
"name": "color",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"is_active": {
"name": "is_active",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": false
},
"created_at": {
"name": "created_at",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {
"players_user_id_idx": {
"name": "players_user_id_idx",
"columns": [
"user_id"
],
"isUnique": false
}
},
"foreignKeys": {
"players_user_id_users_id_fk": {
"name": "players_user_id_users_id_fk",
"tableFrom": "players",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"user_stats": {
"name": "user_stats",
"columns": {
"user_id": {
"name": "user_id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"games_played": {
"name": "games_played",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": 0
},
"total_wins": {
"name": "total_wins",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": 0
},
"favorite_game_type": {
"name": "favorite_game_type",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"best_time": {
"name": "best_time",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"highest_accuracy": {
"name": "highest_accuracy",
"type": "real",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": 0
}
},
"indexes": {},
"foreignKeys": {
"user_stats_user_id_users_id_fk": {
"name": "user_stats_user_id_users_id_fk",
"tableFrom": "user_stats",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
}
},
"views": {},
"enums": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
},
"internal": {
"indexes": {}
}
}

View File

@ -0,0 +1,13 @@
{
"version": "7",
"dialect": "sqlite",
"entries": [
{
"idx": 0,
"version": "6",
"when": 1759701472375,
"tag": "0000_third_carnage",
"breakpoints": true
}
]
}

View File

@ -12,7 +12,12 @@
"type-check": "tsc --noEmit",
"clean": "rm -rf .next",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
"build-storybook": "storybook build",
"db:generate": "drizzle-kit generate",
"db:migrate": "tsx src/db/migrate.ts",
"db:push": "drizzle-kit push",
"db:studio": "drizzle-kit studio",
"db:drop": "drizzle-kit drop"
},
"dependencies": {
"@dnd-kit/core": "^6.3.1",
@ -24,6 +29,7 @@
"@myriaddreamin/typst.ts": "0.6.1-rc3",
"@number-flow/react": "^0.5.10",
"@pandacss/dev": "^0.20.0",
"@paralleldrive/cuid2": "^2.2.2",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.5",
@ -47,11 +53,15 @@
"@tanstack/react-form": "^0.19.0",
"@tanstack/react-query": "^5.90.2",
"@types/jsdom": "^21.1.7",
"better-sqlite3": "^12.4.1",
"drizzle-orm": "^0.44.6",
"emojibase-data": "^16.0.3",
"jose": "^6.1.0",
"lucide-react": "^0.294.0",
"make-plural": "^7.4.0",
"nanoid": "^5.1.6",
"next": "^14.2.32",
"next-auth": "5.0.0-beta.29",
"python-bridge": "^1.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@ -64,17 +74,20 @@
"@storybook/nextjs": "^9.1.7",
"@testing-library/jest-dom": "^6.8.0",
"@testing-library/react": "^16.3.0",
"@types/better-sqlite3": "^7.6.13",
"@types/node": "^20.0.0",
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.0",
"@vitejs/plugin-react": "^5.0.2",
"concurrently": "^8.0.0",
"drizzle-kit": "^0.31.5",
"eslint": "^8.0.0",
"eslint-config-next": "^14.0.0",
"eslint-plugin-storybook": "^9.1.7",
"happy-dom": "^18.0.1",
"jsdom": "^27.0.0",
"storybook": "^9.1.7",
"tsx": "^4.20.5",
"typescript": "^5.0.0",
"vitest": "^1.0.0"
},

View File

@ -0,0 +1,61 @@
import { describe, it, expect } from 'vitest'
import Database from 'better-sqlite3'
import { drizzle } from 'drizzle-orm/better-sqlite3'
describe('Database connection', () => {
it('connects to in-memory database', () => {
const sqlite = new Database(':memory:')
const db = drizzle(sqlite)
expect(db).toBeDefined()
})
it('enables foreign keys', () => {
const sqlite = new Database(':memory:')
sqlite.pragma('foreign_keys = ON')
const result = sqlite.pragma('foreign_keys', { simple: true })
expect(result).toBe(1)
})
it('can run simple queries', () => {
const sqlite = new Database(':memory:')
const db = drizzle(sqlite)
// Create simple table
sqlite.exec(`
CREATE TABLE test (
id TEXT PRIMARY KEY,
value TEXT
)
`)
// Insert
sqlite.prepare("INSERT INTO test (id, value) VALUES ('1', 'hello')").run()
// Query
const result = sqlite.prepare("SELECT * FROM test WHERE id = '1'").get() as {
id: string
value: string
}
expect(result).toEqual({ id: '1', value: 'hello' })
})
it('supports WAL mode (file-based DB)', () => {
// WAL mode doesn't work with in-memory databases
// In-memory databases always use 'memory' journal mode
// This test verifies the pragma can be set (will use WAL for file DBs)
const sqlite = new Database(':memory:')
// Try to set WAL mode (will return 'memory' for in-memory DB)
sqlite.pragma('journal_mode = WAL')
const result = sqlite.pragma('journal_mode', { simple: true })
// In-memory databases can't use WAL, so expect 'memory'
expect(result).toBe('memory')
// The actual app uses a file-based DB which will support WAL
})
})

View File

@ -0,0 +1,147 @@
import { describe, it, expect, beforeEach } from 'vitest'
import Database from 'better-sqlite3'
import { migrate } from 'drizzle-orm/better-sqlite3/migrator'
import { drizzle } from 'drizzle-orm/better-sqlite3'
describe('Migrations E2E', () => {
let sqlite: Database.Database
beforeEach(() => {
// Fresh in-memory DB for each test
sqlite = new Database(':memory:')
sqlite.pragma('foreign_keys = ON')
})
it('applies all migrations successfully', () => {
const db = drizzle(sqlite)
// Should not throw
expect(() => {
migrate(db, { migrationsFolder: './drizzle' })
}).not.toThrow()
})
it('creates all expected tables', () => {
const db = drizzle(sqlite)
migrate(db, { migrationsFolder: './drizzle' })
const tables = sqlite
.prepare("SELECT name FROM sqlite_master WHERE type='table'")
.all() as Array<{ name: string }>
const tableNames = tables.map((t) => t.name)
expect(tableNames).toContain('users')
expect(tableNames).toContain('players')
expect(tableNames).toContain('user_stats')
})
it('creates unique indexes on users', () => {
const db = drizzle(sqlite)
migrate(db, { migrationsFolder: './drizzle' })
const indexes = sqlite
.prepare("SELECT name FROM sqlite_master WHERE type='index' AND tbl_name='users'")
.all() as Array<{ name: string }>
const indexNames = indexes.map((i) => i.name)
expect(indexNames).toContain('users_guest_id_unique')
expect(indexNames).toContain('users_email_unique')
})
it('creates index on players userId', () => {
const db = drizzle(sqlite)
migrate(db, { migrationsFolder: './drizzle' })
const indexes = sqlite
.prepare("SELECT name FROM sqlite_master WHERE type='index' AND tbl_name='players'")
.all() as Array<{ name: string }>
const indexNames = indexes.map((i) => i.name)
expect(indexNames).toContain('players_user_id_idx')
})
it('enforces foreign key constraints', () => {
const db = drizzle(sqlite)
migrate(db, { migrationsFolder: './drizzle' })
// Try to insert player with non-existent userId
expect(() => {
sqlite
.prepare(
"INSERT INTO players (id, user_id, name, emoji, color, is_active, created_at) VALUES ('p1', 'nonexistent', 'Test', '😀', '#000', 0, 0)"
)
.run()
}).toThrow()
})
it('cascades deletes from users to players', () => {
const db = drizzle(sqlite)
migrate(db, { migrationsFolder: './drizzle' })
// Insert user
sqlite
.prepare("INSERT INTO users (id, guest_id, created_at) VALUES ('u1', 'g1', 0)")
.run()
// Insert player for that user
sqlite
.prepare(
"INSERT INTO players (id, user_id, name, emoji, color, is_active, created_at) VALUES ('p1', 'u1', 'Test', '😀', '#000', 0, 0)"
)
.run()
// Verify player exists
const playerBefore = sqlite.prepare("SELECT * FROM players WHERE id = 'p1'").get()
expect(playerBefore).toBeDefined()
// Delete user
sqlite.prepare("DELETE FROM users WHERE id = 'u1'").run()
// Verify player was cascade deleted
const playerAfter = sqlite.prepare("SELECT * FROM players WHERE id = 'p1'").get()
expect(playerAfter).toBeUndefined()
})
it('cascades deletes from users to user_stats', () => {
const db = drizzle(sqlite)
migrate(db, { migrationsFolder: './drizzle' })
// Insert user
sqlite
.prepare("INSERT INTO users (id, guest_id, created_at) VALUES ('u1', 'g1', 0)")
.run()
// Insert stats for that user
sqlite
.prepare(
"INSERT INTO user_stats (user_id, games_played, total_wins, highest_accuracy) VALUES ('u1', 5, 2, 0.8)"
)
.run()
// Verify stats exist
const statsBefore = sqlite.prepare("SELECT * FROM user_stats WHERE user_id = 'u1'").get()
expect(statsBefore).toBeDefined()
// Delete user
sqlite.prepare("DELETE FROM users WHERE id = 'u1'").run()
// Verify stats were cascade deleted
const statsAfter = sqlite.prepare("SELECT * FROM user_stats WHERE user_id = 'u1'").get()
expect(statsAfter).toBeUndefined()
})
it('is idempotent (can run migrations twice)', () => {
const db = drizzle(sqlite)
// Run migrations first time
migrate(db, { migrationsFolder: './drizzle' })
// Run migrations second time (should not throw)
expect(() => {
migrate(db, { migrationsFolder: './drizzle' })
}).not.toThrow()
})
})

View File

@ -0,0 +1,67 @@
import { describe, it, expect } from 'vitest'
import { users, players, userStats } from '../schema'
describe('Schema validation', () => {
describe('users table', () => {
it('has correct structure', () => {
expect(users.id).toBeDefined()
expect(users.guestId).toBeDefined()
expect(users.createdAt).toBeDefined()
expect(users.upgradedAt).toBeDefined()
expect(users.email).toBeDefined()
expect(users.name).toBeDefined()
})
it('has unique constraints on guestId and email', () => {
expect(users.guestId.notNull).toBe(true)
expect(users.email.notNull).toBe(false) // nullable until upgrade
})
})
describe('players table', () => {
it('has correct structure', () => {
expect(players.id).toBeDefined()
expect(players.userId).toBeDefined()
expect(players.name).toBeDefined()
expect(players.emoji).toBeDefined()
expect(players.color).toBeDefined()
expect(players.isActive).toBeDefined()
expect(players.createdAt).toBeDefined()
})
it('has foreign key to users', () => {
const userIdColumn = players.userId
expect(userIdColumn).toBeDefined()
expect(userIdColumn.notNull).toBe(true)
})
it('has required fields as not null', () => {
expect(players.name.notNull).toBe(true)
expect(players.emoji.notNull).toBe(true)
expect(players.color.notNull).toBe(true)
expect(players.isActive.notNull).toBe(true)
})
})
describe('user_stats table', () => {
it('has correct structure', () => {
expect(userStats.userId).toBeDefined()
expect(userStats.gamesPlayed).toBeDefined()
expect(userStats.totalWins).toBeDefined()
expect(userStats.favoriteGameType).toBeDefined()
expect(userStats.bestTime).toBeDefined()
expect(userStats.highestAccuracy).toBeDefined()
})
it('has foreign key to users', () => {
const userIdColumn = userStats.userId
expect(userIdColumn).toBeDefined()
})
it('has correct defaults', () => {
expect(userStats.gamesPlayed.notNull).toBe(true)
expect(userStats.totalWins.notNull).toBe(true)
expect(userStats.highestAccuracy.notNull).toBe(true)
})
})
})

23
apps/web/src/db/index.ts Normal file
View File

@ -0,0 +1,23 @@
import Database from 'better-sqlite3'
import { drizzle } from 'drizzle-orm/better-sqlite3'
import * as schema from './schema'
/**
* Database connection and client
*
* Creates a singleton SQLite connection with Drizzle ORM.
* Enables foreign key constraints (required for cascading deletes).
*/
const databaseUrl = process.env.DATABASE_URL || './data/sqlite.db'
const sqlite = new Database(databaseUrl)
// Enable foreign keys (SQLite requires explicit enable)
sqlite.pragma('foreign_keys = ON')
// Enable WAL mode for better concurrency
sqlite.pragma('journal_mode = WAL')
export const db = drizzle(sqlite, { schema })
export { schema }

View File

@ -0,0 +1,23 @@
import { migrate } from 'drizzle-orm/better-sqlite3/migrator'
import { db } from './index'
/**
* Migration runner
*
* Runs all pending migrations in the drizzle/ folder.
* Safe to run multiple times (migrations are idempotent).
*
* Usage: pnpm db:migrate
*/
try {
console.log('🔄 Running migrations...')
migrate(db, { migrationsFolder: './drizzle' })
console.log('✅ Migrations complete')
process.exit(0)
} catch (error) {
console.error('❌ Migration failed:', error)
process.exit(1)
}

View File

@ -0,0 +1,10 @@
/**
* Database schema exports
*
* This is the single source of truth for the database schema.
* All tables, relations, and types are exported from here.
*/
export * from './users'
export * from './players'
export * from './user-stats'

View File

@ -0,0 +1,43 @@
import { sqliteTable, text, integer, index } from 'drizzle-orm/sqlite-core'
import { createId } from '@paralleldrive/cuid2'
import { users } from './users'
/**
* Players table - user-created player profiles for games
*
* Each user can have multiple players (for multi-player modes).
* Players are scoped to a user and deleted when user is deleted.
*/
export const players = sqliteTable('players', {
id: text('id').primaryKey().$defaultFn(() => createId()),
/** Foreign key to users table - cascades on delete */
userId: text('user_id')
.notNull()
.references(() => users.id, { onDelete: 'cascade' }),
/** Player display name */
name: text('name').notNull(),
/** Player emoji avatar */
emoji: text('emoji').notNull(),
/** Player color (hex) for UI theming */
color: text('color').notNull(),
/** Whether this player is currently active in games */
isActive: integer('is_active', { mode: 'boolean' })
.notNull()
.default(false),
/** When this player was created */
createdAt: integer('created_at', { mode: 'timestamp' })
.notNull()
.$defaultFn(() => new Date()),
}, (table) => ({
/** Index for fast lookups by userId */
userIdIdx: index('players_user_id_idx').on(table.userId),
}))
export type Player = typeof players.$inferSelect
export type NewPlayer = typeof players.$inferInsert

View File

@ -0,0 +1,35 @@
import { sqliteTable, text, integer, real } from 'drizzle-orm/sqlite-core'
import { users } from './users'
/**
* User stats table - game statistics per user
*
* One-to-one with users table. Tracks aggregate game performance.
* Deleted when user is deleted (cascade).
*/
export const userStats = sqliteTable('user_stats', {
/** Primary key and foreign key to users table */
userId: text('user_id')
.primaryKey()
.references(() => users.id, { onDelete: 'cascade' }),
/** Total number of games played */
gamesPlayed: integer('games_played').notNull().default(0),
/** Total number of games won */
totalWins: integer('total_wins').notNull().default(0),
/** User's most-played game type */
favoriteGameType: text('favorite_game_type', {
enum: ['abacus-numeral', 'complement-pairs'],
}),
/** Best completion time in milliseconds */
bestTime: integer('best_time'),
/** Highest accuracy percentage (0.0 - 1.0) */
highestAccuracy: real('highest_accuracy').notNull().default(0),
})
export type UserStats = typeof userStats.$inferSelect
export type NewUserStats = typeof userStats.$inferInsert

View File

@ -0,0 +1,32 @@
import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'
import { createId } from '@paralleldrive/cuid2'
/**
* Users table - stores both guest and authenticated users
*
* Guest users are created automatically on first visit via middleware.
* They can upgrade to full accounts later while preserving their data.
*/
export const users = sqliteTable('users', {
id: text('id').primaryKey().$defaultFn(() => createId()),
/** Stable guest ID from HttpOnly cookie - unique per browser session */
guestId: text('guest_id').notNull().unique(),
/** When this user record was created */
createdAt: integer('created_at', { mode: 'timestamp' })
.notNull()
.$defaultFn(() => new Date()),
/** When guest upgraded to full account (null for guests) */
upgradedAt: integer('upgraded_at', { mode: 'timestamp' }),
/** Email (only set after upgrade) */
email: text('email').unique(),
/** Display name (only set after upgrade) */
name: text('name'),
})
export type User = typeof users.$inferSelect
export type NewUser = typeof users.$inferInsert

View File

@ -67,6 +67,9 @@ importers:
'@pandacss/dev':
specifier: ^0.20.0
version: 0.20.1(jsdom@27.0.0)(typescript@5.0.2)
'@paralleldrive/cuid2':
specifier: ^2.2.2
version: 2.2.2
'@radix-ui/react-accordion':
specifier: ^1.1.2
version: 1.1.2(@types/react-dom@18.2.0)(@types/react@18.2.0)(react-dom@18.2.0)(react@18.2.0)
@ -136,9 +139,18 @@ importers:
'@types/jsdom':
specifier: ^21.1.7
version: 21.1.7
better-sqlite3:
specifier: ^12.4.1
version: 12.4.1
drizzle-orm:
specifier: ^0.44.6
version: 0.44.6(@types/better-sqlite3@7.6.13)(better-sqlite3@12.4.1)
emojibase-data:
specifier: ^16.0.3
version: 16.0.3(emojibase@16.0.0)
jose:
specifier: ^6.1.0
version: 6.1.0
lucide-react:
specifier: ^0.294.0
version: 0.294.0(react@18.2.0)
@ -151,6 +163,9 @@ importers:
next:
specifier: ^14.2.32
version: 14.2.32(@babel/core@7.28.4)(@playwright/test@1.55.1)(react-dom@18.2.0)(react@18.2.0)
next-auth:
specifier: 5.0.0-beta.29
version: 5.0.0-beta.29(next@14.2.32)(react@18.2.0)
python-bridge:
specifier: ^1.1.0
version: 1.1.0
@ -182,6 +197,9 @@ importers:
'@testing-library/react':
specifier: ^16.3.0
version: 16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@18.2.0)(@types/react@18.2.0)(react-dom@18.2.0)(react@18.2.0)
'@types/better-sqlite3':
specifier: ^7.6.13
version: 7.6.13
'@types/node':
specifier: ^20.0.0
version: 20.0.0
@ -197,6 +215,9 @@ importers:
concurrently:
specifier: ^8.0.0
version: 8.0.0
drizzle-kit:
specifier: ^0.31.5
version: 0.31.5
eslint:
specifier: ^8.0.0
version: 8.0.0
@ -215,6 +236,9 @@ importers:
storybook:
specifier: ^9.1.7
version: 9.1.7(@testing-library/dom@10.4.1)(prettier@3.0.0)(vite@5.0.0)
tsx:
specifier: ^4.20.5
version: 4.20.5
typescript:
specifier: ^5.0.0
version: 5.0.2
@ -481,6 +505,27 @@ packages:
/@asamuzakjp/nwsapi@2.3.9:
resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==}
/@auth/core@0.40.0:
resolution: {integrity: sha512-n53uJE0RH5SqZ7N1xZoMKekbHfQgjd0sAEyUbE+IYJnmuQkbvuZnXItCU7d+i7Fj8VGOgqvNO7Mw4YfBTlZeQw==}
peerDependencies:
'@simplewebauthn/browser': ^9.0.1
'@simplewebauthn/server': ^9.0.2
nodemailer: ^6.8.0
peerDependenciesMeta:
'@simplewebauthn/browser':
optional: true
'@simplewebauthn/server':
optional: true
nodemailer:
optional: true
dependencies:
'@panva/hkdf': 1.2.1
jose: 6.1.0
oauth4webapi: 3.8.2
preact: 10.24.3
preact-render-to-string: 6.5.11(preact@10.24.3)
dev: false
/@aw-web-design/x-default-browser@1.4.126:
resolution: {integrity: sha512-Xk1sIhyNC/esHGGVjL/niHLowM0csl/kFO5uawBy4IrWwy0o1G8LGt3jP6nmWGz+USxeeqbihAmp/oVZju6wug==}
hasBin: true
@ -1872,6 +1917,10 @@ packages:
tslib: 2.8.1
dev: false
/@drizzle-team/brocli@0.10.2:
resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==}
dev: true
/@emnapi/core@1.5.0:
resolution: {integrity: sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==}
requiresBuild: true
@ -1905,6 +1954,22 @@ packages:
react: 18.2.0
dev: true
/@esbuild-kit/core-utils@3.3.2:
resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==}
deprecated: 'Merged into tsx: https://tsx.is'
dependencies:
esbuild: 0.18.20
source-map-support: 0.5.21
dev: true
/@esbuild-kit/esm-loader@2.6.5:
resolution: {integrity: sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==}
deprecated: 'Merged into tsx: https://tsx.is'
dependencies:
'@esbuild-kit/core-utils': 3.3.2
get-tsconfig: 4.10.1
dev: true
/@esbuild/aix-ppc64@0.19.12:
resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==}
engines: {node: '>=12'}
@ -2950,6 +3015,11 @@ packages:
requiresBuild: true
optional: true
/@noble/hashes@1.8.0:
resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==}
engines: {node: ^14.21.3 || >=16}
dev: false
/@nodelib/fs.scandir@2.1.5:
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'}
@ -3295,6 +3365,16 @@ packages:
resolution: {integrity: sha512-GWlGqNdv/ILa9iEGks7xZL966yKhCPtLSolPrOnAE6K/lBb4djtpBOHDgQ1kFJ4yVSwCMGkHd0G/NSqdl0t94A==}
dev: false
/@panva/hkdf@1.2.1:
resolution: {integrity: sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==}
dev: false
/@paralleldrive/cuid2@2.2.2:
resolution: {integrity: sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==}
dependencies:
'@noble/hashes': 1.8.0
dev: false
/@pkgjs/parseargs@0.11.0:
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
@ -6540,6 +6620,11 @@ packages:
'@babel/types': 7.28.4
dev: true
/@types/better-sqlite3@7.6.13:
resolution: {integrity: sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==}
dependencies:
'@types/node': 20.0.0
/@types/body-parser@1.19.6:
resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==}
dependencies:
@ -8018,7 +8103,6 @@ packages:
/base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
dev: true
/baseline-browser-mapping@2.8.3:
resolution: {integrity: sha512-mcE+Wr2CAhHNWxXN/DdTI+n4gsPc5QpXpWnyCQWiQYIYZX+ZMJ8juXZgjRa/0/YPJo/NSsgW15/YgmI4nbysYw==}
@ -8035,6 +8119,15 @@ packages:
open: 8.4.2
dev: true
/better-sqlite3@12.4.1:
resolution: {integrity: sha512-3yVdyZhklTiNrtg+4WqHpJpFDd+WHTg2oM7UcR80GqL05AOV0xEJzc6qNvFYoEtE+hRp1n9MpN6/+4yhlGkDXQ==}
engines: {node: 20.x || 22.x || 23.x || 24.x}
requiresBuild: true
dependencies:
bindings: 1.5.0
prebuild-install: 7.1.3
dev: false
/bidi-js@1.0.3:
resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==}
dependencies:
@ -8053,13 +8146,18 @@ packages:
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
engines: {node: '>=8'}
/bindings@1.5.0:
resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
dependencies:
file-uri-to-path: 1.0.0
dev: false
/bl@4.1.0:
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
dependencies:
buffer: 5.7.1
inherits: 2.0.4
readable-stream: 3.6.2
dev: true
/bluebird@3.7.2:
resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==}
@ -8233,7 +8331,6 @@ packages:
dependencies:
base64-js: 1.5.1
ieee754: 1.2.1
dev: true
/buffer@6.0.3:
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
@ -8419,7 +8516,6 @@ packages:
/chownr@1.1.4:
resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
dev: true
/chownr@2.0.0:
resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
@ -9066,6 +9162,13 @@ packages:
resolution: {integrity: sha512-/OMUlsRLrSgHPOWCwembsFFTT4DY7Ts9GGlwK8v9yeLOyYZSPKIfn/1oOuV9UmpQ9CZi5JeyT8edunRoBOOl5g==}
dev: false
/decompress-response@6.0.0:
resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
engines: {node: '>=10'}
dependencies:
mimic-response: 3.1.0
dev: false
/dedent@0.7.0:
resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==}
dev: true
@ -9109,7 +9212,6 @@ packages:
/deep-extend@0.6.0:
resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
engines: {node: '>=4.0.0'}
dev: true
/deep-is@0.1.4:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
@ -9211,6 +9313,11 @@ packages:
engines: {node: '>=8'}
dev: true
/detect-libc@2.1.2:
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
engines: {node: '>=8'}
dev: false
/detect-node-es@1.1.0:
resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==}
@ -9336,6 +9443,114 @@ packages:
engines: {node: '>=12'}
dev: true
/drizzle-kit@0.31.5:
resolution: {integrity: sha512-+CHgPFzuoTQTt7cOYCV6MOw2w8vqEn/ap1yv4bpZOWL03u7rlVRQhUY0WYT3rHsgVTXwYQDZaSUJSQrMBUKuWg==}
hasBin: true
dependencies:
'@drizzle-team/brocli': 0.10.2
'@esbuild-kit/esm-loader': 2.6.5
esbuild: 0.25.9
esbuild-register: 3.6.0(esbuild@0.25.9)
transitivePeerDependencies:
- supports-color
dev: true
/drizzle-orm@0.44.6(@types/better-sqlite3@7.6.13)(better-sqlite3@12.4.1):
resolution: {integrity: sha512-uy6uarrrEOc9K1u5/uhBFJbdF5VJ5xQ/Yzbecw3eAYOunv5FDeYkR2m8iitocdHBOHbvorviKOW5GVw0U1j4LQ==}
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
dependencies:
'@types/better-sqlite3': 7.6.13
better-sqlite3: 12.4.1
dev: false
/dunder-proto@1.0.1:
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
engines: {node: '>= 0.4'}
@ -9431,7 +9646,6 @@ packages:
resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==}
dependencies:
once: 1.4.0
dev: true
/endent@2.1.0:
resolution: {integrity: sha512-r8VyPX7XL8U01Xgnb1CjZ3XV+z90cXIJ9JPE/R9SEC9vpw2P6CfsRPJmp20DppC5N7ZAMCmjYkJIa744Iyg96w==}
@ -10229,6 +10443,11 @@ packages:
strip-final-newline: 3.0.0
dev: true
/expand-template@2.0.3:
resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
engines: {node: '>=6'}
dev: false
/express@4.21.2:
resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==}
engines: {node: '>= 0.10.0'}
@ -10384,6 +10603,10 @@ packages:
ramda: 0.29.0
dev: true
/file-uri-to-path@1.0.0:
resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
dev: false
/filelist@1.0.4:
resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==}
dependencies:
@ -10601,7 +10824,6 @@ packages:
/fs-constants@1.0.0:
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
dev: true
/fs-extra@10.1.0:
resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==}
@ -10793,6 +11015,10 @@ packages:
traverse: 0.6.8
dev: true
/github-from-package@0.0.0:
resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==}
dev: false
/github-slugger@1.5.0:
resolution: {integrity: sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw==}
dev: true
@ -11188,7 +11414,6 @@ packages:
/ieee754@1.2.1:
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
dev: true
/ignore@4.0.6:
resolution: {integrity: sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==}
@ -11258,11 +11483,9 @@ packages:
/inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
dev: true
/ini@1.3.8:
resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
dev: true
/internal-slot@1.1.0:
resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
@ -11819,6 +12042,10 @@ packages:
hasBin: true
dev: true
/jose@6.1.0:
resolution: {integrity: sha512-TTQJyoEoKcC1lscpVDCSsVgYzUDg/0Bt3WE//WiTPK6uOCQC2KZS4MpugbMWt/zyjkopgZoXhZuCi00gLudfUA==}
dev: false
/joycon@3.1.1:
resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==}
engines: {node: '>=10'}
@ -12501,6 +12728,11 @@ packages:
engines: {node: '>=12'}
dev: true
/mimic-response@3.1.0:
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
engines: {node: '>=10'}
dev: false
/min-indent@1.0.1:
resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
engines: {node: '>=4'}
@ -12550,7 +12782,6 @@ packages:
/minimist@1.2.8:
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
dev: true
/minipass@3.3.6:
resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==}
@ -12579,7 +12810,6 @@ packages:
/mkdirp-classic@0.5.3:
resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
dev: true
/mkdirp@0.5.6:
resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
@ -12639,6 +12869,10 @@ packages:
hasBin: true
dev: false
/napi-build-utils@2.0.0:
resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==}
dev: false
/napi-postinstall@0.3.3:
resolution: {integrity: sha512-uTp172LLXSxuSYHv/kou+f6KW3SMppU9ivthaVTXian9sOt3XM/zHYHpRZiLgQoxeWfYUnslNWQHF1+G71xcow==}
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
@ -12667,6 +12901,27 @@ packages:
resolution: {integrity: sha512-EZSPZB70jiVsivaBLYDCyntd5eH8NTSMOn3rB+HxwdmKThGELLdYv8qVIMWvZEFy9w8ZZpW9h9OB32l1rGtj7g==}
dev: true
/next-auth@5.0.0-beta.29(next@14.2.32)(react@18.2.0):
resolution: {integrity: sha512-Ukpnuk3NMc/LiOl32njZPySk7pABEzbjhMUFd5/n10I0ZNC7NCuVv8IY2JgbDek2t/PUOifQEoUiOOTLy4os5A==}
peerDependencies:
'@simplewebauthn/browser': ^9.0.1
'@simplewebauthn/server': ^9.0.2
next: ^14.0.0-0 || ^15.0.0-0
nodemailer: ^6.6.5
react: ^18.2.0 || ^19.0.0-0
peerDependenciesMeta:
'@simplewebauthn/browser':
optional: true
'@simplewebauthn/server':
optional: true
nodemailer:
optional: true
dependencies:
'@auth/core': 0.40.0
next: 14.2.32(@babel/core@7.28.4)(@playwright/test@1.55.1)(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
dev: false
/next@14.2.32(@babel/core@7.28.4)(@playwright/test@1.55.1)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-fg5g0GZ7/nFc09X8wLe6pNSU8cLWbLRG3TZzPJ1BJvi2s9m7eF991se67wliM9kR5yLHRkyGKU49MMx58s3LJg==}
engines: {node: '>=18.17.0'}
@ -12716,6 +12971,13 @@ packages:
tslib: 2.8.1
dev: true
/node-abi@3.77.0:
resolution: {integrity: sha512-DSmt0OEcLoK4i3NuscSbGjOf3bqiDEutejqENSplMSFA/gmB8mkED9G4pKWnPl7MDU4rSHebKPHeitpDfyH0cQ==}
engines: {node: '>=10'}
dependencies:
semver: 7.7.2
dev: false
/node-abort-controller@3.1.1:
resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==}
dev: true
@ -12947,6 +13209,10 @@ packages:
ufo: 1.6.1
dev: true
/oauth4webapi@3.8.2:
resolution: {integrity: sha512-FzZZ+bht5X0FKe7Mwz3DAVAmlH1BV5blSak/lHMBKz0/EBMhX6B10GlQYI51+oRp8ObJaX0g6pXrAxZh5s8rjw==}
dev: false
/object-assign@4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
@ -13046,7 +13312,6 @@ packages:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
dependencies:
wrappy: 1.0.2
dev: true
/onetime@5.1.2:
resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
@ -13701,6 +13966,37 @@ packages:
picocolors: 1.1.1
source-map-js: 1.2.1
/preact-render-to-string@6.5.11(preact@10.24.3):
resolution: {integrity: sha512-ubnauqoGczeGISiOh6RjX0/cdaF8v/oDXIjO85XALCQjwQP+SB4RDXXtvZ6yTYSjG+PC1QRP2AhPgCEsM2EvUw==}
peerDependencies:
preact: '>=10'
dependencies:
preact: 10.24.3
dev: false
/preact@10.24.3:
resolution: {integrity: sha512-Z2dPnBnMUfyQfSQ+GBdsGa16hz35YmLmtTLhM169uW944hYL6xzTYkJjC07j+Wosz733pMWx0fgON3JNw1jJQA==}
dev: false
/prebuild-install@7.1.3:
resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==}
engines: {node: '>=10'}
hasBin: true
dependencies:
detect-libc: 2.1.2
expand-template: 2.0.3
github-from-package: 0.0.0
minimist: 1.2.8
mkdirp-classic: 0.5.3
napi-build-utils: 2.0.0
node-abi: 3.77.0
pump: 3.0.3
rc: 1.2.8
simple-get: 4.0.1
tar-fs: 2.1.4
tunnel-agent: 0.6.0
dev: false
/preferred-pm@3.1.4:
resolution: {integrity: sha512-lEHd+yEm22jXdCphDrkvIJQU66EuLojPPtvZkpKIkiD+l0DMThF/niqZKJSoU8Vl7iuvtmzyMhir9LdVy5WMnA==}
engines: {node: '>=10'}
@ -13835,7 +14131,6 @@ packages:
dependencies:
end-of-stream: 1.4.5
once: 1.4.0
dev: true
/pumpify@1.5.1:
resolution: {integrity: sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==}
@ -13946,7 +14241,6 @@ packages:
ini: 1.3.8
minimist: 1.2.8
strip-json-comments: 2.0.1
dev: true
/react-colorful@5.6.1(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==}
@ -14193,7 +14487,6 @@ packages:
inherits: 2.0.4
string_decoder: 1.3.0
util-deprecate: 1.0.2
dev: true
/readable-stream@4.7.0:
resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==}
@ -14534,7 +14827,6 @@ packages:
/safe-buffer@5.2.1:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
dev: true
/safe-push-apply@1.0.0:
resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==}
@ -14675,7 +14967,6 @@ packages:
resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==}
engines: {node: '>=10'}
hasBin: true
dev: true
/send@0.19.0:
resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==}
@ -14851,6 +15142,18 @@ packages:
pkg-conf: 2.1.0
dev: true
/simple-concat@1.0.1:
resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
dev: false
/simple-get@4.0.1:
resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==}
dependencies:
decompress-response: 6.0.0
once: 1.4.0
simple-concat: 1.0.1
dev: false
/simple-update-notifier@2.0.0:
resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==}
engines: {node: '>=10'}
@ -15166,7 +15469,6 @@ packages:
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
dependencies:
safe-buffer: 5.2.1
dev: true
/strip-ansi@6.0.1:
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
@ -15211,7 +15513,6 @@ packages:
/strip-json-comments@2.0.1:
resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==}
engines: {node: '>=0.10.0'}
dev: true
/strip-json-comments@3.1.1:
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
@ -15335,7 +15636,6 @@ packages:
mkdirp-classic: 0.5.3
pump: 3.0.3
tar-stream: 2.2.0
dev: true
/tar-stream@2.2.0:
resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
@ -15346,7 +15646,6 @@ packages:
fs-constants: 1.0.0
inherits: 2.0.4
readable-stream: 3.6.2
dev: true
/tar@6.2.1:
resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==}
@ -15775,6 +16074,12 @@ packages:
resolution: {integrity: sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==}
dev: true
/tunnel-agent@0.6.0:
resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
dependencies:
safe-buffer: 5.2.1
dev: false
/turbo-darwin-64@1.10.0:
resolution: {integrity: sha512-N0aVGFtBgOKd7pIdUiKREwnDhNHRIvpMJbmUw04c1AqEoTiKAKT6iuzcCozO5N/gYMVr0hxrXgLal5OLYXtcsw==}
cpu: [x64]
@ -16713,7 +17018,6 @@ packages:
/wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
dev: true
/write-file-atomic@2.4.3:
resolution: {integrity: sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==}