Compare commits

...

2 Commits

Author SHA1 Message Date
Oleg Proskurin 298898f79d feat: implement apikey auth 2025-10-01 00:14:14 +07:00
Oleg Proskurin 02c936e880 fix: config issues 2025-09-30 23:03:41 +07:00
25 changed files with 1522 additions and 47 deletions

View File

@ -10,9 +10,10 @@ CORS_ORIGIN=*
# Database Configuration # Database Configuration
DB_HOST=postgres DB_HOST=postgres
DB_PORT=5432 DB_PORT=5432
DB_NAME=banatie DB_NAME=banatie_db
DB_USER=banatie_user DB_USER=banatie_user
DB_PASSWORD=banatie_secure_password DB_PASSWORD=banatie_secure_password
DATABASE_URL=postgresql://banatie_user:banatie_secure_password@postgres:5432/banatie_db
# MinIO Storage Configuration (SNMD) # MinIO Storage Configuration (SNMD)
MINIO_ROOT_USER=banatie_admin MINIO_ROOT_USER=banatie_admin

View File

@ -0,0 +1,26 @@
# Monorepo-aware Dockerfile for API Service
# Development stage - for docker-compose development with hot reload
FROM node:20-alpine AS development
WORKDIR /app
# Install pnpm globally
RUN npm install -g pnpm@10.11.0
# Copy workspace configuration from root
COPY pnpm-workspace.yaml package.json pnpm-lock.yaml ./
# Copy all workspace packages
COPY packages/ ./packages/
COPY apps/api-service/ ./apps/api-service/
# Install all dependencies (workspace-aware)
RUN pnpm install --frozen-lockfile
# Set working directory to API service
WORKDIR /app/apps/api-service
# Expose port
EXPOSE 3000
# Use development command with hot reload
CMD ["pnpm", "dev"]

View File

