├── .npmrc ├── up.sh ├── .gitignore ├── src ├── lib │ ├── logger.ts │ ├── constants.ts │ ├── env.ts │ ├── queue.ts │ ├── jwt.ts │ └── encryption.ts ├── db │ ├── schema │ │ ├── migration │ │ │ ├── meta │ │ │ │ ├── _journal.json │ │ │ │ └── 0000_snapshot.json │ │ │ └── 0000_awesome_scarecrow.sql │ │ └── schema.ts │ └── database.ts ├── web │ ├── middlelayer │ │ └── tracing.ts │ ├── controller │ │ ├── serializer │ │ │ └── user.ts │ │ ├── resp │ │ │ ├── resp.ts │ │ │ └── error.ts │ │ └── auth.ts │ ├── validator │ │ ├── validator.ts │ │ └── user.ts │ └── server.ts ├── task │ ├── sendWelcomeEmail.ts │ ├── client │ │ └── sendWelcomeEmailAsync.ts │ └── tasker.ts ├── util │ └── string.ts ├── repository │ └── user.ts ├── service │ └── user.ts └── index.ts ├── .env.template ├── tsconfig.json ├── drizzle.config.ts ├── Dockerfile ├── package.json ├── LICENSE ├── biome.json ├── docker-compose.yml ├── README.md ├── static └── openapi.yaml └── pnpm-lock.yaml /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /up.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | pnpm run build 4 | pnpm run dev 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .vscode 3 | .DS_Store 4 | 5 | node_modules 6 | dist 7 | 8 | .env 9 | /static/openapi.json 10 | -------------------------------------------------------------------------------- /src/lib/logger.ts: -------------------------------------------------------------------------------- 1 | import pino from 'pino'; 2 | import env from './env.js'; 3 | 4 | const logger = pino.pino({ 5 | level: env.LOG_LEVEL || 'info', 6 | }); 7 | export { logger }; 8 | -------------------------------------------------------------------------------- /src/lib/constants.ts: -------------------------------------------------------------------------------- 1 | const NODE_ENVIRONMENTS = { 2 | development: 'development', 3 | production: 'production', 4 | }; 5 | 6 | const TRACING = 'tracing'; 7 | 8 | export { NODE_ENVIRONMENTS, TRACING }; 9 | -------------------------------------------------------------------------------- /.env.template: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | LOG_LEVEL=debug 3 | NODE_ENV=development 4 | SECRET_KEY=random 5 | 6 | DB_HOST=localhost 7 | DB_USER=user 8 | DB_PASSWORD=password 9 | DB_NAME=db 10 | 11 | REDIS_HOST=localhost 12 | REDIS_PORT=6379 13 | -------------------------------------------------------------------------------- /src/db/schema/migration/meta/_journal.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "5", 3 | "dialect": "mysql", 4 | "entries": [ 5 | { 6 | "idx": 0, 7 | "version": "5", 8 | "when": 1715121268101, 9 | "tag": "0000_awesome_scarecrow", 10 | "breakpoints": true 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /src/web/middlelayer/tracing.ts: -------------------------------------------------------------------------------- 1 | import { createMiddleware } from 'hono/factory'; 2 | import { TRACING } from '../../lib/constants.js'; 3 | import { randomString } from '../../util/string.js'; 4 | 5 | export const tracing = createMiddleware(async (c, next) => { 6 | c.set(TRACING, randomString(10)); 7 | await next(); 8 | }); 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "nodenext", 5 | "moduleResolution": "nodenext", 6 | "strict": true, 7 | "skipLibCheck": true, 8 | "types": ["node"], 9 | "jsx": "react-jsx", 10 | "jsxImportSource": "hono/jsx", 11 | "outDir": "./dist" 12 | }, 13 | "exclude": ["node_modules"] 14 | } 15 | -------------------------------------------------------------------------------- /src/task/sendWelcomeEmail.ts: -------------------------------------------------------------------------------- 1 | import { logger } from '../lib/logger.js'; 2 | import type { UserService } from '../service/user.js'; 3 | 4 | const sendWelcomeEmail = async (data: any, userService: UserService) => { 5 | const user = await userService.find(data.userId); 6 | logger.info(`Welcome email sent to ${user?.email}`); 7 | }; 8 | 9 | export default sendWelcomeEmail; 10 | -------------------------------------------------------------------------------- /src/db/schema/migration/0000_awesome_scarecrow.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE `user` ( 2 | `id` serial AUTO_INCREMENT NOT NULL, 3 | `name` varchar(50) NOT NULL, 4 | `email` varchar(100) NOT NULL, 5 | `password` varchar(65) NOT NULL, 6 | `reset_token` varchar(100), 7 | `created_at` timestamp DEFAULT (now()), 8 | `updated_at` timestamp DEFAULT (now()), 9 | CONSTRAINT `user_id` PRIMARY KEY(`id`), 10 | CONSTRAINT `user_email_unique` UNIQUE(`email`) 11 | ); 12 | -------------------------------------------------------------------------------- /src/web/controller/serializer/user.ts: -------------------------------------------------------------------------------- 1 | import type { User } from '../../../db/database.js'; 2 | 3 | type UserResponse = { 4 | id: number; 5 | name: string; 6 | email: string; 7 | createdAt: Date | null; 8 | }; 9 | 10 | const serializeUser = (user: User): UserResponse => { 11 | return { 12 | id: user.id, 13 | name: user.name, 14 | email: user.email, 15 | createdAt: user.createdAt, 16 | }; 17 | }; 18 | 19 | export { serializeUser }; 20 | -------------------------------------------------------------------------------- /src/web/controller/resp/resp.ts: -------------------------------------------------------------------------------- 1 | import type { Context } from 'hono'; 2 | import type { ContentfulStatusCode } from 'hono/utils/http-status'; 3 | import type { StatusCodes } from 'http-status-codes'; 4 | 5 | const serveData = (c: Context, data: any) => { 6 | return c.json({ data }); 7 | }; 8 | 9 | const serve = (c: Context, status: StatusCodes, data: any) => { 10 | return c.json({ data }, status); 11 | }; 12 | 13 | export { serve, serveData }; 14 | -------------------------------------------------------------------------------- /drizzle.config.ts: -------------------------------------------------------------------------------- 1 | import 'dotenv/config'; 2 | import { defineConfig } from 'drizzle-kit'; 3 | import env from './src/lib/env.js'; 4 | 5 | export default defineConfig({ 6 | schema: './src/db/schema/schema.ts', 7 | out: './src/db/schema/migration', 8 | dialect: 'mysql', 9 | dbCredentials: { 10 | host: env.DB_HOST, 11 | user: env.DB_USER, 12 | password: env.DB_PASSWORD, 13 | database: env.DB_NAME, 14 | }, 15 | verbose: true, 16 | strict: true, 17 | }); 18 | -------------------------------------------------------------------------------- /src/task/client/sendWelcomeEmailAsync.ts: -------------------------------------------------------------------------------- 1 | import { logger } from '../../lib/logger.js'; 2 | import { defaultQueue } from '../../lib/queue.js'; 3 | import { TASK } from '../tasker.js'; 4 | 5 | const sendWelcomeEmailAsync = async (userId: number) => { 6 | const job = await defaultQueue.add(TASK.SendWelcomeEmail, { userId }); 7 | logger.info(`Job ${job.id} added to queue. Task scheduled for ${TASK.SendWelcomeEmail}, user: ${userId}`); 8 | }; 9 | 10 | export default sendWelcomeEmailAsync; 11 | -------------------------------------------------------------------------------- /src/db/schema/schema.ts: -------------------------------------------------------------------------------- 1 | import { mysqlTable, serial, timestamp, varchar } from 'drizzle-orm/mysql-core'; 2 | 3 | export const userSchema = mysqlTable('user', { 4 | id: serial('id').primaryKey(), 5 | name: varchar('name', { length: 50 }).notNull(), 6 | email: varchar('email', { length: 100 }).notNull().unique(), 7 | password: varchar('password', { length: 65 }).notNull(), 8 | reset_token: varchar('reset_token', { length: 100 }), 9 | createdAt: timestamp('created_at').defaultNow(), 10 | updatedAt: timestamp('updated_at').defaultNow(), 11 | }); 12 | -------------------------------------------------------------------------------- /src/lib/env.ts: -------------------------------------------------------------------------------- 1 | import 'dotenv/config'; 2 | 3 | import { z } from 'zod'; 4 | 5 | const envSchema = z.object({ 6 | PORT: z.string().default('3000'), 7 | LOG_LEVEL: z.string().default('info'), 8 | NODE_ENV: z.enum(['development', 'production']).default('development'), 9 | SECRET_KEY: z.string(), 10 | DB_HOST: z.string().default('localhost'), 11 | DB_USER: z.string(), 12 | DB_PASSWORD: z.string(), 13 | DB_NAME: z.string(), 14 | REDIS_HOST: z.string().default('localhost'), 15 | REDIS_PORT: z.string().default('6379'), 16 | }); 17 | 18 | export default envSchema.parse(process.env); 19 | -------------------------------------------------------------------------------- /src/util/string.ts: -------------------------------------------------------------------------------- 1 | const randomString = (len: number) => { 2 | let text = ''; 3 | const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; 4 | const digits = '0123456789'; 5 | 6 | // Generate the first character from the letters 7 | text += letters.charAt(Math.floor(Math.random() * letters.length)); 8 | const possible = letters + digits; 9 | 10 | // Generate the remaining characters from the full set 11 | for (let i = 1; i < len; i++) { 12 | text += possible.charAt(Math.floor(Math.random() * possible.length)); 13 | } 14 | 15 | return text; 16 | }; 17 | 18 | export { randomString }; 19 | -------------------------------------------------------------------------------- /src/repository/user.ts: -------------------------------------------------------------------------------- 1 | import { eq } from 'drizzle-orm'; 2 | import { userSchema } from '../db/schema/schema.js'; 3 | import { db, type NewUser } from '../db/database.js'; 4 | 5 | export class UserRepository { 6 | public async create(user: NewUser) { 7 | return db.insert(userSchema).values(user); 8 | } 9 | 10 | public async find(id: number) { 11 | return db.query.userSchema.findFirst({ 12 | where: eq(userSchema.id, id), 13 | }); 14 | } 15 | 16 | public async findByEmail(email: string) { 17 | return db.query.userSchema.findFirst({ 18 | where: eq(userSchema.email, email), 19 | }); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/web/validator/validator.ts: -------------------------------------------------------------------------------- 1 | import type { Context } from 'hono'; 2 | import type { ZodError, ZodObject } from 'zod'; 3 | import { serveUnprocessableEntity } from '../controller/resp/error.js'; 4 | 5 | const getErrorPhrase = (error: ZodError) => { 6 | const path = error.issues[0].path[0]; 7 | const message = error.issues[0].message; 8 | return `${path}: ${message}`; 9 | }; 10 | 11 | const validateSchema = (c: Context, schema: ZodObject, value: any) => { 12 | const parsed = schema.safeParse(value); 13 | if (!parsed.success) { 14 | return serveUnprocessableEntity(c, getErrorPhrase(parsed.error)); 15 | } 16 | return parsed.data; 17 | }; 18 | 19 | export { getErrorPhrase, validateSchema }; 20 | -------------------------------------------------------------------------------- /src/lib/queue.ts: -------------------------------------------------------------------------------- 1 | import { Queue } from 'bullmq'; 2 | import IORedis from 'ioredis'; 3 | import env from './env.js'; 4 | 5 | const QUEUE = { 6 | default: 'default', 7 | }; 8 | 9 | const connection = new IORedis.default({ 10 | port: Number.parseInt(env.REDIS_PORT), 11 | host: env.REDIS_HOST, 12 | maxRetriesPerRequest: null, 13 | }); 14 | // Reuse the ioredis instance 15 | const defaultQueue = new Queue(QUEUE.default, { 16 | connection, 17 | defaultJobOptions: { 18 | removeOnComplete: { 19 | count: 1000, // keep up to 1000 jobs 20 | age: 24 * 3600, // keep up to 24 hours 21 | }, 22 | removeOnFail: { 23 | age: 24 * 3600, // keep up to 24 hours 24 | }, 25 | }, 26 | }); 27 | 28 | export { connection, defaultQueue, QUEUE }; 29 | -------------------------------------------------------------------------------- /src/service/user.ts: -------------------------------------------------------------------------------- 1 | import { encrypt } from '../lib/encryption.js'; 2 | import type { UserRepository } from '../repository/user.js'; 3 | 4 | export class UserService { 5 | private repo: UserRepository; 6 | 7 | constructor(userRepository: UserRepository) { 8 | this.repo = userRepository; 9 | 10 | this.create = this.create.bind(this); 11 | this.findByEmail = this.findByEmail.bind(this); 12 | } 13 | 14 | public async create(name: string, email: string, password: string) { 15 | const hashedPassword = encrypt(password); 16 | await this.repo.create({ name, email, password: hashedPassword }); 17 | } 18 | 19 | public async findByEmail(email: string) { 20 | return this.repo.findByEmail(email); 21 | } 22 | 23 | public async find(id: number) { 24 | return this.repo.find(id); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:22-alpine AS base 2 | 3 | FROM base AS builder 4 | 5 | RUN apk add --no-cache gcompat 6 | WORKDIR /app 7 | 8 | # Install pnpm 9 | RUN corepack enable && corepack prepare pnpm@latest --activate 10 | 11 | COPY package.json pnpm-lock.yaml tsconfig.json src ./ 12 | 13 | RUN pnpm install --frozen-lockfile && \ 14 | pnpm run build && \ 15 | pnpm prune --prod 16 | 17 | FROM base AS runner 18 | WORKDIR /app 19 | 20 | RUN addgroup --system --gid 1001 nodejs 21 | RUN adduser --system --uid 1001 hono 22 | 23 | COPY static/ /app/static/ 24 | COPY --from=builder --chown=hono:nodejs /app/node_modules /app/node_modules 25 | COPY --from=builder --chown=hono:nodejs /app/dist /app/dist 26 | COPY --from=builder --chown=hono:nodejs /app/package.json /app/package.json 27 | 28 | USER hono 29 | EXPOSE 3000 30 | 31 | CMD ["node", "/app/dist/index.js"] -------------------------------------------------------------------------------- /src/web/validator/user.ts: -------------------------------------------------------------------------------- 1 | import { validator } from 'hono/validator'; 2 | import { z } from 'zod'; 3 | import { validateSchema } from './validator.js'; 4 | 5 | const loginSchema = z.object({ 6 | email: z.string().email(), 7 | password: z.string().min(8).max(20), 8 | }); 9 | 10 | const loginValidator = validator('json', (value, c) => { 11 | return validateSchema(c, loginSchema, value); 12 | }); 13 | 14 | const registrationSchema = loginSchema.extend({ 15 | name: z.string().min(4).max(40), 16 | }); 17 | 18 | const registrationValidator = validator('json', (value, c) => { 19 | return validateSchema(c, registrationSchema, value); 20 | }); 21 | 22 | type LoginBody = z.infer; 23 | type RegistrationBody = z.infer; 24 | 25 | export { type LoginBody, type RegistrationBody, loginValidator, registrationValidator }; 26 | -------------------------------------------------------------------------------- /src/lib/jwt.ts: -------------------------------------------------------------------------------- 1 | import { sign, verify } from 'hono/jwt'; 2 | import env from './env.js'; 3 | 4 | type JWTPayload = { 5 | [key: string]: unknown; 6 | exp?: number; 7 | }; 8 | 9 | /** 10 | * Encodes the given id and email into a JWT token with an expiration time of 30 days. 11 | * 12 | * @param {number} id - The id to be included in the token payload. 13 | * @param {string} email - The email to be included in the token payload. 14 | * @return {Promise} A promise that resolves to the encoded JWT token. 15 | */ 16 | const encode = async (id: number, email: string): Promise => { 17 | const payload: JWTPayload = { 18 | sub: id, 19 | email: email, 20 | exp: Math.floor(Date.now() / 1000) + 60 * 60 * 24 * 30, // Token expires in 30 days 21 | }; 22 | return await sign(payload, env.SECRET_KEY); 23 | }; 24 | 25 | const check = async (token: string): Promise => { 26 | return await verify(token, env.SECRET_KEY); 27 | }; 28 | 29 | export { type JWTPayload, encode }; 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "tsx watch src/index.ts | pino-pretty", 4 | "db:generate": "drizzle-kit generate", 5 | "db:migrate": "drizzle-kit migrate", 6 | "db:drop": "drizzle-kit drop", 7 | "build": "tsc" 8 | }, 9 | "type": "module", 10 | "dependencies": { 11 | "@hono/node-server": "^1.13.7", 12 | "@hono/swagger-ui": "^0.5.0", 13 | "@hono/zod-validator": "^0.4.2", 14 | "bullmq": "^5.36.0", 15 | "dotenv": "^16.4.7", 16 | "drizzle-orm": "^0.38.4", 17 | "hono": "^4.6.18", 18 | "http-status-codes": "^2.3.0", 19 | "ioredis": "^5.4.2", 20 | "mysql2": "^3.12.0", 21 | "pino": "^9.6.0", 22 | "zod": "^3.24.1" 23 | }, 24 | "devDependencies": { 25 | "@biomejs/biome": "1.9.4", 26 | "@types/node": "^22.10.10", 27 | "drizzle-kit": "^0.30.2", 28 | "prettier": "3.4.2", 29 | "tsx": "^4.19.2", 30 | "typescript": "^5.7.3" 31 | }, 32 | "engines": { 33 | "node": ">=20.0.0 <23.0.0" 34 | }, 35 | "packageManager": "pnpm@9.11.0" 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Joker666 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/db/database.ts: -------------------------------------------------------------------------------- 1 | import type { Logger as drizzleLogger } from 'drizzle-orm/logger'; 2 | import { drizzle } from 'drizzle-orm/mysql2'; 3 | import mysql from 'mysql2/promise'; 4 | import * as schema from './schema/schema.js'; 5 | import type { userSchema } from './schema/schema.js'; 6 | import env from '../lib/env.js'; 7 | import { logger } from '../lib/logger.js'; 8 | 9 | const DB_ERRORS = { 10 | DUPLICATE_KEY: 'ER_DUP_ENTRY', 11 | }; 12 | 13 | export interface DatabaseError { 14 | type: string; 15 | message: string; 16 | stack?: string; 17 | code: string; 18 | errno: number; 19 | sql: string; 20 | sqlState: string; 21 | sqlMessage: string; 22 | } 23 | 24 | export type User = typeof userSchema.$inferSelect; 25 | export type NewUser = typeof userSchema.$inferInsert; 26 | 27 | class DBLogger implements drizzleLogger { 28 | logQuery(query: string, params: unknown[]): void { 29 | logger.debug({ query, params }); 30 | } 31 | } 32 | 33 | const connection = await mysql.createConnection({ 34 | host: env.DB_HOST, 35 | user: env.DB_USER, 36 | password: env.DB_PASSWORD, 37 | database: env.DB_NAME, 38 | }); 39 | 40 | const db = drizzle(connection, { schema: schema, mode: 'default', logger: new DBLogger() }); 41 | export { DB_ERRORS, connection, db }; 42 | -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", 3 | "vcs": { "enabled": false, "clientKind": "git", "useIgnoreFile": false }, 4 | "files": { "ignoreUnknown": false, "ignore": [] }, 5 | "formatter": { 6 | "enabled": true, 7 | "useEditorconfig": true, 8 | "formatWithErrors": false, 9 | "indentStyle": "space", 10 | "indentWidth": 2, 11 | "lineEnding": "lf", 12 | "lineWidth": 120, 13 | "attributePosition": "auto", 14 | "bracketSpacing": true, 15 | "ignore": [ 16 | "**/coverage", 17 | "**/node_modules", 18 | "**/.idea", 19 | "**/.next", 20 | "**/pnpm-lock.yaml", 21 | "schema/migration", 22 | "**/docker-compose.yml", 23 | "**/README.md" 24 | ] 25 | }, 26 | "organizeImports": { "enabled": true }, 27 | "linter": { "enabled": true, "rules": { "recommended": true } }, 28 | "javascript": { 29 | "formatter": { 30 | "jsxQuoteStyle": "double", 31 | "quoteProperties": "asNeeded", 32 | "trailingCommas": "all", 33 | "semicolons": "always", 34 | "arrowParentheses": "always", 35 | "bracketSameLine": false, 36 | "quoteStyle": "single", 37 | "attributePosition": "auto", 38 | "bracketSpacing": true 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | app: 3 | build: 4 | context: . 5 | dockerfile: Dockerfile 6 | ports: 7 | - "3000:3000" 8 | environment: 9 | PORT: ${PORT} 10 | LOG_LEVEL: ${LOG_LEVEL} 11 | NODE_ENV: ${NODE_ENV} 12 | SECRET_KEY: ${SECRET_KEY} 13 | 14 | DB_HOST: db 15 | DB_USER: ${DB_USER} 16 | DB_PASSWORD: ${DB_PASSWORD} 17 | DB_NAME: ${DB_NAME} 18 | 19 | REDIS_HOST: redis 20 | REDIS_PORT: ${REDIS_PORT} 21 | depends_on: 22 | db: 23 | condition: service_healthy 24 | restart: true 25 | redis: 26 | condition: service_healthy 27 | restart: true 28 | 29 | db: 30 | image: mysql 31 | restart: no 32 | ports: 33 | - "3306:3306" 34 | volumes: 35 | - mysql_data:/var/lib/mysql 36 | environment: 37 | MYSQL_ROOT_PASSWORD: password 38 | MYSQL_USER: user 39 | MYSQL_PASSWORD: password 40 | MYSQL_DATABASE: db 41 | healthcheck: 42 | test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] 43 | interval: 10s 44 | timeout: 5s 45 | retries: 5 46 | 47 | redis: 48 | image: redis 49 | ports: 50 | - "6379:6379" 51 | healthcheck: 52 | test: ["CMD", "redis-cli", "ping"] 53 | interval: 10s 54 | timeout: 5s 55 | retries: 5 56 | 57 | volumes: 58 | mysql_data: 59 | -------------------------------------------------------------------------------- /src/task/tasker.ts: -------------------------------------------------------------------------------- 1 | import { type Job, Worker } from 'bullmq'; 2 | import { logger } from '../lib/logger.js'; 3 | import { QUEUE, connection } from '../lib/queue.js'; 4 | import type { UserService } from '../service/user.js'; 5 | import sendWelcomeEmail from './sendWelcomeEmail.js'; 6 | 7 | const TASK = { 8 | SendWelcomeEmail: 'send_code_completion', 9 | }; 10 | 11 | class Tasker { 12 | private readonly userService: UserService; 13 | 14 | constructor(userService: UserService) { 15 | this.userService = userService; 16 | 17 | this.setup = this.setup.bind(this); 18 | this.processor = this.processor.bind(this); 19 | } 20 | 21 | public setup() { 22 | const worker = new Worker(QUEUE.default, this.processor, { connection }); 23 | 24 | worker.on('completed', (job: Job) => { 25 | logger.info(`Job ${job.id} completed, task name: ${job.name}`); 26 | }); 27 | 28 | worker.on('failed', (job: Job | undefined, error: Error) => { 29 | if (job) { 30 | logger.error(`Job ${job.id} failed, task name: ${job.name}, error: ${error.message}`); 31 | } else { 32 | logger.error(`Job failed, error: ${error.message}`); 33 | } 34 | }); 35 | 36 | worker.on('error', (err) => { 37 | logger.error(err); 38 | }); 39 | 40 | return worker; 41 | } 42 | 43 | private async processor(job: Job) { 44 | switch (job.name) { 45 | case TASK.SendWelcomeEmail: { 46 | await sendWelcomeEmail(job.data, this.userService); 47 | break; 48 | } 49 | } 50 | } 51 | } 52 | 53 | export { TASK, Tasker }; 54 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import env from './lib/env.js'; 2 | 3 | import { serve } from '@hono/node-server'; 4 | import { Hono } from 'hono'; 5 | import { compress } from 'hono/compress'; 6 | import { cors } from 'hono/cors'; 7 | import { showRoutes } from 'hono/dev'; 8 | import { logger as httpLogger } from 'hono/logger'; 9 | import { trimTrailingSlash } from 'hono/trailing-slash'; 10 | 11 | import { NODE_ENVIRONMENTS } from './lib/constants.js'; 12 | import { connection } from './db/database.js'; 13 | import { logger } from './lib/logger.js'; 14 | import { tracing } from './web/middlelayer/tracing.js'; 15 | import { Server } from './web/server.js'; 16 | 17 | const app = new Hono(); 18 | 19 | // Generic middlewares 20 | app.use(cors()); 21 | app.use(tracing); 22 | app.use(compress()); 23 | app.use(httpLogger()); 24 | app.use(trimTrailingSlash()); 25 | 26 | await connection.ping(); 27 | logger.info('Database connection established'); 28 | 29 | const server = new Server(app); 30 | server.configure(); 31 | 32 | if (env.NODE_ENV === NODE_ENVIRONMENTS.development) { 33 | console.log('Available routes:'); 34 | showRoutes(app); 35 | } 36 | 37 | const port = Number.parseInt(env.PORT); 38 | logger.info(`Server is running on port: ${port}, env: ${env.NODE_ENV}`); 39 | const web = serve({ fetch: app.fetch, port }); 40 | 41 | process.on('SIGTERM', () => { 42 | logger.info('SIGTERM signal received'); 43 | 44 | logger.info('Closing http server'); 45 | web.close(async () => { 46 | logger.info('Closing worker'); 47 | await server.shutDownWorker(); 48 | 49 | logger.info('Closing database connection'); 50 | await connection.end(); 51 | 52 | logger.info('Exiting...'); 53 | process.exit(0); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /src/lib/encryption.ts: -------------------------------------------------------------------------------- 1 | import crypto from 'crypto'; 2 | import env from './env.js'; 3 | 4 | const encrypt = (text: string): string => { 5 | // Generate a random initialization vector (IV) 6 | const iv = crypto.randomBytes(16); 7 | 8 | // Derive a 256-bit key from the password using SHA-256 hashing 9 | const key = crypto.createHash('sha256').update(env.SECRET_KEY).digest('base64').slice(0, 32); 10 | 11 | // Create a cipher using AES-256-CBC algorithm 12 | const cipher = crypto.createCipheriv('aes-256-cbc', key, iv); 13 | 14 | // Encrypt the plaintext 15 | let encrypted = cipher.update(text, 'utf8', 'hex'); 16 | encrypted += cipher.final('hex'); 17 | 18 | // Return the encrypted text along with the IV 19 | return `${iv.toString('hex')}:${encrypted}`; 20 | }; 21 | 22 | const decrypt = (encryptedText: string): string => { 23 | // Split the encrypted text into the IV and the ciphertext 24 | const [ivHex, ciphertext] = encryptedText.split(':'); 25 | 26 | // Convert the hexadecimal IV to a Buffer 27 | const iv = Buffer.from(ivHex, 'hex'); 28 | 29 | // Derive the 256-bit key from the password using SHA-256 hashing 30 | const key = crypto.createHash('sha256').update(env.SECRET_KEY).digest('base64').slice(0, 32); 31 | 32 | // Create a decipher using AES-256-CBC algorithm 33 | const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv); 34 | 35 | // Decrypt the ciphertext 36 | let decrypted = decipher.update(ciphertext, 'hex', 'utf8'); 37 | decrypted += decipher.final('utf8'); 38 | 39 | return decrypted; 40 | }; 41 | 42 | const verify = (text: string, encryptedText: string): boolean => { 43 | return text === decrypt(encryptedText); 44 | }; 45 | 46 | export { encrypt, verify }; 47 | -------------------------------------------------------------------------------- /src/web/controller/resp/error.ts: -------------------------------------------------------------------------------- 1 | import type { Context } from 'hono'; 2 | import { HTTPException } from 'hono/http-exception'; 3 | import type { ContentfulStatusCode } from 'hono/utils/http-status'; 4 | import { StatusCodes, getReasonPhrase } from 'http-status-codes'; 5 | 6 | const serveNotFound = (c: Context, message?: string) => { 7 | return c.json( 8 | { error: message || getReasonPhrase(StatusCodes.NOT_FOUND) }, 9 | StatusCodes.NOT_FOUND, 10 | ); 11 | }; 12 | const serveBadRequest = (c: Context, message: string) => { 13 | return c.json({ error: message }, StatusCodes.BAD_REQUEST); 14 | }; 15 | 16 | const serveUnprocessableEntity = (c: Context, message: string) => { 17 | return c.json({ error: message }, StatusCodes.UNPROCESSABLE_ENTITY); 18 | }; 19 | 20 | const serveUnauthorized = (c: Context) => { 21 | return c.json({ error: getReasonPhrase(StatusCodes.UNAUTHORIZED) }, StatusCodes.UNAUTHORIZED); 22 | }; 23 | 24 | const serveInternalServerError = (c: Context, error: any) => { 25 | if (error instanceof HTTPException) { 26 | return c.json({ error: error.message }, error.status); 27 | } 28 | 29 | return c.json({ error: error }, StatusCodes.INTERNAL_SERVER_ERROR); 30 | }; 31 | 32 | const serveError = (c: Context, status: StatusCodes, message: string) => { 33 | return c.json({ error: message }, status); 34 | }; 35 | 36 | const ERRORS = { 37 | USER_EXISTS: 'User already exists', 38 | USER_NOT_FOUND: 'User not found', 39 | }; 40 | 41 | export { 42 | ERRORS, 43 | serveBadRequest, 44 | serveError, 45 | serveInternalServerError, 46 | serveNotFound, 47 | serveUnauthorized, 48 | serveUnprocessableEntity, 49 | }; 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hono Starter 2 | 3 | A Hono starter boilerplate for TypeScript with minimal dependencies and a clean architecture. All dependencies are 4 | initiated at the start of the application and passed to the controllers and services. A swagger API doc is attached in 5 | the static folder: `openapi.yaml`. 6 | 7 | ### API Doc powered by Swagger UI 8 | 9 | Screenshot 2024-09-28 at 12 48 21 AM 10 | 11 | ### Database browser powered by Drizzle Studio 12 | 13 | Screenshot 2024-09-28 at 12 46 26 AM 14 | 15 | ## Stack 16 | 17 | - Authentication: JWT 18 | - Validation: Zod 19 | - Worker: BullMQ 20 | - Logging: Pino 21 | - ORM: Drizzle 22 | - Queue: Redis 23 | - DB: MySQL 24 | - Runtime: NodeJS 25 | - Framework: Hono 26 | - Formatter: Biome 27 | - API Doc: Swagger 28 | - Language: TypeScript 29 | - Package Manager: PNPM 30 | 31 | ## Install dependencies 32 | 33 | ```bash 34 | pnpm install 35 | pnpm install -g typescript 36 | pnpm install -g pino-pretty 37 | ``` 38 | 39 | ## Run the app 40 | 41 | Create a new file `.env` in the root folder and copy contents from the `.env.template` file. 42 | 43 | ```bash 44 | docker compose up -d 45 | ``` 46 | 47 | ### Generate db table 48 | 49 | ```bash 50 | pnpm run db:generate 51 | ``` 52 | 53 | ### Migrate schema 54 | 55 | ```bash 56 | pnpm run db:migrate 57 | ``` 58 | 59 | ### Open Swagger UI 60 | 61 | ```bash 62 | open http://localhost:3000/doc 63 | ``` 64 | 65 | ## API Doc 66 | 67 | The OpenAPI YAML doc is in the `static` folder. 68 | 69 | If you need the JSON file, it can be generated with the help of `yq`. 70 | 71 | https://github.com/mikefarah/yq 72 | 73 | ```bash 74 | yq eval -o=json static/openapi.yaml > static/openapi.json 75 | ``` 76 | 77 | And the JSON doc will be generated. 78 | 79 | ## Drizzle Studio For Database Browsing 80 | 81 | ```bash 82 | pnpm drizzle-kit studio 83 | open https://local.drizzle.studio/ 84 | ``` 85 | -------------------------------------------------------------------------------- /src/db/schema/migration/meta/0000_snapshot.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "5", 3 | "dialect": "mysql", 4 | "id": "41ca28dc-7263-4cf3-8fda-6de1f342b343", 5 | "prevId": "00000000-0000-0000-0000-000000000000", 6 | "tables": { 7 | "user": { 8 | "name": "user", 9 | "columns": { 10 | "id": { 11 | "name": "id", 12 | "type": "serial", 13 | "primaryKey": false, 14 | "notNull": true, 15 | "autoincrement": true 16 | }, 17 | "name": { 18 | "name": "name", 19 | "type": "varchar(50)", 20 | "primaryKey": false, 21 | "notNull": true, 22 | "autoincrement": false 23 | }, 24 | "email": { 25 | "name": "email", 26 | "type": "varchar(100)", 27 | "primaryKey": false, 28 | "notNull": true, 29 | "autoincrement": false 30 | }, 31 | "password": { 32 | "name": "password", 33 | "type": "varchar(65)", 34 | "primaryKey": false, 35 | "notNull": true, 36 | "autoincrement": false 37 | }, 38 | "reset_token": { 39 | "name": "reset_token", 40 | "type": "varchar(100)", 41 | "primaryKey": false, 42 | "notNull": false, 43 | "autoincrement": false 44 | }, 45 | "created_at": { 46 | "name": "created_at", 47 | "type": "timestamp", 48 | "primaryKey": false, 49 | "notNull": false, 50 | "autoincrement": false, 51 | "default": "(now())" 52 | }, 53 | "updated_at": { 54 | "name": "updated_at", 55 | "type": "timestamp", 56 | "primaryKey": false, 57 | "notNull": false, 58 | "autoincrement": false, 59 | "default": "(now())" 60 | } 61 | }, 62 | "indexes": {}, 63 | "foreignKeys": {}, 64 | "compositePrimaryKeys": { 65 | "user_id": { 66 | "name": "user_id", 67 | "columns": [ 68 | "id" 69 | ] 70 | } 71 | }, 72 | "uniqueConstraints": { 73 | "user_email_unique": { 74 | "name": "user_email_unique", 75 | "columns": [ 76 | "email" 77 | ] 78 | } 79 | } 80 | } 81 | }, 82 | "schemas": {}, 83 | "_meta": { 84 | "schemas": {}, 85 | "tables": {}, 86 | "columns": {} 87 | } 88 | } -------------------------------------------------------------------------------- /src/web/server.ts: -------------------------------------------------------------------------------- 1 | import { serveStatic } from '@hono/node-server/serve-static'; 2 | import { swaggerUI } from '@hono/swagger-ui'; 3 | import type { Worker } from 'bullmq'; 4 | import { Hono } from 'hono'; 5 | import { jwt } from 'hono/jwt'; 6 | import env from '../lib/env.js'; 7 | import { logger } from '../lib/logger.js'; 8 | import { connection } from '../lib/queue.js'; 9 | import { UserRepository } from '../repository/user.js'; 10 | import { UserService } from '../service/user.js'; 11 | import { Tasker } from '../task/tasker.js'; 12 | import { AuthController } from './controller/auth.js'; 13 | import { serveInternalServerError, serveNotFound } from './controller/resp/error.js'; 14 | import { loginValidator, registrationValidator } from './validator/user.js'; 15 | 16 | export class Server { 17 | private app: Hono; 18 | private worker?: Worker; 19 | 20 | constructor(app: Hono) { 21 | this.app = app; 22 | } 23 | 24 | public configure() { 25 | // Index path 26 | this.app.get('/', (c) => { 27 | return c.text('Ok'); 28 | }); 29 | 30 | // Static files 31 | this.app.use('/static/*', serveStatic({ root: './' })); 32 | 33 | // API Doc 34 | this.app.get('/doc', swaggerUI({ url: '/static/openapi.yaml' })); 35 | 36 | // Universal catchall 37 | this.app.notFound((c) => { 38 | return serveNotFound(c); 39 | }); 40 | 41 | // Error handling 42 | this.app.onError((err, c) => { 43 | return serveInternalServerError(c, err); 44 | }); 45 | 46 | const api = this.app.basePath('/v1'); 47 | 48 | // Setup repos 49 | const userRepo = new UserRepository(); 50 | 51 | // Setup services 52 | const userService = new UserService(userRepo); 53 | 54 | // Setup worker 55 | this.registerWorker(userService); 56 | 57 | // Setup controllers 58 | const authController = new AuthController(userService); 59 | 60 | // Register routes 61 | this.registerUserRoutes(api, authController); 62 | } 63 | 64 | private registerUserRoutes(api: Hono, authCtrl: AuthController) { 65 | const user = new Hono(); 66 | const authCheck = jwt({ secret: env.SECRET_KEY }); 67 | 68 | user.get('/me', authCheck, authCtrl.me); 69 | user.post('/login', loginValidator, authCtrl.login); 70 | user.post('/register', registrationValidator, authCtrl.register); 71 | 72 | api.route('/user', user); 73 | } 74 | 75 | private registerWorker(userService: UserService) { 76 | const tasker = new Tasker(userService); 77 | const worker = tasker.setup(); 78 | if (worker.isRunning()) { 79 | logger.info('Worker is running'); 80 | } 81 | this.worker = worker; 82 | } 83 | 84 | public async shutDownWorker() { 85 | await this.worker?.close(); 86 | await connection.quit(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/web/controller/auth.ts: -------------------------------------------------------------------------------- 1 | import type { Context } from 'hono'; 2 | import { DB_ERRORS, type DatabaseError } from '../../db/database.js'; 3 | import { verify } from '../../lib/encryption.js'; 4 | import { type JWTPayload, encode } from '../../lib/jwt.js'; 5 | import type { UserService } from '../../service/user.js'; 6 | import sendWelcomeEmailAsync from '../../task/client/sendWelcomeEmailAsync.js'; 7 | import type { LoginBody, RegistrationBody } from '../validator/user.js'; 8 | import { ERRORS, serveBadRequest, serveInternalServerError, serveNotFound, serveUnauthorized } from './resp/error.js'; 9 | import { serveData } from './resp/resp.js'; 10 | import { serializeUser } from './serializer/user.js'; 11 | 12 | export class AuthController { 13 | private service: UserService; 14 | 15 | constructor(userService: UserService) { 16 | this.service = userService; 17 | 18 | this.login = this.login.bind(this); 19 | this.register = this.register.bind(this); 20 | this.me = this.me.bind(this); 21 | } 22 | 23 | public async login(c: Context) { 24 | const body: LoginBody = await c.req.json(); 25 | const user = await this.service.findByEmail(body.email); 26 | if (!user) { 27 | return serveUnauthorized(c); 28 | } 29 | const isVerified = verify(body.password, user.password); 30 | if (!isVerified) { 31 | return serveUnauthorized(c); 32 | } 33 | 34 | const token = await encode(user.id, user.email); 35 | const serializedUser = serializeUser(user); 36 | return serveData(c, { token, user: serializedUser }); 37 | } 38 | 39 | public async register(c: Context) { 40 | const body: RegistrationBody = await c.req.json(); 41 | try { 42 | await this.service.create(body.name, body.email, body.password); 43 | } catch (err) { 44 | const e = err as DatabaseError; 45 | if (e.code === DB_ERRORS.DUPLICATE_KEY) { 46 | return serveBadRequest(c, ERRORS.USER_EXISTS); 47 | } 48 | return serveInternalServerError(c, err); 49 | } 50 | const user = await this.service.findByEmail(body.email); 51 | if (!user) { 52 | return serveNotFound(c, ERRORS.USER_NOT_FOUND); 53 | } 54 | 55 | await sendWelcomeEmailAsync(user.id); 56 | 57 | const token = await encode(user.id, user.email); 58 | const serializedUser = serializeUser(user); 59 | return serveData(c, { token, user: serializedUser }); 60 | } 61 | 62 | public async me(c: Context) { 63 | const payload: JWTPayload = c.get('jwtPayload'); 64 | const user = await this.service.findByEmail(payload.email as string); 65 | if (!user) { 66 | return serveNotFound(c, ERRORS.USER_NOT_FOUND); 67 | } 68 | 69 | const serializedUser = serializeUser(user); 70 | return serveData(c, { user: serializedUser }); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /static/openapi.yaml: -------------------------------------------------------------------------------- 1 | openapi: 3.1.0 2 | info: 3 | description: All APIs currently supported by Hono Starter 4 | title: Hono Starter 5 | version: 1.0.0 6 | servers: 7 | - url: http://localhost:3000 8 | paths: 9 | /v1/user/login: 10 | post: 11 | operationId: login 12 | parameters: [] 13 | requestBody: 14 | content: 15 | application/json: 16 | schema: 17 | properties: 18 | email: 19 | type: string 20 | required: true 21 | password: 22 | type: string 23 | required: true 24 | type: object 25 | description: '' 26 | required: true 27 | responses: 28 | '200': 29 | content: 30 | application/json: 31 | schema: 32 | $ref: '#/components/schemas/User' 33 | description: The request was successful, and the server has returned the 34 | requested resource in the response body. 35 | '401': 36 | content: 37 | application/json: 38 | schema: 39 | properties: 40 | error: 41 | type: string 42 | description: Authentication is required to access the requested resource. The 43 | client must include the appropriate credentials. 44 | summary: Login 45 | tags: 46 | - User 47 | /v1/user/me: 48 | get: 49 | operationId: getUser 50 | responses: 51 | '200': 52 | content: 53 | application/json: 54 | schema: 55 | $ref: '#/components/schemas/User' 56 | description: OK - Successful request with response body 57 | '401': 58 | content: 59 | application/json: 60 | schema: 61 | properties: 62 | error: 63 | type: string 64 | description: Authentication is required to access the requested resource. The 65 | client must include the appropriate credentials. 66 | security: 67 | - bearerAuth: [] 68 | summary: Get user 69 | tags: 70 | - User 71 | /v1/user/register: 72 | post: 73 | operationId: register 74 | requestBody: 75 | content: 76 | application/json: 77 | schema: 78 | properties: 79 | email: 80 | type: string 81 | required: true 82 | name: 83 | type: string 84 | required: true 85 | password: 86 | type: string 87 | required: true 88 | type: object 89 | description: '' 90 | required: true 91 | responses: 92 | '201': 93 | content: 94 | application/json: 95 | schema: 96 | $ref: '#/components/schemas/User' 97 | description: Created - Resource successfully created 98 | '500': 99 | content: 100 | application/json: 101 | schema: 102 | properties: 103 | error: 104 | type: string 105 | description: The server encountered an unexpected condition that prevented it 106 | from fulfilling the request. Report the issue to the support team if 107 | it persists. 108 | summary: Register 109 | tags: 110 | - User 111 | components: 112 | schemas: 113 | User: 114 | properties: 115 | id: 116 | type: integer 117 | email: 118 | type: string 119 | name: 120 | type: string 121 | createdAt: 122 | type: string 123 | required: 124 | - id 125 | - name 126 | - email 127 | - createdAt 128 | type: object 129 | securitySchemes: 130 | bearerAuth: 131 | scheme: Bearer 132 | type: http 133 | tags: 134 | - description: '' 135 | name: User 136 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | dependencies: 11 | '@hono/node-server': 12 | specifier: ^1.13.7 13 | version: 1.13.7(hono@4.6.18) 14 | '@hono/swagger-ui': 15 | specifier: ^0.5.0 16 | version: 0.5.0(hono@4.6.18) 17 | '@hono/zod-validator': 18 | specifier: ^0.4.2 19 | version: 0.4.2(hono@4.6.18)(zod@3.24.1) 20 | bullmq: 21 | specifier: ^5.36.0 22 | version: 5.36.0 23 | dotenv: 24 | specifier: ^16.4.7 25 | version: 16.4.7 26 | drizzle-orm: 27 | specifier: ^0.38.4 28 | version: 0.38.4(mysql2@3.12.0) 29 | hono: 30 | specifier: ^4.6.18 31 | version: 4.6.18 32 | http-status-codes: 33 | specifier: ^2.3.0 34 | version: 2.3.0 35 | ioredis: 36 | specifier: ^5.4.2 37 | version: 5.4.2 38 | mysql2: 39 | specifier: ^3.12.0 40 | version: 3.12.0 41 | pino: 42 | specifier: ^9.6.0 43 | version: 9.6.0 44 | zod: 45 | specifier: ^3.24.1 46 | version: 3.24.1 47 | devDependencies: 48 | '@biomejs/biome': 49 | specifier: 1.9.4 50 | version: 1.9.4 51 | '@types/node': 52 | specifier: ^22.10.10 53 | version: 22.10.10 54 | drizzle-kit: 55 | specifier: ^0.30.2 56 | version: 0.30.2 57 | prettier: 58 | specifier: 3.4.2 59 | version: 3.4.2 60 | tsx: 61 | specifier: ^4.19.2 62 | version: 4.19.2 63 | typescript: 64 | specifier: ^5.7.3 65 | version: 5.7.3 66 | 67 | packages: 68 | 69 | '@biomejs/biome@1.9.4': 70 | resolution: {integrity: sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==} 71 | engines: {node: '>=14.21.3'} 72 | hasBin: true 73 | 74 | '@biomejs/cli-darwin-arm64@1.9.4': 75 | resolution: {integrity: sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw==} 76 | engines: {node: '>=14.21.3'} 77 | cpu: [arm64] 78 | os: [darwin] 79 | 80 | '@biomejs/cli-darwin-x64@1.9.4': 81 | resolution: {integrity: sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg==} 82 | engines: {node: '>=14.21.3'} 83 | cpu: [x64] 84 | os: [darwin] 85 | 86 | '@biomejs/cli-linux-arm64-musl@1.9.4': 87 | resolution: {integrity: sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA==} 88 | engines: {node: '>=14.21.3'} 89 | cpu: [arm64] 90 | os: [linux] 91 | 92 | '@biomejs/cli-linux-arm64@1.9.4': 93 | resolution: {integrity: sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==} 94 | engines: {node: '>=14.21.3'} 95 | cpu: [arm64] 96 | os: [linux] 97 | 98 | '@biomejs/cli-linux-x64-musl@1.9.4': 99 | resolution: {integrity: sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==} 100 | engines: {node: '>=14.21.3'} 101 | cpu: [x64] 102 | os: [linux] 103 | 104 | '@biomejs/cli-linux-x64@1.9.4': 105 | resolution: {integrity: sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==} 106 | engines: {node: '>=14.21.3'} 107 | cpu: [x64] 108 | os: [linux] 109 | 110 | '@biomejs/cli-win32-arm64@1.9.4': 111 | resolution: {integrity: sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==} 112 | engines: {node: '>=14.21.3'} 113 | cpu: [arm64] 114 | os: [win32] 115 | 116 | '@biomejs/cli-win32-x64@1.9.4': 117 | resolution: {integrity: sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA==} 118 | engines: {node: '>=14.21.3'} 119 | cpu: [x64] 120 | os: [win32] 121 | 122 | '@drizzle-team/brocli@0.10.2': 123 | resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==} 124 | 125 | '@esbuild-kit/core-utils@3.3.2': 126 | resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==} 127 | deprecated: 'Merged into tsx: https://tsx.is' 128 | 129 | '@esbuild-kit/esm-loader@2.6.5': 130 | resolution: {integrity: sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==} 131 | deprecated: 'Merged into tsx: https://tsx.is' 132 | 133 | '@esbuild/aix-ppc64@0.19.12': 134 | resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==} 135 | engines: {node: '>=12'} 136 | cpu: [ppc64] 137 | os: [aix] 138 | 139 | '@esbuild/aix-ppc64@0.23.1': 140 | resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} 141 | engines: {node: '>=18'} 142 | cpu: [ppc64] 143 | os: [aix] 144 | 145 | '@esbuild/android-arm64@0.18.20': 146 | resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} 147 | engines: {node: '>=12'} 148 | cpu: [arm64] 149 | os: [android] 150 | 151 | '@esbuild/android-arm64@0.19.12': 152 | resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==} 153 | engines: {node: '>=12'} 154 | cpu: [arm64] 155 | os: [android] 156 | 157 | '@esbuild/android-arm64@0.23.1': 158 | resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} 159 | engines: {node: '>=18'} 160 | cpu: [arm64] 161 | os: [android] 162 | 163 | '@esbuild/android-arm@0.18.20': 164 | resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} 165 | engines: {node: '>=12'} 166 | cpu: [arm] 167 | os: [android] 168 | 169 | '@esbuild/android-arm@0.19.12': 170 | resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==} 171 | engines: {node: '>=12'} 172 | cpu: [arm] 173 | os: [android] 174 | 175 | '@esbuild/android-arm@0.23.1': 176 | resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} 177 | engines: {node: '>=18'} 178 | cpu: [arm] 179 | os: [android] 180 | 181 | '@esbuild/android-x64@0.18.20': 182 | resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} 183 | engines: {node: '>=12'} 184 | cpu: [x64] 185 | os: [android] 186 | 187 | '@esbuild/android-x64@0.19.12': 188 | resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==} 189 | engines: {node: '>=12'} 190 | cpu: [x64] 191 | os: [android] 192 | 193 | '@esbuild/android-x64@0.23.1': 194 | resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} 195 | engines: {node: '>=18'} 196 | cpu: [x64] 197 | os: [android] 198 | 199 | '@esbuild/darwin-arm64@0.18.20': 200 | resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} 201 | engines: {node: '>=12'} 202 | cpu: [arm64] 203 | os: [darwin] 204 | 205 | '@esbuild/darwin-arm64@0.19.12': 206 | resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==} 207 | engines: {node: '>=12'} 208 | cpu: [arm64] 209 | os: [darwin] 210 | 211 | '@esbuild/darwin-arm64@0.23.1': 212 | resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} 213 | engines: {node: '>=18'} 214 | cpu: [arm64] 215 | os: [darwin] 216 | 217 | '@esbuild/darwin-x64@0.18.20': 218 | resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} 219 | engines: {node: '>=12'} 220 | cpu: [x64] 221 | os: [darwin] 222 | 223 | '@esbuild/darwin-x64@0.19.12': 224 | resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==} 225 | engines: {node: '>=12'} 226 | cpu: [x64] 227 | os: [darwin] 228 | 229 | '@esbuild/darwin-x64@0.23.1': 230 | resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} 231 | engines: {node: '>=18'} 232 | cpu: [x64] 233 | os: [darwin] 234 | 235 | '@esbuild/freebsd-arm64@0.18.20': 236 | resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} 237 | engines: {node: '>=12'} 238 | cpu: [arm64] 239 | os: [freebsd] 240 | 241 | '@esbuild/freebsd-arm64@0.19.12': 242 | resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==} 243 | engines: {node: '>=12'} 244 | cpu: [arm64] 245 | os: [freebsd] 246 | 247 | '@esbuild/freebsd-arm64@0.23.1': 248 | resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} 249 | engines: {node: '>=18'} 250 | cpu: [arm64] 251 | os: [freebsd] 252 | 253 | '@esbuild/freebsd-x64@0.18.20': 254 | resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} 255 | engines: {node: '>=12'} 256 | cpu: [x64] 257 | os: [freebsd] 258 | 259 | '@esbuild/freebsd-x64@0.19.12': 260 | resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==} 261 | engines: {node: '>=12'} 262 | cpu: [x64] 263 | os: [freebsd] 264 | 265 | '@esbuild/freebsd-x64@0.23.1': 266 | resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} 267 | engines: {node: '>=18'} 268 | cpu: [x64] 269 | os: [freebsd] 270 | 271 | '@esbuild/linux-arm64@0.18.20': 272 | resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} 273 | engines: {node: '>=12'} 274 | cpu: [arm64] 275 | os: [linux] 276 | 277 | '@esbuild/linux-arm64@0.19.12': 278 | resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==} 279 | engines: {node: '>=12'} 280 | cpu: [arm64] 281 | os: [linux] 282 | 283 | '@esbuild/linux-arm64@0.23.1': 284 | resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} 285 | engines: {node: '>=18'} 286 | cpu: [arm64] 287 | os: [linux] 288 | 289 | '@esbuild/linux-arm@0.18.20': 290 | resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} 291 | engines: {node: '>=12'} 292 | cpu: [arm] 293 | os: [linux] 294 | 295 | '@esbuild/linux-arm@0.19.12': 296 | resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==} 297 | engines: {node: '>=12'} 298 | cpu: [arm] 299 | os: [linux] 300 | 301 | '@esbuild/linux-arm@0.23.1': 302 | resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} 303 | engines: {node: '>=18'} 304 | cpu: [arm] 305 | os: [linux] 306 | 307 | '@esbuild/linux-ia32@0.18.20': 308 | resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} 309 | engines: {node: '>=12'} 310 | cpu: [ia32] 311 | os: [linux] 312 | 313 | '@esbuild/linux-ia32@0.19.12': 314 | resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==} 315 | engines: {node: '>=12'} 316 | cpu: [ia32] 317 | os: [linux] 318 | 319 | '@esbuild/linux-ia32@0.23.1': 320 | resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} 321 | engines: {node: '>=18'} 322 | cpu: [ia32] 323 | os: [linux] 324 | 325 | '@esbuild/linux-loong64@0.18.20': 326 | resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} 327 | engines: {node: '>=12'} 328 | cpu: [loong64] 329 | os: [linux] 330 | 331 | '@esbuild/linux-loong64@0.19.12': 332 | resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==} 333 | engines: {node: '>=12'} 334 | cpu: [loong64] 335 | os: [linux] 336 | 337 | '@esbuild/linux-loong64@0.23.1': 338 | resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} 339 | engines: {node: '>=18'} 340 | cpu: [loong64] 341 | os: [linux] 342 | 343 | '@esbuild/linux-mips64el@0.18.20': 344 | resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} 345 | engines: {node: '>=12'} 346 | cpu: [mips64el] 347 | os: [linux] 348 | 349 | '@esbuild/linux-mips64el@0.19.12': 350 | resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==} 351 | engines: {node: '>=12'} 352 | cpu: [mips64el] 353 | os: [linux] 354 | 355 | '@esbuild/linux-mips64el@0.23.1': 356 | resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} 357 | engines: {node: '>=18'} 358 | cpu: [mips64el] 359 | os: [linux] 360 | 361 | '@esbuild/linux-ppc64@0.18.20': 362 | resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} 363 | engines: {node: '>=12'} 364 | cpu: [ppc64] 365 | os: [linux] 366 | 367 | '@esbuild/linux-ppc64@0.19.12': 368 | resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==} 369 | engines: {node: '>=12'} 370 | cpu: [ppc64] 371 | os: [linux] 372 | 373 | '@esbuild/linux-ppc64@0.23.1': 374 | resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} 375 | engines: {node: '>=18'} 376 | cpu: [ppc64] 377 | os: [linux] 378 | 379 | '@esbuild/linux-riscv64@0.18.20': 380 | resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} 381 | engines: {node: '>=12'} 382 | cpu: [riscv64] 383 | os: [linux] 384 | 385 | '@esbuild/linux-riscv64@0.19.12': 386 | resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==} 387 | engines: {node: '>=12'} 388 | cpu: [riscv64] 389 | os: [linux] 390 | 391 | '@esbuild/linux-riscv64@0.23.1': 392 | resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} 393 | engines: {node: '>=18'} 394 | cpu: [riscv64] 395 | os: [linux] 396 | 397 | '@esbuild/linux-s390x@0.18.20': 398 | resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} 399 | engines: {node: '>=12'} 400 | cpu: [s390x] 401 | os: [linux] 402 | 403 | '@esbuild/linux-s390x@0.19.12': 404 | resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==} 405 | engines: {node: '>=12'} 406 | cpu: [s390x] 407 | os: [linux] 408 | 409 | '@esbuild/linux-s390x@0.23.1': 410 | resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} 411 | engines: {node: '>=18'} 412 | cpu: [s390x] 413 | os: [linux] 414 | 415 | '@esbuild/linux-x64@0.18.20': 416 | resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} 417 | engines: {node: '>=12'} 418 | cpu: [x64] 419 | os: [linux] 420 | 421 | '@esbuild/linux-x64@0.19.12': 422 | resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==} 423 | engines: {node: '>=12'} 424 | cpu: [x64] 425 | os: [linux] 426 | 427 | '@esbuild/linux-x64@0.23.1': 428 | resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} 429 | engines: {node: '>=18'} 430 | cpu: [x64] 431 | os: [linux] 432 | 433 | '@esbuild/netbsd-x64@0.18.20': 434 | resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} 435 | engines: {node: '>=12'} 436 | cpu: [x64] 437 | os: [netbsd] 438 | 439 | '@esbuild/netbsd-x64@0.19.12': 440 | resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==} 441 | engines: {node: '>=12'} 442 | cpu: [x64] 443 | os: [netbsd] 444 | 445 | '@esbuild/netbsd-x64@0.23.1': 446 | resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} 447 | engines: {node: '>=18'} 448 | cpu: [x64] 449 | os: [netbsd] 450 | 451 | '@esbuild/openbsd-arm64@0.23.1': 452 | resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} 453 | engines: {node: '>=18'} 454 | cpu: [arm64] 455 | os: [openbsd] 456 | 457 | '@esbuild/openbsd-x64@0.18.20': 458 | resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} 459 | engines: {node: '>=12'} 460 | cpu: [x64] 461 | os: [openbsd] 462 | 463 | '@esbuild/openbsd-x64@0.19.12': 464 | resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==} 465 | engines: {node: '>=12'} 466 | cpu: [x64] 467 | os: [openbsd] 468 | 469 | '@esbuild/openbsd-x64@0.23.1': 470 | resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} 471 | engines: {node: '>=18'} 472 | cpu: [x64] 473 | os: [openbsd] 474 | 475 | '@esbuild/sunos-x64@0.18.20': 476 | resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} 477 | engines: {node: '>=12'} 478 | cpu: [x64] 479 | os: [sunos] 480 | 481 | '@esbuild/sunos-x64@0.19.12': 482 | resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==} 483 | engines: {node: '>=12'} 484 | cpu: [x64] 485 | os: [sunos] 486 | 487 | '@esbuild/sunos-x64@0.23.1': 488 | resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} 489 | engines: {node: '>=18'} 490 | cpu: [x64] 491 | os: [sunos] 492 | 493 | '@esbuild/win32-arm64@0.18.20': 494 | resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} 495 | engines: {node: '>=12'} 496 | cpu: [arm64] 497 | os: [win32] 498 | 499 | '@esbuild/win32-arm64@0.19.12': 500 | resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==} 501 | engines: {node: '>=12'} 502 | cpu: [arm64] 503 | os: [win32] 504 | 505 | '@esbuild/win32-arm64@0.23.1': 506 | resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} 507 | engines: {node: '>=18'} 508 | cpu: [arm64] 509 | os: [win32] 510 | 511 | '@esbuild/win32-ia32@0.18.20': 512 | resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} 513 | engines: {node: '>=12'} 514 | cpu: [ia32] 515 | os: [win32] 516 | 517 | '@esbuild/win32-ia32@0.19.12': 518 | resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==} 519 | engines: {node: '>=12'} 520 | cpu: [ia32] 521 | os: [win32] 522 | 523 | '@esbuild/win32-ia32@0.23.1': 524 | resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} 525 | engines: {node: '>=18'} 526 | cpu: [ia32] 527 | os: [win32] 528 | 529 | '@esbuild/win32-x64@0.18.20': 530 | resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} 531 | engines: {node: '>=12'} 532 | cpu: [x64] 533 | os: [win32] 534 | 535 | '@esbuild/win32-x64@0.19.12': 536 | resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==} 537 | engines: {node: '>=12'} 538 | cpu: [x64] 539 | os: [win32] 540 | 541 | '@esbuild/win32-x64@0.23.1': 542 | resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} 543 | engines: {node: '>=18'} 544 | cpu: [x64] 545 | os: [win32] 546 | 547 | '@hono/node-server@1.13.7': 548 | resolution: {integrity: sha512-kTfUMsoloVKtRA2fLiGSd9qBddmru9KadNyhJCwgKBxTiNkaAJEwkVN9KV/rS4HtmmNRtUh6P+YpmjRMl0d9vQ==} 549 | engines: {node: '>=18.14.1'} 550 | peerDependencies: 551 | hono: ^4 552 | 553 | '@hono/swagger-ui@0.5.0': 554 | resolution: {integrity: sha512-MWYYSv9kC8IwFBLZdwgZZMT9zUq2C/4/ekuyEYOkHEgUMqu+FG3eebtBZ4ofMh60xYRxRR2BgQGoNIILys/PFg==} 555 | peerDependencies: 556 | hono: '*' 557 | 558 | '@hono/zod-validator@0.4.2': 559 | resolution: {integrity: sha512-1rrlBg+EpDPhzOV4hT9pxr5+xDVmKuz6YJl+la7VCwK6ass5ldyKm5fD+umJdV2zhHD6jROoCCv8NbTwyfhT0g==} 560 | peerDependencies: 561 | hono: '>=3.9.0' 562 | zod: ^3.19.1 563 | 564 | '@ioredis/commands@1.2.0': 565 | resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==} 566 | 567 | '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': 568 | resolution: {integrity: sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==} 569 | cpu: [arm64] 570 | os: [darwin] 571 | 572 | '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': 573 | resolution: {integrity: sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==} 574 | cpu: [x64] 575 | os: [darwin] 576 | 577 | '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': 578 | resolution: {integrity: sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==} 579 | cpu: [arm64] 580 | os: [linux] 581 | 582 | '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': 583 | resolution: {integrity: sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==} 584 | cpu: [arm] 585 | os: [linux] 586 | 587 | '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': 588 | resolution: {integrity: sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==} 589 | cpu: [x64] 590 | os: [linux] 591 | 592 | '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': 593 | resolution: {integrity: sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==} 594 | cpu: [x64] 595 | os: [win32] 596 | 597 | '@types/node@22.10.10': 598 | resolution: {integrity: sha512-X47y/mPNzxviAGY5TcYPtYL8JsY3kAq2n8fMmKoRCxq/c4v4pyGNCzM2R6+M5/umG4ZfHuT+sgqDYqWc9rJ6ww==} 599 | 600 | atomic-sleep@1.0.0: 601 | resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} 602 | engines: {node: '>=8.0.0'} 603 | 604 | aws-ssl-profiles@1.1.2: 605 | resolution: {integrity: sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==} 606 | engines: {node: '>= 6.0.0'} 607 | 608 | buffer-from@1.1.2: 609 | resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} 610 | 611 | bullmq@5.36.0: 612 | resolution: {integrity: sha512-+08ghSARvO2W0VxYh5HO4AnrF7FZ0MC3Shig76HO6shsytMCrgXAvYwbfBufP2bd0HR9eI1a5J7xKazjXX7ogA==} 613 | 614 | cluster-key-slot@1.1.2: 615 | resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} 616 | engines: {node: '>=0.10.0'} 617 | 618 | cron-parser@4.9.0: 619 | resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==} 620 | engines: {node: '>=12.0.0'} 621 | 622 | debug@4.4.0: 623 | resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} 624 | engines: {node: '>=6.0'} 625 | peerDependencies: 626 | supports-color: '*' 627 | peerDependenciesMeta: 628 | supports-color: 629 | optional: true 630 | 631 | denque@2.1.0: 632 | resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} 633 | engines: {node: '>=0.10'} 634 | 635 | detect-libc@2.0.3: 636 | resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} 637 | engines: {node: '>=8'} 638 | 639 | dotenv@16.4.7: 640 | resolution: {integrity: sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==} 641 | engines: {node: '>=12'} 642 | 643 | drizzle-kit@0.30.2: 644 | resolution: {integrity: sha512-vhdLrxWA32WNVF77NabpSnX7pQBornx64VDQDmKddRonOB2Xe/yY4glQ7rECoa+ogqcQNo7VblLUbeBK6Zn9Ow==} 645 | hasBin: true 646 | 647 | drizzle-orm@0.38.4: 648 | resolution: {integrity: sha512-s7/5BpLKO+WJRHspvpqTydxFob8i1vo2rEx4pY6TGY7QSMuUfWUuzaY0DIpXCkgHOo37BaFC+SJQb99dDUXT3Q==} 649 | peerDependencies: 650 | '@aws-sdk/client-rds-data': '>=3' 651 | '@cloudflare/workers-types': '>=4' 652 | '@electric-sql/pglite': '>=0.2.0' 653 | '@libsql/client': '>=0.10.0' 654 | '@libsql/client-wasm': '>=0.10.0' 655 | '@neondatabase/serverless': '>=0.10.0' 656 | '@op-engineering/op-sqlite': '>=2' 657 | '@opentelemetry/api': ^1.4.1 658 | '@planetscale/database': '>=1' 659 | '@prisma/client': '*' 660 | '@tidbcloud/serverless': '*' 661 | '@types/better-sqlite3': '*' 662 | '@types/pg': '*' 663 | '@types/react': '>=18' 664 | '@types/sql.js': '*' 665 | '@vercel/postgres': '>=0.8.0' 666 | '@xata.io/client': '*' 667 | better-sqlite3: '>=7' 668 | bun-types: '*' 669 | expo-sqlite: '>=14.0.0' 670 | knex: '*' 671 | kysely: '*' 672 | mysql2: '>=2' 673 | pg: '>=8' 674 | postgres: '>=3' 675 | prisma: '*' 676 | react: '>=18' 677 | sql.js: '>=1' 678 | sqlite3: '>=5' 679 | peerDependenciesMeta: 680 | '@aws-sdk/client-rds-data': 681 | optional: true 682 | '@cloudflare/workers-types': 683 | optional: true 684 | '@electric-sql/pglite': 685 | optional: true 686 | '@libsql/client': 687 | optional: true 688 | '@libsql/client-wasm': 689 | optional: true 690 | '@neondatabase/serverless': 691 | optional: true 692 | '@op-engineering/op-sqlite': 693 | optional: true 694 | '@opentelemetry/api': 695 | optional: true 696 | '@planetscale/database': 697 | optional: true 698 | '@prisma/client': 699 | optional: true 700 | '@tidbcloud/serverless': 701 | optional: true 702 | '@types/better-sqlite3': 703 | optional: true 704 | '@types/pg': 705 | optional: true 706 | '@types/react': 707 | optional: true 708 | '@types/sql.js': 709 | optional: true 710 | '@vercel/postgres': 711 | optional: true 712 | '@xata.io/client': 713 | optional: true 714 | better-sqlite3: 715 | optional: true 716 | bun-types: 717 | optional: true 718 | expo-sqlite: 719 | optional: true 720 | knex: 721 | optional: true 722 | kysely: 723 | optional: true 724 | mysql2: 725 | optional: true 726 | pg: 727 | optional: true 728 | postgres: 729 | optional: true 730 | prisma: 731 | optional: true 732 | react: 733 | optional: true 734 | sql.js: 735 | optional: true 736 | sqlite3: 737 | optional: true 738 | 739 | esbuild-register@3.6.0: 740 | resolution: {integrity: sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==} 741 | peerDependencies: 742 | esbuild: '>=0.12 <1' 743 | 744 | esbuild@0.18.20: 745 | resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} 746 | engines: {node: '>=12'} 747 | hasBin: true 748 | 749 | esbuild@0.19.12: 750 | resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} 751 | engines: {node: '>=12'} 752 | hasBin: true 753 | 754 | esbuild@0.23.1: 755 | resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} 756 | engines: {node: '>=18'} 757 | hasBin: true 758 | 759 | fast-redact@3.5.0: 760 | resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} 761 | engines: {node: '>=6'} 762 | 763 | fsevents@2.3.3: 764 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 765 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 766 | os: [darwin] 767 | 768 | generate-function@2.3.1: 769 | resolution: {integrity: sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==} 770 | 771 | get-tsconfig@4.10.0: 772 | resolution: {integrity: sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==} 773 | 774 | hono@4.6.18: 775 | resolution: {integrity: sha512-Fu7hEPvdwtPG8LqSAiPn8p8HjD+PDxrP/HVnwRBnwtVKOn5zDDKsw0ma2Xt58oq97Rlp3t/mRNADEV/Ym6cDng==} 776 | engines: {node: '>=16.9.0'} 777 | 778 | http-status-codes@2.3.0: 779 | resolution: {integrity: sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==} 780 | 781 | iconv-lite@0.6.3: 782 | resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} 783 | engines: {node: '>=0.10.0'} 784 | 785 | ioredis@5.4.2: 786 | resolution: {integrity: sha512-0SZXGNGZ+WzISQ67QDyZ2x0+wVxjjUndtD8oSeik/4ajifeiRufed8fCb8QW8VMyi4MXcS+UO1k/0NGhvq1PAg==} 787 | engines: {node: '>=12.22.0'} 788 | 789 | is-property@1.0.2: 790 | resolution: {integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==} 791 | 792 | lodash.defaults@4.2.0: 793 | resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} 794 | 795 | lodash.isarguments@3.1.0: 796 | resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==} 797 | 798 | long@5.2.4: 799 | resolution: {integrity: sha512-qtzLbJE8hq7VabR3mISmVGtoXP8KGc2Z/AT8OuqlYD7JTR3oqrgwdjnk07wpj1twXxYmgDXgoKVWUG/fReSzHg==} 800 | 801 | lru-cache@7.18.3: 802 | resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} 803 | engines: {node: '>=12'} 804 | 805 | lru.min@1.1.1: 806 | resolution: {integrity: sha512-FbAj6lXil6t8z4z3j0E5mfRlPzxkySotzUHwRXjlpRh10vc6AI6WN62ehZj82VG7M20rqogJ0GLwar2Xa05a8Q==} 807 | engines: {bun: '>=1.0.0', deno: '>=1.30.0', node: '>=8.0.0'} 808 | 809 | luxon@3.5.0: 810 | resolution: {integrity: sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==} 811 | engines: {node: '>=12'} 812 | 813 | ms@2.1.3: 814 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 815 | 816 | msgpackr-extract@3.0.3: 817 | resolution: {integrity: sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==} 818 | hasBin: true 819 | 820 | msgpackr@1.11.2: 821 | resolution: {integrity: sha512-F9UngXRlPyWCDEASDpTf6c9uNhGPTqnTeLVt7bN+bU1eajoR/8V9ys2BRaV5C/e5ihE6sJ9uPIKaYt6bFuO32g==} 822 | 823 | mysql2@3.12.0: 824 | resolution: {integrity: sha512-C8fWhVysZoH63tJbX8d10IAoYCyXy4fdRFz2Ihrt9jtPILYynFEKUUzpp1U7qxzDc3tMbotvaBH+sl6bFnGZiw==} 825 | engines: {node: '>= 8.0'} 826 | 827 | named-placeholders@1.1.3: 828 | resolution: {integrity: sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==} 829 | engines: {node: '>=12.0.0'} 830 | 831 | node-abort-controller@3.1.1: 832 | resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} 833 | 834 | node-gyp-build-optional-packages@5.2.2: 835 | resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==} 836 | hasBin: true 837 | 838 | on-exit-leak-free@2.1.2: 839 | resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} 840 | engines: {node: '>=14.0.0'} 841 | 842 | pino-abstract-transport@2.0.0: 843 | resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} 844 | 845 | pino-std-serializers@7.0.0: 846 | resolution: {integrity: sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==} 847 | 848 | pino@9.6.0: 849 | resolution: {integrity: sha512-i85pKRCt4qMjZ1+L7sy2Ag4t1atFcdbEt76+7iRJn1g2BvsnRMGu9p8pivl9fs63M2kF/A0OacFZhTub+m/qMg==} 850 | hasBin: true 851 | 852 | prettier@3.4.2: 853 | resolution: {integrity: sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==} 854 | engines: {node: '>=14'} 855 | hasBin: true 856 | 857 | process-warning@4.0.1: 858 | resolution: {integrity: sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==} 859 | 860 | quick-format-unescaped@4.0.4: 861 | resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} 862 | 863 | real-require@0.2.0: 864 | resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} 865 | engines: {node: '>= 12.13.0'} 866 | 867 | redis-errors@1.2.0: 868 | resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==} 869 | engines: {node: '>=4'} 870 | 871 | redis-parser@3.0.0: 872 | resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} 873 | engines: {node: '>=4'} 874 | 875 | resolve-pkg-maps@1.0.0: 876 | resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} 877 | 878 | safe-stable-stringify@2.5.0: 879 | resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} 880 | engines: {node: '>=10'} 881 | 882 | safer-buffer@2.1.2: 883 | resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} 884 | 885 | semver@7.6.3: 886 | resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} 887 | engines: {node: '>=10'} 888 | hasBin: true 889 | 890 | seq-queue@0.0.5: 891 | resolution: {integrity: sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==} 892 | 893 | sonic-boom@4.2.0: 894 | resolution: {integrity: sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==} 895 | 896 | source-map-support@0.5.21: 897 | resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} 898 | 899 | source-map@0.6.1: 900 | resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} 901 | engines: {node: '>=0.10.0'} 902 | 903 | split2@4.2.0: 904 | resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} 905 | engines: {node: '>= 10.x'} 906 | 907 | sqlstring@2.3.3: 908 | resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==} 909 | engines: {node: '>= 0.6'} 910 | 911 | standard-as-callback@2.1.0: 912 | resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} 913 | 914 | thread-stream@3.1.0: 915 | resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} 916 | 917 | tslib@2.8.1: 918 | resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} 919 | 920 | tsx@4.19.2: 921 | resolution: {integrity: sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==} 922 | engines: {node: '>=18.0.0'} 923 | hasBin: true 924 | 925 | typescript@5.7.3: 926 | resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==} 927 | engines: {node: '>=14.17'} 928 | hasBin: true 929 | 930 | undici-types@6.20.0: 931 | resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} 932 | 933 | uuid@9.0.1: 934 | resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} 935 | hasBin: true 936 | 937 | zod@3.24.1: 938 | resolution: {integrity: sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==} 939 | 940 | snapshots: 941 | 942 | '@biomejs/biome@1.9.4': 943 | optionalDependencies: 944 | '@biomejs/cli-darwin-arm64': 1.9.4 945 | '@biomejs/cli-darwin-x64': 1.9.4 946 | '@biomejs/cli-linux-arm64': 1.9.4 947 | '@biomejs/cli-linux-arm64-musl': 1.9.4 948 | '@biomejs/cli-linux-x64': 1.9.4 949 | '@biomejs/cli-linux-x64-musl': 1.9.4 950 | '@biomejs/cli-win32-arm64': 1.9.4 951 | '@biomejs/cli-win32-x64': 1.9.4 952 | 953 | '@biomejs/cli-darwin-arm64@1.9.4': 954 | optional: true 955 | 956 | '@biomejs/cli-darwin-x64@1.9.4': 957 | optional: true 958 | 959 | '@biomejs/cli-linux-arm64-musl@1.9.4': 960 | optional: true 961 | 962 | '@biomejs/cli-linux-arm64@1.9.4': 963 | optional: true 964 | 965 | '@biomejs/cli-linux-x64-musl@1.9.4': 966 | optional: true 967 | 968 | '@biomejs/cli-linux-x64@1.9.4': 969 | optional: true 970 | 971 | '@biomejs/cli-win32-arm64@1.9.4': 972 | optional: true 973 | 974 | '@biomejs/cli-win32-x64@1.9.4': 975 | optional: true 976 | 977 | '@drizzle-team/brocli@0.10.2': {} 978 | 979 | '@esbuild-kit/core-utils@3.3.2': 980 | dependencies: 981 | esbuild: 0.18.20 982 | source-map-support: 0.5.21 983 | 984 | '@esbuild-kit/esm-loader@2.6.5': 985 | dependencies: 986 | '@esbuild-kit/core-utils': 3.3.2 987 | get-tsconfig: 4.10.0 988 | 989 | '@esbuild/aix-ppc64@0.19.12': 990 | optional: true 991 | 992 | '@esbuild/aix-ppc64@0.23.1': 993 | optional: true 994 | 995 | '@esbuild/android-arm64@0.18.20': 996 | optional: true 997 | 998 | '@esbuild/android-arm64@0.19.12': 999 | optional: true 1000 | 1001 | '@esbuild/android-arm64@0.23.1': 1002 | optional: true 1003 | 1004 | '@esbuild/android-arm@0.18.20': 1005 | optional: true 1006 | 1007 | '@esbuild/android-arm@0.19.12': 1008 | optional: true 1009 | 1010 | '@esbuild/android-arm@0.23.1': 1011 | optional: true 1012 | 1013 | '@esbuild/android-x64@0.18.20': 1014 | optional: true 1015 | 1016 | '@esbuild/android-x64@0.19.12': 1017 | optional: true 1018 | 1019 | '@esbuild/android-x64@0.23.1': 1020 | optional: true 1021 | 1022 | '@esbuild/darwin-arm64@0.18.20': 1023 | optional: true 1024 | 1025 | '@esbuild/darwin-arm64@0.19.12': 1026 | optional: true 1027 | 1028 | '@esbuild/darwin-arm64@0.23.1': 1029 | optional: true 1030 | 1031 | '@esbuild/darwin-x64@0.18.20': 1032 | optional: true 1033 | 1034 | '@esbuild/darwin-x64@0.19.12': 1035 | optional: true 1036 | 1037 | '@esbuild/darwin-x64@0.23.1': 1038 | optional: true 1039 | 1040 | '@esbuild/freebsd-arm64@0.18.20': 1041 | optional: true 1042 | 1043 | '@esbuild/freebsd-arm64@0.19.12': 1044 | optional: true 1045 | 1046 | '@esbuild/freebsd-arm64@0.23.1': 1047 | optional: true 1048 | 1049 | '@esbuild/freebsd-x64@0.18.20': 1050 | optional: true 1051 | 1052 | '@esbuild/freebsd-x64@0.19.12': 1053 | optional: true 1054 | 1055 | '@esbuild/freebsd-x64@0.23.1': 1056 | optional: true 1057 | 1058 | '@esbuild/linux-arm64@0.18.20': 1059 | optional: true 1060 | 1061 | '@esbuild/linux-arm64@0.19.12': 1062 | optional: true 1063 | 1064 | '@esbuild/linux-arm64@0.23.1': 1065 | optional: true 1066 | 1067 | '@esbuild/linux-arm@0.18.20': 1068 | optional: true 1069 | 1070 | '@esbuild/linux-arm@0.19.12': 1071 | optional: true 1072 | 1073 | '@esbuild/linux-arm@0.23.1': 1074 | optional: true 1075 | 1076 | '@esbuild/linux-ia32@0.18.20': 1077 | optional: true 1078 | 1079 | '@esbuild/linux-ia32@0.19.12': 1080 | optional: true 1081 | 1082 | '@esbuild/linux-ia32@0.23.1': 1083 | optional: true 1084 | 1085 | '@esbuild/linux-loong64@0.18.20': 1086 | optional: true 1087 | 1088 | '@esbuild/linux-loong64@0.19.12': 1089 | optional: true 1090 | 1091 | '@esbuild/linux-loong64@0.23.1': 1092 | optional: true 1093 | 1094 | '@esbuild/linux-mips64el@0.18.20': 1095 | optional: true 1096 | 1097 | '@esbuild/linux-mips64el@0.19.12': 1098 | optional: true 1099 | 1100 | '@esbuild/linux-mips64el@0.23.1': 1101 | optional: true 1102 | 1103 | '@esbuild/linux-ppc64@0.18.20': 1104 | optional: true 1105 | 1106 | '@esbuild/linux-ppc64@0.19.12': 1107 | optional: true 1108 | 1109 | '@esbuild/linux-ppc64@0.23.1': 1110 | optional: true 1111 | 1112 | '@esbuild/linux-riscv64@0.18.20': 1113 | optional: true 1114 | 1115 | '@esbuild/linux-riscv64@0.19.12': 1116 | optional: true 1117 | 1118 | '@esbuild/linux-riscv64@0.23.1': 1119 | optional: true 1120 | 1121 | '@esbuild/linux-s390x@0.18.20': 1122 | optional: true 1123 | 1124 | '@esbuild/linux-s390x@0.19.12': 1125 | optional: true 1126 | 1127 | '@esbuild/linux-s390x@0.23.1': 1128 | optional: true 1129 | 1130 | '@esbuild/linux-x64@0.18.20': 1131 | optional: true 1132 | 1133 | '@esbuild/linux-x64@0.19.12': 1134 | optional: true 1135 | 1136 | '@esbuild/linux-x64@0.23.1': 1137 | optional: true 1138 | 1139 | '@esbuild/netbsd-x64@0.18.20': 1140 | optional: true 1141 | 1142 | '@esbuild/netbsd-x64@0.19.12': 1143 | optional: true 1144 | 1145 | '@esbuild/netbsd-x64@0.23.1': 1146 | optional: true 1147 | 1148 | '@esbuild/openbsd-arm64@0.23.1': 1149 | optional: true 1150 | 1151 | '@esbuild/openbsd-x64@0.18.20': 1152 | optional: true 1153 | 1154 | '@esbuild/openbsd-x64@0.19.12': 1155 | optional: true 1156 | 1157 | '@esbuild/openbsd-x64@0.23.1': 1158 | optional: true 1159 | 1160 | '@esbuild/sunos-x64@0.18.20': 1161 | optional: true 1162 | 1163 | '@esbuild/sunos-x64@0.19.12': 1164 | optional: true 1165 | 1166 | '@esbuild/sunos-x64@0.23.1': 1167 | optional: true 1168 | 1169 | '@esbuild/win32-arm64@0.18.20': 1170 | optional: true 1171 | 1172 | '@esbuild/win32-arm64@0.19.12': 1173 | optional: true 1174 | 1175 | '@esbuild/win32-arm64@0.23.1': 1176 | optional: true 1177 | 1178 | '@esbuild/win32-ia32@0.18.20': 1179 | optional: true 1180 | 1181 | '@esbuild/win32-ia32@0.19.12': 1182 | optional: true 1183 | 1184 | '@esbuild/win32-ia32@0.23.1': 1185 | optional: true 1186 | 1187 | '@esbuild/win32-x64@0.18.20': 1188 | optional: true 1189 | 1190 | '@esbuild/win32-x64@0.19.12': 1191 | optional: true 1192 | 1193 | '@esbuild/win32-x64@0.23.1': 1194 | optional: true 1195 | 1196 | '@hono/node-server@1.13.7(hono@4.6.18)': 1197 | dependencies: 1198 | hono: 4.6.18 1199 | 1200 | '@hono/swagger-ui@0.5.0(hono@4.6.18)': 1201 | dependencies: 1202 | hono: 4.6.18 1203 | 1204 | '@hono/zod-validator@0.4.2(hono@4.6.18)(zod@3.24.1)': 1205 | dependencies: 1206 | hono: 4.6.18 1207 | zod: 3.24.1 1208 | 1209 | '@ioredis/commands@1.2.0': {} 1210 | 1211 | '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': 1212 | optional: true 1213 | 1214 | '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': 1215 | optional: true 1216 | 1217 | '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': 1218 | optional: true 1219 | 1220 | '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': 1221 | optional: true 1222 | 1223 | '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': 1224 | optional: true 1225 | 1226 | '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': 1227 | optional: true 1228 | 1229 | '@types/node@22.10.10': 1230 | dependencies: 1231 | undici-types: 6.20.0 1232 | 1233 | atomic-sleep@1.0.0: {} 1234 | 1235 | aws-ssl-profiles@1.1.2: {} 1236 | 1237 | buffer-from@1.1.2: {} 1238 | 1239 | bullmq@5.36.0: 1240 | dependencies: 1241 | cron-parser: 4.9.0 1242 | ioredis: 5.4.2 1243 | msgpackr: 1.11.2 1244 | node-abort-controller: 3.1.1 1245 | semver: 7.6.3 1246 | tslib: 2.8.1 1247 | uuid: 9.0.1 1248 | transitivePeerDependencies: 1249 | - supports-color 1250 | 1251 | cluster-key-slot@1.1.2: {} 1252 | 1253 | cron-parser@4.9.0: 1254 | dependencies: 1255 | luxon: 3.5.0 1256 | 1257 | debug@4.4.0: 1258 | dependencies: 1259 | ms: 2.1.3 1260 | 1261 | denque@2.1.0: {} 1262 | 1263 | detect-libc@2.0.3: 1264 | optional: true 1265 | 1266 | dotenv@16.4.7: {} 1267 | 1268 | drizzle-kit@0.30.2: 1269 | dependencies: 1270 | '@drizzle-team/brocli': 0.10.2 1271 | '@esbuild-kit/esm-loader': 2.6.5 1272 | esbuild: 0.19.12 1273 | esbuild-register: 3.6.0(esbuild@0.19.12) 1274 | transitivePeerDependencies: 1275 | - supports-color 1276 | 1277 | drizzle-orm@0.38.4(mysql2@3.12.0): 1278 | optionalDependencies: 1279 | mysql2: 3.12.0 1280 | 1281 | esbuild-register@3.6.0(esbuild@0.19.12): 1282 | dependencies: 1283 | debug: 4.4.0 1284 | esbuild: 0.19.12 1285 | transitivePeerDependencies: 1286 | - supports-color 1287 | 1288 | esbuild@0.18.20: 1289 | optionalDependencies: 1290 | '@esbuild/android-arm': 0.18.20 1291 | '@esbuild/android-arm64': 0.18.20 1292 | '@esbuild/android-x64': 0.18.20 1293 | '@esbuild/darwin-arm64': 0.18.20 1294 | '@esbuild/darwin-x64': 0.18.20 1295 | '@esbuild/freebsd-arm64': 0.18.20 1296 | '@esbuild/freebsd-x64': 0.18.20 1297 | '@esbuild/linux-arm': 0.18.20 1298 | '@esbuild/linux-arm64': 0.18.20 1299 | '@esbuild/linux-ia32': 0.18.20 1300 | '@esbuild/linux-loong64': 0.18.20 1301 | '@esbuild/linux-mips64el': 0.18.20 1302 | '@esbuild/linux-ppc64': 0.18.20 1303 | '@esbuild/linux-riscv64': 0.18.20 1304 | '@esbuild/linux-s390x': 0.18.20 1305 | '@esbuild/linux-x64': 0.18.20 1306 | '@esbuild/netbsd-x64': 0.18.20 1307 | '@esbuild/openbsd-x64': 0.18.20 1308 | '@esbuild/sunos-x64': 0.18.20 1309 | '@esbuild/win32-arm64': 0.18.20 1310 | '@esbuild/win32-ia32': 0.18.20 1311 | '@esbuild/win32-x64': 0.18.20 1312 | 1313 | esbuild@0.19.12: 1314 | optionalDependencies: 1315 | '@esbuild/aix-ppc64': 0.19.12 1316 | '@esbuild/android-arm': 0.19.12 1317 | '@esbuild/android-arm64': 0.19.12 1318 | '@esbuild/android-x64': 0.19.12 1319 | '@esbuild/darwin-arm64': 0.19.12 1320 | '@esbuild/darwin-x64': 0.19.12 1321 | '@esbuild/freebsd-arm64': 0.19.12 1322 | '@esbuild/freebsd-x64': 0.19.12 1323 | '@esbuild/linux-arm': 0.19.12 1324 | '@esbuild/linux-arm64': 0.19.12 1325 | '@esbuild/linux-ia32': 0.19.12 1326 | '@esbuild/linux-loong64': 0.19.12 1327 | '@esbuild/linux-mips64el': 0.19.12 1328 | '@esbuild/linux-ppc64': 0.19.12 1329 | '@esbuild/linux-riscv64': 0.19.12 1330 | '@esbuild/linux-s390x': 0.19.12 1331 | '@esbuild/linux-x64': 0.19.12 1332 | '@esbuild/netbsd-x64': 0.19.12 1333 | '@esbuild/openbsd-x64': 0.19.12 1334 | '@esbuild/sunos-x64': 0.19.12 1335 | '@esbuild/win32-arm64': 0.19.12 1336 | '@esbuild/win32-ia32': 0.19.12 1337 | '@esbuild/win32-x64': 0.19.12 1338 | 1339 | esbuild@0.23.1: 1340 | optionalDependencies: 1341 | '@esbuild/aix-ppc64': 0.23.1 1342 | '@esbuild/android-arm': 0.23.1 1343 | '@esbuild/android-arm64': 0.23.1 1344 | '@esbuild/android-x64': 0.23.1 1345 | '@esbuild/darwin-arm64': 0.23.1 1346 | '@esbuild/darwin-x64': 0.23.1 1347 | '@esbuild/freebsd-arm64': 0.23.1 1348 | '@esbuild/freebsd-x64': 0.23.1 1349 | '@esbuild/linux-arm': 0.23.1 1350 | '@esbuild/linux-arm64': 0.23.1 1351 | '@esbuild/linux-ia32': 0.23.1 1352 | '@esbuild/linux-loong64': 0.23.1 1353 | '@esbuild/linux-mips64el': 0.23.1 1354 | '@esbuild/linux-ppc64': 0.23.1 1355 | '@esbuild/linux-riscv64': 0.23.1 1356 | '@esbuild/linux-s390x': 0.23.1 1357 | '@esbuild/linux-x64': 0.23.1 1358 | '@esbuild/netbsd-x64': 0.23.1 1359 | '@esbuild/openbsd-arm64': 0.23.1 1360 | '@esbuild/openbsd-x64': 0.23.1 1361 | '@esbuild/sunos-x64': 0.23.1 1362 | '@esbuild/win32-arm64': 0.23.1 1363 | '@esbuild/win32-ia32': 0.23.1 1364 | '@esbuild/win32-x64': 0.23.1 1365 | 1366 | fast-redact@3.5.0: {} 1367 | 1368 | fsevents@2.3.3: 1369 | optional: true 1370 | 1371 | generate-function@2.3.1: 1372 | dependencies: 1373 | is-property: 1.0.2 1374 | 1375 | get-tsconfig@4.10.0: 1376 | dependencies: 1377 | resolve-pkg-maps: 1.0.0 1378 | 1379 | hono@4.6.18: {} 1380 | 1381 | http-status-codes@2.3.0: {} 1382 | 1383 | iconv-lite@0.6.3: 1384 | dependencies: 1385 | safer-buffer: 2.1.2 1386 | 1387 | ioredis@5.4.2: 1388 | dependencies: 1389 | '@ioredis/commands': 1.2.0 1390 | cluster-key-slot: 1.1.2 1391 | debug: 4.4.0 1392 | denque: 2.1.0 1393 | lodash.defaults: 4.2.0 1394 | lodash.isarguments: 3.1.0 1395 | redis-errors: 1.2.0 1396 | redis-parser: 3.0.0 1397 | standard-as-callback: 2.1.0 1398 | transitivePeerDependencies: 1399 | - supports-color 1400 | 1401 | is-property@1.0.2: {} 1402 | 1403 | lodash.defaults@4.2.0: {} 1404 | 1405 | lodash.isarguments@3.1.0: {} 1406 | 1407 | long@5.2.4: {} 1408 | 1409 | lru-cache@7.18.3: {} 1410 | 1411 | lru.min@1.1.1: {} 1412 | 1413 | luxon@3.5.0: {} 1414 | 1415 | ms@2.1.3: {} 1416 | 1417 | msgpackr-extract@3.0.3: 1418 | dependencies: 1419 | node-gyp-build-optional-packages: 5.2.2 1420 | optionalDependencies: 1421 | '@msgpackr-extract/msgpackr-extract-darwin-arm64': 3.0.3 1422 | '@msgpackr-extract/msgpackr-extract-darwin-x64': 3.0.3 1423 | '@msgpackr-extract/msgpackr-extract-linux-arm': 3.0.3 1424 | '@msgpackr-extract/msgpackr-extract-linux-arm64': 3.0.3 1425 | '@msgpackr-extract/msgpackr-extract-linux-x64': 3.0.3 1426 | '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.3 1427 | optional: true 1428 | 1429 | msgpackr@1.11.2: 1430 | optionalDependencies: 1431 | msgpackr-extract: 3.0.3 1432 | 1433 | mysql2@3.12.0: 1434 | dependencies: 1435 | aws-ssl-profiles: 1.1.2 1436 | denque: 2.1.0 1437 | generate-function: 2.3.1 1438 | iconv-lite: 0.6.3 1439 | long: 5.2.4 1440 | lru.min: 1.1.1 1441 | named-placeholders: 1.1.3 1442 | seq-queue: 0.0.5 1443 | sqlstring: 2.3.3 1444 | 1445 | named-placeholders@1.1.3: 1446 | dependencies: 1447 | lru-cache: 7.18.3 1448 | 1449 | node-abort-controller@3.1.1: {} 1450 | 1451 | node-gyp-build-optional-packages@5.2.2: 1452 | dependencies: 1453 | detect-libc: 2.0.3 1454 | optional: true 1455 | 1456 | on-exit-leak-free@2.1.2: {} 1457 | 1458 | pino-abstract-transport@2.0.0: 1459 | dependencies: 1460 | split2: 4.2.0 1461 | 1462 | pino-std-serializers@7.0.0: {} 1463 | 1464 | pino@9.6.0: 1465 | dependencies: 1466 | atomic-sleep: 1.0.0 1467 | fast-redact: 3.5.0 1468 | on-exit-leak-free: 2.1.2 1469 | pino-abstract-transport: 2.0.0 1470 | pino-std-serializers: 7.0.0 1471 | process-warning: 4.0.1 1472 | quick-format-unescaped: 4.0.4 1473 | real-require: 0.2.0 1474 | safe-stable-stringify: 2.5.0 1475 | sonic-boom: 4.2.0 1476 | thread-stream: 3.1.0 1477 | 1478 | prettier@3.4.2: {} 1479 | 1480 | process-warning@4.0.1: {} 1481 | 1482 | quick-format-unescaped@4.0.4: {} 1483 | 1484 | real-require@0.2.0: {} 1485 | 1486 | redis-errors@1.2.0: {} 1487 | 1488 | redis-parser@3.0.0: 1489 | dependencies: 1490 | redis-errors: 1.2.0 1491 | 1492 | resolve-pkg-maps@1.0.0: {} 1493 | 1494 | safe-stable-stringify@2.5.0: {} 1495 | 1496 | safer-buffer@2.1.2: {} 1497 | 1498 | semver@7.6.3: {} 1499 | 1500 | seq-queue@0.0.5: {} 1501 | 1502 | sonic-boom@4.2.0: 1503 | dependencies: 1504 | atomic-sleep: 1.0.0 1505 | 1506 | source-map-support@0.5.21: 1507 | dependencies: 1508 | buffer-from: 1.1.2 1509 | source-map: 0.6.1 1510 | 1511 | source-map@0.6.1: {} 1512 | 1513 | split2@4.2.0: {} 1514 | 1515 | sqlstring@2.3.3: {} 1516 | 1517 | standard-as-callback@2.1.0: {} 1518 | 1519 | thread-stream@3.1.0: 1520 | dependencies: 1521 | real-require: 0.2.0 1522 | 1523 | tslib@2.8.1: {} 1524 | 1525 | tsx@4.19.2: 1526 | dependencies: 1527 | esbuild: 0.23.1 1528 | get-tsconfig: 4.10.0 1529 | optionalDependencies: 1530 | fsevents: 2.3.3 1531 | 1532 | typescript@5.7.3: {} 1533 | 1534 | undici-types@6.20.0: {} 1535 | 1536 | uuid@9.0.1: {} 1537 | 1538 | zod@3.24.1: {} 1539 | --------------------------------------------------------------------------------