@ -36,6 +36,7 @@
"pnpm": ">=8.0.0" "pnpm": ">=8.0.0"
}, },
"dependencies": { "dependencies": {
"@banatie/database": "workspace:*",
"@google/genai": "^1.17.0", "@google/genai": "^1.17.0",
"cors": "^2.8.5", "cors": "^2.8.5",
"dotenv": "^17.2.2", "dotenv": "^17.2.2",

View File

@ -6,6 +6,8 @@ import { generateRouter } from './routes/generate';
import { enhanceRouter } from './routes/enhance'; import { enhanceRouter } from './routes/enhance';
import { textToImageRouter } from './routes/textToImage'; import { textToImageRouter } from './routes/textToImage';
import { imagesRouter } from './routes/images'; import { imagesRouter } from './routes/images';
import bootstrapRoutes from './routes/bootstrap';
import adminKeysRoutes from './routes/admin/keys';
import { errorHandler, notFoundHandler } from './middleware/errorHandler'; import { errorHandler, notFoundHandler } from './middleware/errorHandler';
// Load environment variables // Load environment variables
@ -25,9 +27,16 @@ export const appConfig: Config = {
export const createApp = (): Application => { export const createApp = (): Application => {
const app = express(); const app = express();
// Middleware // Middleware - CORS configuration
const corsOrigin = process.env['CORS_ORIGIN']?.split(',') || [
'http://localhost:3001', // Landing
'http://localhost:3002', // Studio
'http://localhost:3003', // Admin
'*' // Allow all for development
];
app.use(cors({ app.use(cors({
origin: process.env['CORS_ORIGIN'] || '*', origin: corsOrigin,
credentials: true credentials: true
})); }));
@ -79,7 +88,14 @@ export const createApp = (): Application => {
res.json(info); res.json(info);
}); });
// Mount API routes // Public routes (no authentication)
// Bootstrap route (no auth, but works only once)
app.use('/api/bootstrap', bootstrapRoutes);
// Admin routes (require master key)
app.use('/api/admin/keys', adminKeysRoutes);
// Protected API routes (require valid API key)
app.use('/api', generateRouter); app.use('/api', generateRouter);
app.use('/api', enhanceRouter); app.use('/api', enhanceRouter);
app.use('/api', textToImageRouter); app.use('/api', textToImageRouter);

View File

@ -0,0 +1,8 @@
import { createDbClient } from '@banatie/database';
const DATABASE_URL = process.env['DATABASE_URL'] ||
'postgresql://banatie_user:banatie_secure_password@localhost:5434/banatie_db';
export const db = createDbClient(DATABASE_URL);
console.log(`[${new Date().toISOString()}] Database client initialized - ${new URL(DATABASE_URL).host}`);

View File

@ -0,0 +1,93 @@
import { Request, Response, NextFunction } from 'express';
/**
* Simple in-memory rate limiter
* Tracks requests per API key
*/
class RateLimiter {
private requests: Map<string, { count: number; resetAt: number }> = new Map();
private readonly limit: number;
private readonly windowMs: number;
constructor(limit: number = 100, windowMs: number = 60 * 60 * 1000) {
this.limit = limit;
this.windowMs = windowMs;
// Cleanup old entries every 5 minutes
setInterval(() => this.cleanup(), 5 * 60 * 1000);
}
check(keyId: string): { allowed: boolean; remaining: number; resetAt: number } {
const now = Date.now();
const record = this.requests.get(keyId);
if (!record || record.resetAt < now) {
// Create new window
const resetAt = now + this.windowMs;
this.requests.set(keyId, { count: 1, resetAt });
return { allowed: true, remaining: this.limit - 1, resetAt };
}
if (record.count >= this.limit) {
return { allowed: false, remaining: 0, resetAt: record.resetAt };
}
// Increment counter
record.count++;
return {
allowed: true,
remaining: this.limit - record.count,
resetAt: record.resetAt
};
}
private cleanup() {
const now = Date.now();
for (const [keyId, record] of this.requests.entries()) {
if (record.resetAt < now) {
this.requests.delete(keyId);
}
}
}
}
const rateLimiter = new RateLimiter(100, 60 * 60 * 1000); // 100 requests per hour
/**
* Rate limiting middleware
* Must be used AFTER validateApiKey middleware
*/
export function rateLimitByApiKey(
req: Request,
res: Response,
next: NextFunction
): void {
if (!req.apiKey) {
next();
return;
}
const result = rateLimiter.check(req.apiKey.id);
// Add rate limit headers
res.setHeader('X-RateLimit-Limit', '100');
res.setHeader('X-RateLimit-Remaining', result.remaining.toString());
res.setHeader('X-RateLimit-Reset', new Date(result.resetAt).toISOString());
if (!result.allowed) {
const retryAfter = Math.ceil((result.resetAt - Date.now()) / 1000);
console.warn(`[${new Date().toISOString()}] Rate limit exceeded: ${req.apiKey.id} (${req.apiKey.keyType}) - reset: ${new Date(result.resetAt).toISOString()}`);
res.status(429)
.setHeader('Retry-After', retryAfter.toString())
.json({
error: 'Rate limit exceeded',
message: `Too many requests. Retry after ${retryAfter} seconds`,
retryAfter,
});
return;
}
next();
}

View File

@ -0,0 +1,31 @@
import { Request, Response, NextFunction } from 'express';
/**
* Middleware to ensure the API key is a master key
* Must be used AFTER validateApiKey middleware
*/
export function requireMasterKey(
req: Request,
res: Response,
next: NextFunction
): void {
if (!req.apiKey) {
res.status(401).json({
error: 'Authentication required',
message: 'This endpoint requires authentication',
});
return;
}
if (req.apiKey.keyType !== 'master') {
console.warn(`[${new Date().toISOString()}] Non-master key attempted admin action: ${req.apiKey.id} (${req.apiKey.keyType}) - ${req.path}`);
res.status(403).json({
error: 'Master key required',
message: 'This endpoint requires a master API key',
});
return;
}
next();
}

View File

@ -0,0 +1,58 @@
import { Request, Response, NextFunction } from 'express';
import { ApiKeyService } from '../../services/ApiKeyService';
import type { ApiKey } from '@banatie/database';
// Extend Express Request type to include apiKey
declare global {
namespace Express {
interface Request {
apiKey?: ApiKey;
}
}
}
const apiKeyService = new ApiKeyService();
/**
* Middleware to validate API key from X-API-Key header
*/
export async function validateApiKey(
req: Request,
res: Response,
next: NextFunction
): Promise<void> {
const providedKey = req.headers['x-api-key'] as string;
if (!providedKey) {
res.status(401).json({
error: 'Missing API key',
message: 'Provide your API key via X-API-Key header',
});
return;
}
try {
const apiKey = await apiKeyService.validateKey(providedKey);
if (!apiKey) {
res.status(401).json({
error: 'Invalid API key',
message: 'The provided API key is invalid, expired, or revoked',
});
return;
}
// Attach to request for use in routes
req.apiKey = apiKey;
console.log(`[${new Date().toISOString()}] API key validated: ${apiKey.id} (${apiKey.keyType})`);
next();
} catch (error) {
console.error(`[${new Date().toISOString()}] API key validation error:`, error);
res.status(500).json({
error: 'Authentication failed',
message: 'An error occurred during authentication',
});
}
}

View File

@ -0,0 +1,137 @@
import express from 'express';
import { ApiKeyService } from '../../services/ApiKeyService';
import { validateApiKey } from '../../middleware/auth/validateApiKey';
import { requireMasterKey } from '../../middleware/auth/requireMasterKey';
const router = express.Router();
const apiKeyService = new ApiKeyService();
// All admin routes require master key
router.use(validateApiKey);
router.use(requireMasterKey);
/**
* Create a new API key
* POST /api/admin/keys
*/
router.post('/', async (req, res) => {
try {
const { type, projectId, name, expiresInDays } = req.body;
// Validation
if (!type || !['master', 'project'].includes(type)) {
return res.status(400).json({
error: 'Invalid type',
message: 'Type must be either "master" or "project"',
});
}
if (type === 'project' && !projectId) {
return res.status(400).json({
error: 'Missing projectId',
message: 'Project keys require a projectId',
});
}
// Create key
const result = type === 'master'
? await apiKeyService.createMasterKey(name, req.apiKey!.id)
: await apiKeyService.createProjectKey(
projectId,
name,
req.apiKey!.id,
expiresInDays || 90
);
console.log(`[${new Date().toISOString()}] New API key created by admin: ${result.metadata.id} (${result.metadata.keyType}) - by: ${req.apiKey!.id}`);
res.status(201).json({
apiKey: result.key,
metadata: {
id: result.metadata.id,
type: result.metadata.keyType,
projectId: result.metadata.projectId,
name: result.metadata.name,
expiresAt: result.metadata.expiresAt,
scopes: result.metadata.scopes,
createdAt: result.metadata.createdAt,
},
message: 'IMPORTANT: Save this key securely. You will not see it again!',
});
} catch (error) {
console.error(`[${new Date().toISOString()}] Error creating API key:`, error);
res.status(500).json({
error: 'Failed to create API key',
message: 'An error occurred while creating the key',
});
}
});
/**
* List all API keys
* GET /api/admin/keys
*/
router.get('/', async (req, res) => {
try {
const keys = await apiKeyService.listKeys();
// Don't expose key hashes
const safeKeys = keys.map(key => ({
id: key.id,
type: key.keyType,
projectId: key.projectId,
name: key.name,
scopes: key.scopes,
isActive: key.isActive,
createdAt: key.createdAt,
expiresAt: key.expiresAt,
lastUsedAt: key.lastUsedAt,
createdBy: key.createdBy,
}));
res.json({
keys: safeKeys,
total: safeKeys.length,
});
} catch (error) {
console.error(`[${new Date().toISOString()}] Error listing API keys:`, error);
res.status(500).json({
error: 'Failed to list keys',
message: 'An error occurred while fetching keys',
});
}
});
/**
* Revoke an API key
* DELETE /api/admin/keys/:keyId
*/
router.delete('/:keyId', async (req, res) => {
try {
const { keyId } = req.params;
const success = await apiKeyService.revokeKey(keyId);
if (!success) {
return res.status(404).json({
error: 'Key not found',
message: 'The specified API key does not exist',
});
}
console.log(`[${new Date().toISOString()}] API key revoked: ${keyId} - by: ${req.apiKey!.id}`);
res.json({
message: 'API key revoked successfully',
keyId,
});
} catch (error) {
console.error(`[${new Date().toISOString()}] Error revoking API key:`, error);
res.status(500).json({
error: 'Failed to revoke key',
message: 'An error occurred while revoking the key',
});
}
});
export default router;

View File

@ -0,0 +1,47 @@
import express from 'express';
import { ApiKeyService } from '../services/ApiKeyService';
const router = express.Router();
const apiKeyService = new ApiKeyService();
/**
* Bootstrap endpoint - creates the first master key
* Only works when no keys exist in the database
*
* POST /api/bootstrap/initial-key
*/
router.post('/initial-key', async (req, res) => {
try {
// Check if any keys already exist
const hasKeys = await apiKeyService.hasAnyKeys();
if (hasKeys) {
console.warn(`[${new Date().toISOString()}] Bootstrap attempt when keys already exist`);
return res.status(403).json({
error: 'Bootstrap not allowed',
message: 'API keys already exist. Use /api/admin/keys to create new keys.',
});
}
// Create first master key
const { key, metadata } = await apiKeyService.createMasterKey('Initial Master Key');
console.log(`[${new Date().toISOString()}] Initial master key created via bootstrap: ${metadata.id}`);
res.status(201).json({
apiKey: key,
type: metadata.keyType,
name: metadata.name,
expiresAt: metadata.expiresAt,
message: 'IMPORTANT: Save this key securely. You will not see it again!',
});
} catch (error) {
console.error(`[${new Date().toISOString()}] Bootstrap error:`, error);
res.status(500).json({
error: 'Bootstrap failed',
message: 'Failed to create initial API key',
});
}
});
export default router;

View File

@ -14,6 +14,8 @@ import {
logEnhancementResult, logEnhancementResult,
} from "../middleware/promptEnhancement"; } from "../middleware/promptEnhancement";
import { asyncHandler } from "../middleware/errorHandler"; import { asyncHandler } from "../middleware/errorHandler";
import { validateApiKey } from "../middleware/auth/validateApiKey";
import { rateLimitByApiKey } from "../middleware/auth/rateLimiter";
import { GenerateImageResponse } from "../types/api"; import { GenerateImageResponse } from "../types/api";
// Create router // Create router
export const generateRouter: RouterType = Router(); export const generateRouter: RouterType = Router();
@ -26,6 +28,10 @@ let imageGenService: ImageGenService;
*/ */
generateRouter.post( generateRouter.post(
"/generate", "/generate",
// Authentication middleware
validateApiKey,
rateLimitByApiKey,
// File upload middleware // File upload middleware
uploadReferenceImages, uploadReferenceImages,
handleUploadErrors, handleUploadErrors,

View File

@ -0,0 +1,164 @@
import crypto from 'crypto';
import { db } from '../db';
import { apiKeys, type ApiKey, type NewApiKey } from '@banatie/database';
import { eq, and, desc } from 'drizzle-orm';
export class ApiKeyService {
/**
* Generate a new API key
* Format: bnt_{64_hex_chars}
*/
private generateKey(): { fullKey: string; keyHash: string; keyPrefix: string } {
const secret = crypto.randomBytes(32).toString('hex'); // 64 chars
const keyPrefix = 'bnt_';
const fullKey = keyPrefix + secret;
// Hash for storage (SHA-256)
const keyHash = crypto
.createHash('sha256')
.update(fullKey)
.digest('hex');
return { fullKey, keyHash, keyPrefix };
}
/**
* Create a master key (admin access, never expires)
*/
async createMasterKey(name?: string, createdBy?: string): Promise<{ key: string; metadata: ApiKey }> {
const { fullKey, keyHash, keyPrefix } = this.generateKey();
const [newKey] = await db.insert(apiKeys).values({
keyHash,
keyPrefix,
keyType: 'master',
projectId: null,
scopes: ['*'], // Full access
name: name || 'Master Key',
expiresAt: null, // Never expires
createdBy: createdBy || null,
}).returning();
console.log(`[${new Date().toISOString()}] Master key created: ${newKey?.id} - ${newKey?.name}`);
return { key: fullKey, metadata: newKey! };
}
/**
* Create a project key (expires in 90 days)
*/
async createProjectKey(
projectId: string,
name?: string,
createdBy?: string,
expiresInDays: number = 90
): Promise<{ key: string; metadata: ApiKey }> {
const { fullKey, keyHash, keyPrefix } = this.generateKey();
const expiresAt = new Date();
expiresAt.setDate(expiresAt.getDate() + expiresInDays);
const [newKey] = await db.insert(apiKeys).values({
keyHash,
keyPrefix,
keyType: 'project',
projectId,
scopes: ['generate', 'read'],
name: name || `Project Key - ${projectId}`,
expiresAt,
createdBy: createdBy || null,
}).returning();
console.log(`[${new Date().toISOString()}] Project key created: ${newKey?.id} - ${projectId} - expires: ${expiresAt.toISOString()}`);
return { key: fullKey, metadata: newKey! };
}
/**
* Validate an API key
* Returns null if invalid/expired/revoked
*/
async validateKey(providedKey: string): Promise<ApiKey | null> {
if (!providedKey || !providedKey.startsWith('bnt_')) {
return null;
}
// Hash the provided key
const keyHash = crypto
.createHash('sha256')
.update(providedKey)
.digest('hex');
// Find in database
const [key] = await db
.select()
.from(apiKeys)
.where(
and(
eq(apiKeys.keyHash, keyHash),
eq(apiKeys.isActive, true)
)
)
.limit(1);
if (!key) {
console.warn(`[${new Date().toISOString()}] Invalid API key attempt: ${providedKey.substring(0, 10)}...`);
return null;
}
// Check expiration
if (key.expiresAt && key.expiresAt < new Date()) {
console.warn(`[${new Date().toISOString()}] Expired API key used: ${key.id} - expired: ${key.expiresAt.toISOString()}`);
return null;
}
// Update last used timestamp (async, don't wait)
db.update(apiKeys)
.set({ lastUsedAt: new Date() })
.where(eq(apiKeys.id, key.id))
.execute()
.catch(err => console.error(`[${new Date().toISOString()}] Failed to update lastUsedAt:`, err));
return key;
}
/**
* Revoke a key (soft delete)
*/
async revokeKey(keyId: string): Promise<boolean> {
const result = await db
.update(apiKeys)
.set({ isActive: false })
.where(eq(apiKeys.id, keyId))
.returning();
if (result.length > 0) {
console.log(`[${new Date().toISOString()}] API key revoked: ${keyId}`);
return true;
}
return false;
}
/**
* List all keys (for admin)
*/
async listKeys(): Promise<ApiKey[]> {
return db
.select()
.from(apiKeys)
.orderBy(desc(apiKeys.createdAt));
}
/**
* Check if any keys exist (for bootstrap)
*/
async hasAnyKeys(): Promise<boolean> {
const keys = await db
.select({ id: apiKeys.id })
.from(apiKeys)
.limit(1);
return keys.length > 0;
}
}

View File

@ -2,14 +2,16 @@
services: services:
app: app:
build: build:
context: ./apps/api-service context: .
dockerfile: ./apps/api-service/Dockerfile.mono
target: development target: development
container_name: banatie-app container_name: banatie-app
ports: ports:
- "3000:3000" - "3000:3000"
volumes: volumes:
- ./apps/api-service/src:/app/src - ./apps/api-service/src:/app/apps/api-service/src
- ./apps/api-service/logs:/app/logs - ./apps/api-service/logs:/app/apps/api-service/logs
- ./packages:/app/packages
networks: networks:
- banatie-network - banatie-network
depends_on: depends_on:
@ -34,11 +36,11 @@ services:
networks: networks:
- banatie-network - banatie-network
environment: environment:
POSTGRES_DB: banatie POSTGRES_DB: banatie_db
POSTGRES_USER: banatie_user POSTGRES_USER: banatie_user
POSTGRES_PASSWORD: banatie_secure_password POSTGRES_PASSWORD: banatie_secure_password
healthcheck: healthcheck:
test: ["CMD-SHELL", "pg_isready -U banatie_user -d banatie"] test: ["CMD-SHELL", "pg_isready -U banatie_user -d banatie_db"]
interval: 30s interval: 30s
timeout: 10s timeout: 10s
retries: 3 retries: 3
@ -84,45 +86,46 @@ services:
depends_on: depends_on:
minio: minio:
condition: service_healthy condition: service_healthy
entrypoint: > entrypoint:
/bin/sh -c " - /bin/sh
echo 'Setting up MinIO alias...'; - -c
mc alias set storage http://minio:9000 ${MINIO_ROOT_USER} ${MINIO_ROOT_PASSWORD}; - |
echo 'Setting up MinIO alias...'
mc alias set storage http://minio:9000 $${MINIO_ROOT_USER} $${MINIO_ROOT_PASSWORD}
echo 'Creating main bucket...'; echo 'Creating main bucket...'
mc mb --ignore-existing storage/banatie; mc mb --ignore-existing storage/banatie
echo 'Creating service user...'; echo 'Creating service user...'
mc admin user add storage banatie_service banatie_service_key_2024; mc admin user add storage banatie_service banatie_service_key_2024
echo 'Attaching readwrite policy to service user...'; echo 'Attaching readwrite policy to service user...'
mc admin policy attach storage readwrite --user=banatie_service; mc admin policy attach storage readwrite --user=banatie_service
echo 'Setting up lifecycle policy...'; echo 'Setting up lifecycle policy...'
cat > /tmp/lifecycle.json << EOF cat > /tmp/lifecycle.json <<'LIFECYCLE'
{ {
\"Rules\": [ "Rules": [
{ {
\"ID\": \"temp-cleanup\", "ID": "temp-cleanup",
\"Status\": \"Enabled\", "Status": "Enabled",
\"Filter\": { "Filter": {
\"Prefix\": \"temp/\" "Prefix": "temp/"
}, },
\"Expiration\": { "Expiration": {
\"Days\": 7 "Days": 7
} }
} }
] ]
} }
EOF LIFECYCLE
mc ilm import storage/banatie < /tmp/lifecycle.json; mc ilm import storage/banatie < /tmp/lifecycle.json
echo 'Storage initialization completed!'; echo 'Storage initialization completed!'
echo 'Bucket: banatie'; echo 'Bucket: banatie'
echo 'Using presigned URLs for secure access'; echo 'Using presigned URLs for secure access'
echo 'SNMD mode: Full S3 compatibility enabled'; echo 'SNMD mode: Full S3 compatibility enabled'
exit 0; exit 0
"
restart: "no" restart: "no"
networks: networks:

View File

@ -0,0 +1,10 @@
import type { Config } from 'drizzle-kit';
export default {
schema: './src/schema/index.ts',
out: './migrations',
dialect: 'postgresql',
dbCredentials: {
url: process.env.DATABASE_URL || 'postgresql://banatie_user:banatie_secure_password@localhost:5434/banatie_db',
},
} satisfies Config;

View File

@ -0,0 +1,15 @@
CREATE TABLE IF NOT EXISTS "api_keys" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"key_hash" text NOT NULL,
"key_prefix" text DEFAULT 'bnt_' NOT NULL,
"key_type" text NOT NULL,
"project_id" text,
"scopes" jsonb DEFAULT '["generate"]'::jsonb NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL,
"expires_at" timestamp,
"last_used_at" timestamp,
"is_active" boolean DEFAULT true NOT NULL,
"name" text,
"created_by" uuid,
CONSTRAINT "api_keys_key_hash_unique" UNIQUE("key_hash")
);

View File

@ -0,0 +1,117 @@
{
"id": "a0f532c8-8e34-4297-a580-060eb8b49306",
"prevId": "00000000-0000-0000-0000-000000000000",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.api_keys": {
"name": "api_keys",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"key_hash": {
"name": "key_hash",
"type": "text",
"primaryKey": false,
"notNull": true
},
"key_prefix": {
"name": "key_prefix",
"type": "text",
"primaryKey": false,
"notNull": true,
"default": "'bnt_'"
},
"key_type": {
"name": "key_type",
"type": "text",
"primaryKey": false,
"notNull": true
},
"project_id": {
"name": "project_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"scopes": {
"name": "scopes",
"type": "jsonb",
"primaryKey": false,
"notNull": true,
"default": "'[\"generate\"]'::jsonb"
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"expires_at": {
"name": "expires_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false
},
"last_used_at": {
"name": "last_used_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false
},
"is_active": {
"name": "is_active",
"type": "boolean",
"primaryKey": false,
"notNull": true,
"default": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false
},
"created_by": {
"name": "created_by",
"type": "uuid",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"api_keys_key_hash_unique": {
"name": "api_keys_key_hash_unique",
"nullsNotDistinct": false,
"columns": [
"key_hash"
]
}
},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
}
},
"enums": {},
"schemas": {},
"sequences": {},
"roles": {},
"policies": {},
"views": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}

View File

@ -0,0 +1,13 @@
{
"version": "7",
"dialect": "postgresql",
"entries": [
{
"idx": 0,
"version": "7",
"when": 1759250997369,
"tag": "0000_gifted_sunfire",
"breakpoints": true
}
]
}

View File

@ -0,0 +1,21 @@
{
"name": "@banatie/database",
"version": "1.0.0",
"private": true,
"main": "./src/index.ts",
"types": "./src/index.ts",
"scripts": {
"db:generate": "drizzle-kit generate",
"db:push": "drizzle-kit push",
"db:studio": "drizzle-kit studio"
},
"dependencies": {
"drizzle-orm": "^0.36.4",
"postgres": "^3.4.3"
},
"devDependencies": {
"drizzle-kit": "^0.28.1",
"typescript": "^5.9.2",
"@types/node": "^20.0.0"
}
}

View File

@ -0,0 +1,10 @@
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
import * as schema from './schema';
export function createDbClient(connectionString: string) {
const queryClient = postgres(connectionString);
return drizzle(queryClient, { schema });
}
export type DbClient = ReturnType<typeof createDbClient>;

View File

@ -0,0 +1,3 @@
export * from './schema';
export * from './client';
export type { DbClient } from './client';

View File

@ -0,0 +1,31 @@
import { pgTable, uuid, text, timestamp, boolean, jsonb } from 'drizzle-orm/pg-core';
export const apiKeys = pgTable('api_keys', {
id: uuid('id').primaryKey().defaultRandom(),
// Key data
keyHash: text('key_hash').notNull().unique(),
keyPrefix: text('key_prefix').notNull().default('bnt_'),
// Key type: 'master' or 'project'
keyType: text('key_type').notNull().$type<'master' | 'project'>(),
// For project keys
projectId: text('project_id'),
// Permissions (for future use)
scopes: jsonb('scopes').$type<string[]>().notNull().default(['generate']),
// Lifecycle
createdAt: timestamp('created_at').notNull().defaultNow(),
expiresAt: timestamp('expires_at'), // NULL for master keys, NOW() + 90 days for project
lastUsedAt: timestamp('last_used_at'),
isActive: boolean('is_active').notNull().default(true),
// Metadata
name: text('name'), // Optional friendly name
createdBy: uuid('created_by'), // Which key created this key (for audit)
});
export type ApiKey = typeof apiKeys.$inferSelect;
export type NewApiKey = typeof apiKeys.$inferInsert;

View File

@ -0,0 +1 @@
export * from './apiKeys';

View File

@ -0,0 +1,14 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true,
"declaration": true,
"declarationMap": true,
"outDir": "./dist"
},
"include": ["src/**/*"]
}

View File

@ -66,6 +66,9 @@ importers:
apps/api-service: apps/api-service:
dependencies: dependencies:
'@banatie/database':
specifier: workspace:*
version: link:../../packages/database
'@google/genai': '@google/genai':
specifier: ^1.17.0 specifier: ^1.17.0
version: 1.20.0 version: 1.20.0
@ -256,6 +259,25 @@ importers:
specifier: ^3.4.0 specifier: ^3.4.0
version: 3.4.17 version: 3.4.17
packages/database:
dependencies:
drizzle-orm:
specifier: ^0.36.4
version: 0.36.4(@types/react@18.3.24)(postgres@3.4.7)(react@18.3.1)
postgres:
specifier: ^3.4.3
version: 3.4.7
devDependencies:
'@types/node':
specifier: ^20.0.0
version: 20.19.17
drizzle-kit:
specifier: ^0.28.1
version: 0.28.1
typescript:
specifier: ^5.9.2
version: 5.9.2
packages: packages:
'@alloc/quick-lru@5.2.0': '@alloc/quick-lru@5.2.0':
@ -438,6 +460,9 @@ packages:
'@dabh/diagnostics@2.0.3': '@dabh/diagnostics@2.0.3':
resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==}
'@drizzle-team/brocli@0.10.2':
resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==}
'@emnapi/core@1.5.0': '@emnapi/core@1.5.0':
resolution: {integrity: sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==} resolution: {integrity: sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==}
@ -447,102 +472,308 @@ packages:
'@emnapi/wasi-threads@1.1.0': '@emnapi/wasi-threads@1.1.0':
resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==}
'@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.19.12':
resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [aix]
'@esbuild/aix-ppc64@0.25.10': '@esbuild/aix-ppc64@0.25.10':
resolution: {integrity: sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==} resolution: {integrity: sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [ppc64] cpu: [ppc64]
os: [aix] os: [aix]
'@esbuild/android-arm64@0.18.20':
resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==}
engines: {node: '>=12'}
cpu: [arm64]
os: [android]
'@esbuild/android-arm64@0.19.12':
resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==}
engines: {node: '>=12'}
cpu: [arm64]
os: [android]
'@esbuild/android-arm64@0.25.10': '@esbuild/android-arm64@0.25.10':
resolution: {integrity: sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==} resolution: {integrity: sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [arm64] cpu: [arm64]
os: [android] 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.19.12':
resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==}
engines: {node: '>=12'}
cpu: [arm]
os: [android]
'@esbuild/android-arm@0.25.10': '@esbuild/android-arm@0.25.10':
resolution: {integrity: sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==} resolution: {integrity: sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [arm] cpu: [arm]
os: [android] 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.19.12':
resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==}
engines: {node: '>=12'}
cpu: [x64]
os: [android]
'@esbuild/android-x64@0.25.10': '@esbuild/android-x64@0.25.10':
resolution: {integrity: sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==} resolution: {integrity: sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [x64] cpu: [x64]
os: [android] 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.19.12':
resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==}
engines: {node: '>=12'}
cpu: [arm64]
os: [darwin]
'@esbuild/darwin-arm64@0.25.10': '@esbuild/darwin-arm64@0.25.10':
resolution: {integrity: sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==} resolution: {integrity: sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [arm64] cpu: [arm64]
os: [darwin] os: [darwin]
'@esbuild/darwin-x64@0.18.20':
resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [darwin]
'@esbuild/darwin-x64@0.19.12':
resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==}
engines: {node: '>=12'}
cpu: [x64]
os: [darwin]
'@esbuild/darwin-x64@0.25.10': '@esbuild/darwin-x64@0.25.10':
resolution: {integrity: sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==} resolution: {integrity: sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [x64] cpu: [x64]
os: [darwin] 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.19.12':
resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==}
engines: {node: '>=12'}
cpu: [arm64]
os: [freebsd]
'@esbuild/freebsd-arm64@0.25.10': '@esbuild/freebsd-arm64@0.25.10':
resolution: {integrity: sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==} resolution: {integrity: sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [arm64] cpu: [arm64]
os: [freebsd] os: [freebsd]
'@esbuild/freebsd-x64@0.18.20':
resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [freebsd]
'@esbuild/freebsd-x64@0.19.12':
resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==}
engines: {node: '>=12'}
cpu: [x64]
os: [freebsd]
'@esbuild/freebsd-x64@0.25.10': '@esbuild/freebsd-x64@0.25.10':
resolution: {integrity: sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==} resolution: {integrity: sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [x64] cpu: [x64]
os: [freebsd] 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.19.12':
resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==}
engines: {node: '>=12'}
cpu: [arm64]
os: [linux]
'@esbuild/linux-arm64@0.25.10': '@esbuild/linux-arm64@0.25.10':
resolution: {integrity: sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==} resolution: {integrity: sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [arm64] cpu: [arm64]
os: [linux] 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.19.12':
resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==}
engines: {node: '>=12'}
cpu: [arm]
os: [linux]
'@esbuild/linux-arm@0.25.10': '@esbuild/linux-arm@0.25.10':
resolution: {integrity: sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==} resolution: {integrity: sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
'@esbuild/linux-ia32@0.18.20':
resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==}
engines: {node: '>=12'}
cpu: [ia32]
os: [linux]
'@esbuild/linux-ia32@0.19.12':
resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==}
engines: {node: '>=12'}
cpu: [ia32]
os: [linux]
'@esbuild/linux-ia32@0.25.10': '@esbuild/linux-ia32@0.25.10':
resolution: {integrity: sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==} resolution: {integrity: sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [ia32] cpu: [ia32]
os: [linux] 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.19.12':
resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==}
engines: {node: '>=12'}
cpu: [loong64]
os: [linux]
'@esbuild/linux-loong64@0.25.10': '@esbuild/linux-loong64@0.25.10':
resolution: {integrity: sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==} resolution: {integrity: sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [loong64] cpu: [loong64]
os: [linux] 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.19.12':
resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==}
engines: {node: '>=12'}
cpu: [mips64el]
os: [linux]
'@esbuild/linux-mips64el@0.25.10': '@esbuild/linux-mips64el@0.25.10':
resolution: {integrity: sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==} resolution: {integrity: sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [mips64el] cpu: [mips64el]
os: [linux] os: [linux]
'@esbuild/linux-ppc64@0.18.20':
resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [linux]
'@esbuild/linux-ppc64@0.19.12':
resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [linux]
'@esbuild/linux-ppc64@0.25.10': '@esbuild/linux-ppc64@0.25.10':
resolution: {integrity: sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==} resolution: {integrity: sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [ppc64] cpu: [ppc64]
os: [linux] os: [linux]
'@esbuild/linux-riscv64@0.18.20':
resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==}
engines: {node: '>=12'}
cpu: [riscv64]
os: [linux]
'@esbuild/linux-riscv64@0.19.12':
resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==}
engines: {node: '>=12'}
cpu: [riscv64]
os: [linux]
'@esbuild/linux-riscv64@0.25.10': '@esbuild/linux-riscv64@0.25.10':
resolution: {integrity: sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==} resolution: {integrity: sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [riscv64] cpu: [riscv64]
os: [linux] os: [linux]
'@esbuild/linux-s390x@0.18.20':
resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==}
engines: {node: '>=12'}
cpu: [s390x]
os: [linux]
'@esbuild/linux-s390x@0.19.12':
resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==}
engines: {node: '>=12'}
cpu: [s390x]
os: [linux]
'@esbuild/linux-s390x@0.25.10': '@esbuild/linux-s390x@0.25.10':
resolution: {integrity: sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==} resolution: {integrity: sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [s390x] cpu: [s390x]
os: [linux] os: [linux]
'@esbuild/linux-x64@0.18.20':
resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==}
engines: {node: '>=12'}
cpu: [x64]
os: [linux]
'@esbuild/linux-x64@0.19.12':
resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==}
engines: {node: '>=12'}
cpu: [x64]
os: [linux]
'@esbuild/linux-x64@0.25.10': '@esbuild/linux-x64@0.25.10':
resolution: {integrity: sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==} resolution: {integrity: sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==}
engines: {node: '>=18'} engines: {node: '>=18'}
@ -555,6 +786,18 @@ packages:
cpu: [arm64] cpu: [arm64]
os: [netbsd] os: [netbsd]
'@esbuild/netbsd-x64@0.18.20':
resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==}
engines: {node: '>=12'}
cpu: [x64]
os: [netbsd]
'@esbuild/netbsd-x64@0.19.12':
resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==}
engines: {node: '>=12'}
cpu: [x64]
os: [netbsd]
'@esbuild/netbsd-x64@0.25.10': '@esbuild/netbsd-x64@0.25.10':
resolution: {integrity: sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==} resolution: {integrity: sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==}
engines: {node: '>=18'} engines: {node: '>=18'}
@ -567,6 +810,18 @@ packages:
cpu: [arm64] cpu: [arm64]
os: [openbsd] 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.19.12':
resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==}
engines: {node: '>=12'}
cpu: [x64]
os: [openbsd]
'@esbuild/openbsd-x64@0.25.10': '@esbuild/openbsd-x64@0.25.10':
resolution: {integrity: sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==} resolution: {integrity: sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==}
engines: {node: '>=18'} engines: {node: '>=18'}
@ -579,24 +834,72 @@ packages:
cpu: [arm64] cpu: [arm64]
os: [openharmony] 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.19.12':
resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==}
engines: {node: '>=12'}
cpu: [x64]
os: [sunos]
'@esbuild/sunos-x64@0.25.10': '@esbuild/sunos-x64@0.25.10':
resolution: {integrity: sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==} resolution: {integrity: sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [x64] cpu: [x64]
os: [sunos] os: [sunos]
'@esbuild/win32-arm64@0.18.20':
resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==}
engines: {node: '>=12'}
cpu: [arm64]
os: [win32]
'@esbuild/win32-arm64@0.19.12':
resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==}
engines: {node: '>=12'}
cpu: [arm64]
os: [win32]
'@esbuild/win32-arm64@0.25.10': '@esbuild/win32-arm64@0.25.10':
resolution: {integrity: sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==} resolution: {integrity: sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [arm64] cpu: [arm64]
os: [win32] os: [win32]
'@esbuild/win32-ia32@0.18.20':
resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==}
engines: {node: '>=12'}
cpu: [ia32]
os: [win32]
'@esbuild/win32-ia32@0.19.12':
resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==}
engines: {node: '>=12'}
cpu: [ia32]
os: [win32]
'@esbuild/win32-ia32@0.25.10': '@esbuild/win32-ia32@0.25.10':
resolution: {integrity: sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==} resolution: {integrity: sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==}
engines: {node: '>=18'} engines: {node: '>=18'}
cpu: [ia32] cpu: [ia32]
os: [win32] 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.19.12':
resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==}
engines: {node: '>=12'}
cpu: [x64]
os: [win32]
'@esbuild/win32-x64@0.25.10': '@esbuild/win32-x64@0.25.10':
resolution: {integrity: sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==} resolution: {integrity: sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==}
engines: {node: '>=18'} engines: {node: '>=18'}
@ -1850,6 +2153,102 @@ packages:
resolution: {integrity: sha512-Sf2LSQP+bOlhKWWyhFsn0UsfdK/kCWRv1iuA2gXAwt3dyNabr6QSj00I2V10pidqz69soatm9ZwZvpQMTIOd5Q==} resolution: {integrity: sha512-Sf2LSQP+bOlhKWWyhFsn0UsfdK/kCWRv1iuA2gXAwt3dyNabr6QSj00I2V10pidqz69soatm9ZwZvpQMTIOd5Q==}
engines: {node: '>=12'} engines: {node: '>=12'}
drizzle-kit@0.28.1:
resolution: {integrity: sha512-JimOV+ystXTWMgZkLHYHf2w3oS28hxiH1FR0dkmJLc7GHzdGJoJAQtQS5DRppnabsRZwE2U1F6CuezVBgmsBBQ==}
hasBin: true
drizzle-orm@0.36.4:
resolution: {integrity: sha512-1OZY3PXD7BR00Gl61UUOFihslDldfH4NFRH2MbP54Yxi0G/PKn4HfO65JYZ7c16DeP3SpM3Aw+VXVG9j6CRSXA==}
peerDependencies:
'@aws-sdk/client-rds-data': '>=3'
'@cloudflare/workers-types': '>=3'
'@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'
'@prisma/client': '*'
'@tidbcloud/serverless': '*'
'@types/better-sqlite3': '*'
'@types/pg': '*'
'@types/react': '>=18'
'@types/sql.js': '*'
'@vercel/postgres': '>=0.8.0'
'@xata.io/client': '*'
better-sqlite3: '>=7'
bun-types: '*'
expo-sqlite: '>=14.0.0'
knex: '*'
kysely: '*'
mysql2: '>=2'
pg: '>=8'
postgres: '>=3'
prisma: '*'
react: '>=18'
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/react':
optional: true
'@types/sql.js':
optional: true
'@vercel/postgres':
optional: true
'@xata.io/client':
optional: true
better-sqlite3:
optional: true
bun-types:
optional: true
expo-sqlite:
optional: true
knex:
optional: true
kysely:
optional: true
mysql2:
optional: true
pg:
optional: true
postgres:
optional: true
prisma:
optional: true
react:
optional: true
sql.js:
optional: true
sqlite3:
optional: true
dunder-proto@1.0.1: dunder-proto@1.0.1:
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@ -1918,6 +2317,21 @@ packages:
resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
esbuild-register@3.6.0:
resolution: {integrity: sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==}
peerDependencies:
esbuild: '>=0.12 <1'
esbuild@0.18.20:
resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==}
engines: {node: '>=12'}
hasBin: true
esbuild@0.19.12:
resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==}
engines: {node: '>=12'}
hasBin: true
esbuild@0.25.10: esbuild@0.25.10:
resolution: {integrity: sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==} resolution: {integrity: sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
@ -3267,6 +3681,10 @@ packages:
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
engines: {node: ^10 || ^12 || >=14} engines: {node: ^10 || ^12 || >=14}
postgres@3.4.7:
resolution: {integrity: sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==}
engines: {node: '>=12'}
prelude-ls@1.2.1: prelude-ls@1.2.1:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
engines: {node: '>= 0.8.0'} engines: {node: '>= 0.8.0'}
@ -3543,6 +3961,9 @@ packages:
source-map-support@0.5.13: source-map-support@0.5.13:
resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==}
source-map-support@0.5.21:
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
source-map@0.6.1: source-map@0.6.1:
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@ -4239,6 +4660,8 @@ snapshots:
enabled: 2.0.0 enabled: 2.0.0
kuler: 2.0.0 kuler: 2.0.0
'@drizzle-team/brocli@0.10.2': {}
'@emnapi/core@1.5.0': '@emnapi/core@1.5.0':
dependencies: dependencies:
'@emnapi/wasi-threads': 1.1.0 '@emnapi/wasi-threads': 1.1.0
@ -4255,81 +4678,226 @@ snapshots:
tslib: 2.8.1 tslib: 2.8.1
optional: true optional: true
'@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.10.1
'@esbuild/aix-ppc64@0.19.12':
optional: true
'@esbuild/aix-ppc64@0.25.10': '@esbuild/aix-ppc64@0.25.10':
optional: true optional: true
'@esbuild/android-arm64@0.18.20':
optional: true
'@esbuild/android-arm64@0.19.12':
optional: true
'@esbuild/android-arm64@0.25.10': '@esbuild/android-arm64@0.25.10':
optional: true optional: true
'@esbuild/android-arm@0.18.20':
optional: true
'@esbuild/android-arm@0.19.12':
optional: true
'@esbuild/android-arm@0.25.10': '@esbuild/android-arm@0.25.10':
optional: true optional: true
'@esbuild/android-x64@0.18.20':
optional: true
'@esbuild/android-x64@0.19.12':
optional: true
'@esbuild/android-x64@0.25.10': '@esbuild/android-x64@0.25.10':
optional: true optional: true
'@esbuild/darwin-arm64@0.18.20':
optional: true
'@esbuild/darwin-arm64@0.19.12':
optional: true
'@esbuild/darwin-arm64@0.25.10': '@esbuild/darwin-arm64@0.25.10':
optional: true optional: true
'@esbuild/darwin-x64@0.18.20':
optional: true
'@esbuild/darwin-x64@0.19.12':
optional: true
'@esbuild/darwin-x64@0.25.10': '@esbuild/darwin-x64@0.25.10':
optional: true optional: true
'@esbuild/freebsd-arm64@0.18.20':
optional: true
'@esbuild/freebsd-arm64@0.19.12':
optional: true
'@esbuild/freebsd-arm64@0.25.10': '@esbuild/freebsd-arm64@0.25.10':
optional: true optional: true
'@esbuild/freebsd-x64@0.18.20':
optional: true
'@esbuild/freebsd-x64@0.19.12':
optional: true
'@esbuild/freebsd-x64@0.25.10': '@esbuild/freebsd-x64@0.25.10':
optional: true optional: true
'@esbuild/linux-arm64@0.18.20':
optional: true
'@esbuild/linux-arm64@0.19.12':
optional: true
'@esbuild/linux-arm64@0.25.10': '@esbuild/linux-arm64@0.25.10':
optional: true optional: true
'@esbuild/linux-arm@0.18.20':
optional: true
'@esbuild/linux-arm@0.19.12':
optional: true
'@esbuild/linux-arm@0.25.10': '@esbuild/linux-arm@0.25.10':
optional: true optional: true
'@esbuild/linux-ia32@0.18.20':
optional: true
'@esbuild/linux-ia32@0.19.12':
optional: true
'@esbuild/linux-ia32@0.25.10': '@esbuild/linux-ia32@0.25.10':
optional: true optional: true
'@esbuild/linux-loong64@0.18.20':
optional: true
'@esbuild/linux-loong64@0.19.12':
optional: true
'@esbuild/linux-loong64@0.25.10': '@esbuild/linux-loong64@0.25.10':
optional: true optional: true
'@esbuild/linux-mips64el@0.18.20':
optional: true
'@esbuild/linux-mips64el@0.19.12':
optional: true
'@esbuild/linux-mips64el@0.25.10': '@esbuild/linux-mips64el@0.25.10':
optional: true optional: true
'@esbuild/linux-ppc64@0.18.20':
optional: true
'@esbuild/linux-ppc64@0.19.12':
optional: true
'@esbuild/linux-ppc64@0.25.10': '@esbuild/linux-ppc64@0.25.10':
optional: true optional: true
'@esbuild/linux-riscv64@0.18.20':
optional: true
'@esbuild/linux-riscv64@0.19.12':
optional: true
'@esbuild/linux-riscv64@0.25.10': '@esbuild/linux-riscv64@0.25.10':
optional: true optional: true
'@esbuild/linux-s390x@0.18.20':
optional: true
'@esbuild/linux-s390x@0.19.12':
optional: true
'@esbuild/linux-s390x@0.25.10': '@esbuild/linux-s390x@0.25.10':
optional: true optional: true
'@esbuild/linux-x64@0.18.20':
optional: true
'@esbuild/linux-x64@0.19.12':
optional: true
'@esbuild/linux-x64@0.25.10': '@esbuild/linux-x64@0.25.10':
optional: true optional: true
'@esbuild/netbsd-arm64@0.25.10': '@esbuild/netbsd-arm64@0.25.10':
optional: true optional: true
'@esbuild/netbsd-x64@0.18.20':
optional: true
'@esbuild/netbsd-x64@0.19.12':
optional: true
'@esbuild/netbsd-x64@0.25.10': '@esbuild/netbsd-x64@0.25.10':
optional: true optional: true
'@esbuild/openbsd-arm64@0.25.10': '@esbuild/openbsd-arm64@0.25.10':
optional: true optional: true
'@esbuild/openbsd-x64@0.18.20':
optional: true
'@esbuild/openbsd-x64@0.19.12':
optional: true
'@esbuild/openbsd-x64@0.25.10': '@esbuild/openbsd-x64@0.25.10':
optional: true optional: true
'@esbuild/openharmony-arm64@0.25.10': '@esbuild/openharmony-arm64@0.25.10':
optional: true optional: true
'@esbuild/sunos-x64@0.18.20':
optional: true
'@esbuild/sunos-x64@0.19.12':
optional: true
'@esbuild/sunos-x64@0.25.10': '@esbuild/sunos-x64@0.25.10':
optional: true optional: true
'@esbuild/win32-arm64@0.18.20':
optional: true
'@esbuild/win32-arm64@0.19.12':
optional: true
'@esbuild/win32-arm64@0.25.10': '@esbuild/win32-arm64@0.25.10':
optional: true optional: true
'@esbuild/win32-ia32@0.18.20':
optional: true
'@esbuild/win32-ia32@0.19.12':
optional: true
'@esbuild/win32-ia32@0.25.10': '@esbuild/win32-ia32@0.25.10':
optional: true optional: true
'@esbuild/win32-x64@0.18.20':
optional: true
'@esbuild/win32-x64@0.19.12':
optional: true
'@esbuild/win32-x64@0.25.10': '@esbuild/win32-x64@0.25.10':
optional: true optional: true
@ -5043,7 +5611,7 @@ snapshots:
'@types/ws@8.18.1': '@types/ws@8.18.1':
dependencies: dependencies:
'@types/node': 20.19.17 '@types/node': 24.5.2
'@types/yargs-parser@21.0.3': {} '@types/yargs-parser@21.0.3': {}
@ -5810,6 +6378,21 @@ snapshots:
dotenv@17.2.2: {} dotenv@17.2.2: {}
drizzle-kit@0.28.1:
dependencies:
'@drizzle-team/brocli': 0.10.2
'@esbuild-kit/esm-loader': 2.6.5
esbuild: 0.19.12
esbuild-register: 3.6.0(esbuild@0.19.12)
transitivePeerDependencies:
- supports-color
drizzle-orm@0.36.4(@types/react@18.3.24)(postgres@3.4.7)(react@18.3.1):
optionalDependencies:
'@types/react': 18.3.24
postgres: 3.4.7
react: 18.3.1
dunder-proto@1.0.1: dunder-proto@1.0.1:
dependencies: dependencies:
call-bind-apply-helpers: 1.0.2 call-bind-apply-helpers: 1.0.2
@ -5941,6 +6524,64 @@ snapshots:
is-date-object: 1.1.0 is-date-object: 1.1.0
is-symbol: 1.1.1 is-symbol: 1.1.1
esbuild-register@3.6.0(esbuild@0.19.12):
dependencies:
debug: 4.4.3(supports-color@5.5.0)
esbuild: 0.19.12
transitivePeerDependencies:
- supports-color
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.19.12:
optionalDependencies:
'@esbuild/aix-ppc64': 0.19.12
'@esbuild/android-arm': 0.19.12
'@esbuild/android-arm64': 0.19.12
'@esbuild/android-x64': 0.19.12
'@esbuild/darwin-arm64': 0.19.12
'@esbuild/darwin-x64': 0.19.12
'@esbuild/freebsd-arm64': 0.19.12
'@esbuild/freebsd-x64': 0.19.12
'@esbuild/linux-arm': 0.19.12
'@esbuild/linux-arm64': 0.19.12
'@esbuild/linux-ia32': 0.19.12
'@esbuild/linux-loong64': 0.19.12
'@esbuild/linux-mips64el': 0.19.12
'@esbuild/linux-ppc64': 0.19.12
'@esbuild/linux-riscv64': 0.19.12
'@esbuild/linux-s390x': 0.19.12
'@esbuild/linux-x64': 0.19.12
'@esbuild/netbsd-x64': 0.19.12
'@esbuild/openbsd-x64': 0.19.12
'@esbuild/sunos-x64': 0.19.12
'@esbuild/win32-arm64': 0.19.12
'@esbuild/win32-ia32': 0.19.12
'@esbuild/win32-x64': 0.19.12
esbuild@0.25.10: esbuild@0.25.10:
optionalDependencies: optionalDependencies:
'@esbuild/aix-ppc64': 0.25.10 '@esbuild/aix-ppc64': 0.25.10
@ -5987,7 +6628,7 @@ snapshots:
eslint: 8.57.1 eslint: 8.57.1
eslint-import-resolver-node: 0.3.9 eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.44.0(eslint@8.57.1)(typescript@5.9.2))(eslint@8.57.1))(eslint@8.57.1) eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.44.0(eslint@8.57.1)(typescript@5.9.2))(eslint@8.57.1))(eslint@8.57.1)
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.44.0(eslint@8.57.1)(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.44.0(eslint@8.57.1)(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.44.0(eslint@8.57.1)(typescript@5.9.2))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)
eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1) eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1)
eslint-plugin-react: 7.37.5(eslint@8.57.1) eslint-plugin-react: 7.37.5(eslint@8.57.1)
eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@8.57.1) eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@8.57.1)
@ -6021,7 +6662,7 @@ snapshots:
tinyglobby: 0.2.15 tinyglobby: 0.2.15
unrs-resolver: 1.11.1 unrs-resolver: 1.11.1
optionalDependencies: optionalDependencies:
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.44.0(eslint@8.57.1)(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.44.0(eslint@8.57.1)(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.44.0(eslint@8.57.1)(typescript@5.9.2))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@ -6036,7 +6677,7 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.44.0(eslint@8.57.1)(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1): eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.44.0(eslint@8.57.1)(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.44.0(eslint@8.57.1)(typescript@5.9.2))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1):
dependencies: dependencies:
'@rtsao/scc': 1.1.0 '@rtsao/scc': 1.1.0
array-includes: 3.1.9 array-includes: 3.1.9
@ -7652,6 +8293,8 @@ snapshots:
picocolors: 1.1.1 picocolors: 1.1.1
source-map-js: 1.2.1 source-map-js: 1.2.1
postgres@3.4.7: {}
prelude-ls@1.2.1: {} prelude-ls@1.2.1: {}
prettier-linter-helpers@1.0.0: prettier-linter-helpers@1.0.0:
@ -7981,6 +8624,11 @@ snapshots:
buffer-from: 1.1.2 buffer-from: 1.1.2
source-map: 0.6.1 source-map: 0.6.1
source-map-support@0.5.21:
dependencies:
buffer-from: 1.1.2
source-map: 0.6.1
source-map@0.6.1: {} source-map@0.6.1: {}
split-on-first@1.1.0: {} split-on-first@1.1.0: {}

View File

@ -1,2 +1,3 @@
packages: packages:
- 'apps/*' - 'apps/*'
- 'packages/*'