From 3dfaeff1af76dd953ad2ce1ff93ddf29500f2dd9 Mon Sep 17 00:00:00 2001 From: Yaroslav Grishajev Date: Thu, 27 Jun 2024 12:01:25 +0200 Subject: [PATCH] feat(billing): add billing module with trial wallet creation As a part of this add: - DI container - Drizzle ORM - logging refs #247 --- apps/api/.env.functional.test | 9 + apps/api/drizzle.config.ts | 12 + apps/api/drizzle/0000_chilly_mastermind.sql | 7 + apps/api/drizzle/meta/0000_snapshot.json | 56 + apps/api/drizzle/meta/_journal.json | 13 + apps/api/package.json | 23 +- apps/api/src/app.ts | 41 +- apps/api/src/billing/config/env.config.ts | 18 + apps/api/src/billing/config/index.ts | 7 + apps/api/src/billing/config/network.config.ts | 4 + .../controllers/wallet/wallet.controller.ts | 38 + apps/api/src/billing/index.ts | 3 + apps/api/src/billing/model-schemas/index.ts | 1 + .../user-wallet/user-wallet.schema.ts | 14 + apps/api/src/billing/models/index.ts | 1 + .../models/user-wallet/user-wallet.model.ts | 37 + .../src/billing/providers/config.provider.ts | 11 + apps/api/src/billing/providers/index.ts | 5 + .../providers/user-wallet-schema.provider.ts | 11 + apps/api/src/billing/repositories/index.ts | 1 + .../user-wallet/user-wallet.repository.ts | 52 + apps/api/src/billing/routes/index.ts | 1 + .../billing/routes/wallet/wallet.router.ts | 40 + apps/api/src/billing/services/index.ts | 3 + .../rpc-message.service.ts | 74 + .../wallet-initializer.service.ts | 20 + .../billing/services/wallet/wallet.service.ts | 131 ++ apps/api/src/console.ts | 21 + apps/api/src/core/config/env.config.ts | 14 + apps/api/src/core/config/index.ts | 5 + apps/api/src/core/index.ts | 3 + .../api/src/core/providers/config.provider.ts | 9 + apps/api/src/core/providers/index.ts | 2 + .../src/core/providers/postgres.provider.ts | 22 + .../contextual-logger.service.ts | 32 + .../http-logger/http-logger.service.ts | 29 + apps/api/src/core/services/index.ts | 6 + .../core/services/logger/logger.service.ts | 50 + .../postgres-logger.service.ts | 22 + .../postgres-migrator.service.ts | 19 + .../request-storage.service.ts | 35 + apps/api/src/core/services/tx/tx.service.ts | 44 + .../src/core/types/hono-interceptor.type.ts | 5 + apps/api/src/index.ts | 10 +- .../api/test/functional/create-wallet.spec.ts | 47 + apps/api/test/setup-functional-tests.ts | 9 + docker/bin/check-postgres-init.sh | 0 docker/bin/prepare-and-seed-postgres.sh | 0 package-lock.json | 1763 ++++++++++++++++- package.json | 4 + packages/dev-config/.eslintrc.base.js | 2 +- 51 files changed, 2744 insertions(+), 42 deletions(-) create mode 100644 apps/api/.env.functional.test create mode 100644 apps/api/drizzle.config.ts create mode 100644 apps/api/drizzle/0000_chilly_mastermind.sql create mode 100644 apps/api/drizzle/meta/0000_snapshot.json create mode 100644 apps/api/drizzle/meta/_journal.json create mode 100644 apps/api/src/billing/config/env.config.ts create mode 100644 apps/api/src/billing/config/index.ts create mode 100644 apps/api/src/billing/config/network.config.ts create mode 100644 apps/api/src/billing/controllers/wallet/wallet.controller.ts create mode 100644 apps/api/src/billing/index.ts create mode 100644 apps/api/src/billing/model-schemas/index.ts create mode 100644 apps/api/src/billing/model-schemas/user-wallet/user-wallet.schema.ts create mode 100644 apps/api/src/billing/models/index.ts create mode 100644 apps/api/src/billing/models/user-wallet/user-wallet.model.ts create mode 100644 apps/api/src/billing/providers/config.provider.ts create mode 100644 apps/api/src/billing/providers/index.ts create mode 100644 apps/api/src/billing/providers/user-wallet-schema.provider.ts create mode 100644 apps/api/src/billing/repositories/index.ts create mode 100644 apps/api/src/billing/repositories/user-wallet/user-wallet.repository.ts create mode 100644 apps/api/src/billing/routes/index.ts create mode 100644 apps/api/src/billing/routes/wallet/wallet.router.ts create mode 100644 apps/api/src/billing/services/index.ts create mode 100644 apps/api/src/billing/services/rpc-message-service/rpc-message.service.ts create mode 100644 apps/api/src/billing/services/wallet-initializer/wallet-initializer.service.ts create mode 100644 apps/api/src/billing/services/wallet/wallet.service.ts create mode 100644 apps/api/src/console.ts create mode 100644 apps/api/src/core/config/env.config.ts create mode 100644 apps/api/src/core/config/index.ts create mode 100644 apps/api/src/core/index.ts create mode 100644 apps/api/src/core/providers/config.provider.ts create mode 100644 apps/api/src/core/providers/index.ts create mode 100644 apps/api/src/core/providers/postgres.provider.ts create mode 100644 apps/api/src/core/services/contextual-logger/contextual-logger.service.ts create mode 100644 apps/api/src/core/services/http-logger/http-logger.service.ts create mode 100644 apps/api/src/core/services/index.ts create mode 100644 apps/api/src/core/services/logger/logger.service.ts create mode 100644 apps/api/src/core/services/postgres-logger/postgres-logger.service.ts create mode 100644 apps/api/src/core/services/postgres-migrator/postgres-migrator.service.ts create mode 100644 apps/api/src/core/services/request-storage/request-storage.service.ts create mode 100644 apps/api/src/core/services/tx/tx.service.ts create mode 100644 apps/api/src/core/types/hono-interceptor.type.ts create mode 100644 apps/api/test/functional/create-wallet.spec.ts mode change 100755 => 100644 docker/bin/check-postgres-init.sh mode change 100755 => 100644 docker/bin/prepare-and-seed-postgres.sh diff --git a/apps/api/.env.functional.test b/apps/api/.env.functional.test new file mode 100644 index 000000000..3d67c3ea2 --- /dev/null +++ b/apps/api/.env.functional.test @@ -0,0 +1,9 @@ +MASTER_WALLET_MNEMONIC="south super just human picture mesh garlic carbon witness drill river design" +NETWORK=sandbox +POSTGRES_DB_URI=postgres://postgres:password@192.168.2.20:5432/console-users +RPC_NODE_ENDPOINT=https://rpc.sandbox-01.aksh.pw:443 +TRIAL_DEPLOYMENT_ALLOWANCE_AMOUNT=100000 +TRIAL_FEES_ALLOWANCE_AMOUNT=100000 +TRIAL_ALLOWANCE_DENOM=uakt +LOG_LEVEL=info +BILLING_ENABLED=true \ No newline at end of file diff --git a/apps/api/drizzle.config.ts b/apps/api/drizzle.config.ts new file mode 100644 index 000000000..e061ecdd4 --- /dev/null +++ b/apps/api/drizzle.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from "drizzle-kit"; + +import { env } from "./src/utils/env"; + +export default defineConfig({ + schema: "./src/billing/model-schemas", + out: "./drizzle", + dialect: "postgresql", + dbCredentials: { + url: env.UserDatabaseCS + } +}); diff --git a/apps/api/drizzle/0000_chilly_mastermind.sql b/apps/api/drizzle/0000_chilly_mastermind.sql new file mode 100644 index 000000000..3397b2ecc --- /dev/null +++ b/apps/api/drizzle/0000_chilly_mastermind.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS "user_wallets" ( + "id" serial PRIMARY KEY NOT NULL, + "user_id" varchar, + "address" varchar, + "stripe_customer_id" varchar, + "credit_amount" numeric(10, 2) DEFAULT '0.00' NOT NULL +); diff --git a/apps/api/drizzle/meta/0000_snapshot.json b/apps/api/drizzle/meta/0000_snapshot.json new file mode 100644 index 000000000..49d0b0834 --- /dev/null +++ b/apps/api/drizzle/meta/0000_snapshot.json @@ -0,0 +1,56 @@ +{ + "id": "777d9ff9-333b-42b3-a5f4-759b3774909b", + "prevId": "00000000-0000-0000-0000-000000000000", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.user_wallets": { + "name": "user_wallets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "address": { + "name": "address", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "stripe_customer_id": { + "name": "stripe_customer_id", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "credit_amount": { + "name": "credit_amount", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0.00'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "schemas": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/api/drizzle/meta/_journal.json b/apps/api/drizzle/meta/_journal.json new file mode 100644 index 000000000..d44d4166f --- /dev/null +++ b/apps/api/drizzle/meta/_journal.json @@ -0,0 +1,13 @@ +{ + "version": "7", + "dialect": "postgresql", + "entries": [ + { + "idx": 0, + "version": "7", + "when": 1720022370732, + "tag": "0000_chilly_mastermind", + "breakpoints": true + } + ] +} \ No newline at end of file diff --git a/apps/api/package.json b/apps/api/package.json index ff39b1df4..514da5272 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -11,10 +11,12 @@ "main": "server.js", "scripts": { "build": "webpack --config webpack.prod.js", + "dev": "npm run start", "format": "prettier --write ./*.{js,json} **/*.{ts,js,json}", "lint": "eslint .", + "migrate": "node-pg-migrate", "start": "webpack --config webpack.dev.js --watch", - "dev": "npm run start", + "console": "webpack --config webpack.prod.js --entry ./src/console.ts --output-filename console.js && node dist/console.js", "test": "jest --selectProjects unit functional", "test:cov": "jest --selectProjects unit functional --coverage", "test:functional": "jest --selectProjects functional", @@ -23,7 +25,8 @@ "test:unit": "jest --selectProjects unit", "test:unit:cov": "jest --selectProjects unit --coverage", "test:unit:watch": "jest --selectProjects unit --watch", - "test:watch": "jest --selectProjects unit functional --watch" + "test:watch": "jest --selectProjects unit functional --watch", + "migration:gen": "drizzle-kit generate" }, "dependencies": { "@akashnetwork/akash-api": "^1.3.0", @@ -40,27 +43,34 @@ "@hono/zod-openapi": "0.9.5", "@octokit/rest": "^18.12.0", "@sentry/node": "^7.55.2", + "@supercharge/promise-pool": "^3.2.0", "axios": "^0.27.2", + "commander": "^12.1.0", "cosmjs-types": "^0.5.0", "date-fns": "^2.29.2", "date-fns-tz": "^1.3.6", "dotenv": "^12.0.4", + "drizzle-orm": "^0.31.2", "hono": "3.12.0", "human-interval": "^2.0.1", "js-sha256": "^0.9.0", "lodash": "^4.17.21", "markdown-to-txt": "^2.0.1", "memory-cache": "^0.2.0", + "node-dependency-injection": "^3.1.2", "node-fetch": "^2.6.1", - "pg": "^8.7.3", + "pg": "^8.12.0", "pg-hstore": "^2.3.4", + "pino": "^9.2.0", + "pino-pretty": "^11.2.1", "protobufjs": "^6.11.2", "semver": "^7.3.8", "sequelize": "^6.21.3", "sequelize-typescript": "^2.1.5", + "sql-formatter": "^15.3.2", "stripe": "^10.14.0", - "uuid": "^9.0.1", - "zod": "^3.22.4" + "tsyringe": "^4.8.0", + "uuid": "^9.0.1" }, "devDependencies": { "@akashnetwork/dev-config": "*", @@ -70,11 +80,12 @@ "@types/memory-cache": "^0.2.2", "@types/node": "20.14.0", "@types/node-fetch": "^2.6.2", - "@types/pg": "^8.6.5", + "@types/pg": "^8.11.6", "@types/semver": "^7.5.2", "@types/uuid": "^8.3.1", "@typescript-eslint/eslint-plugin": "^7.12.0", "alias-hq": "^5.1.6", + "drizzle-kit": "^0.22.7", "eslint": "^8.57.0", "eslint-config-next": "^14.2.3", "eslint-plugin-simple-import-sort": "^12.1.0", diff --git a/apps/api/src/app.ts b/apps/api/src/app.ts index c36995882..d28739d26 100644 --- a/apps/api/src/app.ts +++ b/apps/api/src/app.ts @@ -1,3 +1,5 @@ +import "reflect-metadata"; + import { serve } from "@hono/node-server"; // TODO: find out how to properly import this // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -6,7 +8,9 @@ import { sentry } from "@hono/sentry"; import * as Sentry from "@sentry/node"; import { Hono } from "hono"; import { cors } from "hono/cors"; +import { container } from "tsyringe"; +import { HttpLoggerService, LoggerService, RequestStorageService } from "@src/core"; import packageJson from "../package.json"; import { chainDb, syncUserSchema, userDb } from "./db/dbConnection"; import { apiRouter } from "./routers/apiRouter"; @@ -28,7 +32,7 @@ appHono.use( }) ); -const { PORT = 3080 } = process.env; +const { PORT = 3080, BILLING_ENABLED } = process.env; Sentry.init({ dsn: env.SentryDSN, @@ -55,6 +59,8 @@ const scheduler = new Scheduler({ } }); +appHono.use(container.resolve(RequestStorageService).intercept()); +appHono.use(container.resolve(HttpLoggerService).intercept()); appHono.use( "*", sentry({ @@ -77,6 +83,12 @@ appHono.route("/web3-index", web3IndexRouter); appHono.route("/dashboard", dashboardRouter); appHono.route("/internal", internalRouter); +// TODO: remove condition once billing is in prod +if (BILLING_ENABLED === "true") { + // eslint-disable-next-line @typescript-eslint/no-var-requires + appHono.route("/", require("./billing").walletRouter); +} + appHono.get("/status", c => { const version = packageJson.version; const tasksStatus = scheduler.getTasksStatus(); @@ -95,6 +107,8 @@ function startScheduler() { scheduler.start(); } +const appLogger = new LoggerService({ context: "APP" }); + /** * Initialize database * Start scheduler @@ -105,15 +119,14 @@ export async function initApp() { await initDb(); startScheduler(); - console.log("Starting server at http://localhost:" + PORT); + appLogger.info({ event: "SERVER_STARTING", url: `http://localhost:${PORT}` }); serve({ fetch: appHono.fetch, port: typeof PORT === "string" ? parseInt(PORT) : PORT }); - } catch (err) { - console.error("Error while initializing app", err); - - Sentry.captureException(err); + } catch (error) { + appLogger.error({ event: "APP_INIT_ERROR", error }); + Sentry.captureException(error); } } @@ -123,20 +136,18 @@ export async function initApp() { * Create backups per version * Load from backup if exists for current version */ -export async function initDb(options: { log?: boolean } = { log: true }) { - const log = (value: string) => options?.log && console.log(value); - - log(`Connecting to chain database (${chainDb.config.host}/${chainDb.config.database})...`); +export async function initDb() { + appLogger.debug(`Connecting to chain database (${chainDb.config.host}/${chainDb.config.database})...`); await chainDb.authenticate(); - log("Connection has been established successfully."); + appLogger.debug("Connection has been established successfully."); - log(`Connecting to user database (${userDb.config.host}/${userDb.config.database})...`); + appLogger.debug(`Connecting to user database (${userDb.config.host}/${userDb.config.database})...`); await userDb.authenticate(); - log("Connection has been established successfully."); + appLogger.debug("Connection has been established successfully."); - log("Sync user schema..."); + appLogger.debug("Sync user schema..."); await syncUserSchema(); - log("User schema synced."); + appLogger.debug("User schema synced."); } export { appHono as app }; diff --git a/apps/api/src/billing/config/env.config.ts b/apps/api/src/billing/config/env.config.ts new file mode 100644 index 000000000..7376a505b --- /dev/null +++ b/apps/api/src/billing/config/env.config.ts @@ -0,0 +1,18 @@ +import dotenv from "dotenv"; +import { z } from "zod"; + +dotenv.config({ path: ".env.local" }); +dotenv.config(); + +const envSchema = z.object({ + MASTER_WALLET_MNEMONIC: z.string(), + NETWORK: z.enum(["mainnet", "testnet", "sandbox"]), + RPC_NODE_ENDPOINT: z.string(), + TRIAL_ALLOWANCE_EXPIRATION_DAYS: z.number({ coerce: true }).default(14), + TRIAL_DEPLOYMENT_ALLOWANCE_AMOUNT: z.number({ coerce: true }), + TRIAL_FEES_ALLOWANCE_AMOUNT: z.number({ coerce: true }), + TRIAL_ALLOWANCE_DENOM: z.string(), + GAS_SAFETY_MULTIPLIER: z.number({ coerce: true }).default(1.5) +}); + +export const envConfig = envSchema.parse(process.env); diff --git a/apps/api/src/billing/config/index.ts b/apps/api/src/billing/config/index.ts new file mode 100644 index 000000000..8e88be4c8 --- /dev/null +++ b/apps/api/src/billing/config/index.ts @@ -0,0 +1,7 @@ +import { envConfig } from "./env.config"; +import { USDC_IBC_DENOMS } from "./network.config"; + +export const config = { + ...envConfig, + USDC_IBC_DENOMS +}; diff --git a/apps/api/src/billing/config/network.config.ts b/apps/api/src/billing/config/network.config.ts new file mode 100644 index 000000000..3056f7fce --- /dev/null +++ b/apps/api/src/billing/config/network.config.ts @@ -0,0 +1,4 @@ +export const USDC_IBC_DENOMS = { + mainnetId: "ibc/170C677610AC31DF0904FFE09CD3B5C657492170E7E52372E48756B71E56F2F1", + sandboxId: "ibc/12C6A0C374171B595A0A9E18B83FA09D295FB1F2D8C6DAA3AC28683471752D84" +}; diff --git a/apps/api/src/billing/controllers/wallet/wallet.controller.ts b/apps/api/src/billing/controllers/wallet/wallet.controller.ts new file mode 100644 index 000000000..73e201a54 --- /dev/null +++ b/apps/api/src/billing/controllers/wallet/wallet.controller.ts @@ -0,0 +1,38 @@ +import { PromisePool } from "@supercharge/promise-pool"; +import { singleton } from "tsyringe"; + +import { UserWalletRepository } from "@src/billing/repositories"; +import { CreateWalletInput, CreateWalletOutput } from "@src/billing/routes"; +import { WalletInitializerService, WalletService } from "@src/billing/services"; +import { WithTransaction } from "@src/core/services"; + +@singleton() +export class WalletController { + constructor( + private readonly walletManager: WalletService, + private readonly userWalletRepository: UserWalletRepository, + private readonly walletInitializer: WalletInitializerService + ) {} + + @WithTransaction() + async create({ userId }: CreateWalletInput): Promise { + await this.walletInitializer.initialize(userId); + } + + async refillAll() { + const wallets = await this.userWalletRepository.find(); + const { results, errors } = await PromisePool.withConcurrency(2) + .for(wallets) + .process(async wallet => { + const refilled = await this.walletManager.refill(wallet); + console.log("DEBUG refilled", refilled); + return refilled; + }); + + if (errors) { + console.log("DEBUG errors", errors); + } + + console.log("DEBUG results", results); + } +} diff --git a/apps/api/src/billing/index.ts b/apps/api/src/billing/index.ts new file mode 100644 index 000000000..6300b123c --- /dev/null +++ b/apps/api/src/billing/index.ts @@ -0,0 +1,3 @@ +import "./providers"; + +export * from "./routes"; diff --git a/apps/api/src/billing/model-schemas/index.ts b/apps/api/src/billing/model-schemas/index.ts new file mode 100644 index 000000000..b50d9db48 --- /dev/null +++ b/apps/api/src/billing/model-schemas/index.ts @@ -0,0 +1 @@ +export * from "./user-wallet/user-wallet.schema"; diff --git a/apps/api/src/billing/model-schemas/user-wallet/user-wallet.schema.ts b/apps/api/src/billing/model-schemas/user-wallet/user-wallet.schema.ts new file mode 100644 index 000000000..74ab3f2a8 --- /dev/null +++ b/apps/api/src/billing/model-schemas/user-wallet/user-wallet.schema.ts @@ -0,0 +1,14 @@ +import { numeric, pgTable, serial, varchar } from "drizzle-orm/pg-core"; + +export const userWalletSchema = pgTable("user_wallets", { + id: serial("id").primaryKey(), + userId: varchar("user_id"), + address: varchar("address"), + stripeCustomerId: varchar("stripe_customer_id"), + creditAmount: numeric("credit_amount", { + precision: 10, + scale: 2 + }) + .notNull() + .default("0.00") +}); diff --git a/apps/api/src/billing/models/index.ts b/apps/api/src/billing/models/index.ts new file mode 100644 index 000000000..adfe57585 --- /dev/null +++ b/apps/api/src/billing/models/index.ts @@ -0,0 +1 @@ +export * from "./user-wallet/user-wallet.model"; diff --git a/apps/api/src/billing/models/user-wallet/user-wallet.model.ts b/apps/api/src/billing/models/user-wallet/user-wallet.model.ts new file mode 100644 index 000000000..bda89535d --- /dev/null +++ b/apps/api/src/billing/models/user-wallet/user-wallet.model.ts @@ -0,0 +1,37 @@ +import { DataTypes, UUIDV4 } from "sequelize"; +import { AllowNull, Column, Default, Model, PrimaryKey, Table } from "sequelize-typescript"; + +export interface UserWallet { + id: string; + address: string; + cert: string; + certKey: string; + stripeCustomerId: string; + creditAmount: number; + createdAt: Date; + updatedAt: Date; +} + +@Table({ + modelName: "user_wallet" +}) +export class UserWalletModel extends Model implements Omit { + @AllowNull(false) + @PrimaryKey + @Default(UUIDV4) + @Column(DataTypes.UUID) + id: string; + + @AllowNull(false) + @Column(DataTypes.STRING) + address: string; + + @AllowNull(false) + @Column(DataTypes.STRING) + stripeCustomerId: string; + + @AllowNull(false) + @Column(DataTypes.DECIMAL(10, 2)) + @Default(0.0) + creditAmount: number; +} diff --git a/apps/api/src/billing/providers/config.provider.ts b/apps/api/src/billing/providers/config.provider.ts new file mode 100644 index 000000000..3ebfe3fbc --- /dev/null +++ b/apps/api/src/billing/providers/config.provider.ts @@ -0,0 +1,11 @@ +import { container, inject } from "tsyringe"; + +import { config } from "@src/billing/config"; + +export const BILLING_CONFIG = "BILLING_CONFIG"; + +container.register(BILLING_CONFIG, { useValue: config }); + +export type BillingConfig = typeof config; + +export const InjectBillingConfig = () => inject(BILLING_CONFIG); diff --git a/apps/api/src/billing/providers/index.ts b/apps/api/src/billing/providers/index.ts new file mode 100644 index 000000000..2c8f88597 --- /dev/null +++ b/apps/api/src/billing/providers/index.ts @@ -0,0 +1,5 @@ +import "./user-wallet-schema.provider"; +import "./config.provider"; + +export * from "./user-wallet-schema.provider"; +export * from "./config.provider"; diff --git a/apps/api/src/billing/providers/user-wallet-schema.provider.ts b/apps/api/src/billing/providers/user-wallet-schema.provider.ts new file mode 100644 index 000000000..70675d98f --- /dev/null +++ b/apps/api/src/billing/providers/user-wallet-schema.provider.ts @@ -0,0 +1,11 @@ +import { container, inject } from "tsyringe"; + +import { userWalletSchema } from "@src/billing/model-schemas"; + +export const USER_WALLET_SCHEMA = "USER_WALLET_SCHEMA"; + +container.register(USER_WALLET_SCHEMA, { useValue: userWalletSchema }); + +export type UserWalletSchema = typeof userWalletSchema; + +export const InjectUserWalletSchema = () => inject(USER_WALLET_SCHEMA); diff --git a/apps/api/src/billing/repositories/index.ts b/apps/api/src/billing/repositories/index.ts new file mode 100644 index 000000000..60853884a --- /dev/null +++ b/apps/api/src/billing/repositories/index.ts @@ -0,0 +1 @@ +export * from "./user-wallet/user-wallet.repository"; diff --git a/apps/api/src/billing/repositories/user-wallet/user-wallet.repository.ts b/apps/api/src/billing/repositories/user-wallet/user-wallet.repository.ts new file mode 100644 index 000000000..c1ec7836a --- /dev/null +++ b/apps/api/src/billing/repositories/user-wallet/user-wallet.repository.ts @@ -0,0 +1,52 @@ +import { eq } from "drizzle-orm"; +import first from "lodash/first"; +import { singleton } from "tsyringe"; + +import { InjectUserWalletSchema, UserWalletSchema } from "@src/billing/providers"; +import { ApiPgDatabase, InjectPg } from "@src/core/providers"; +import { TxService } from "@src/core/services"; + +export type UserInput = Partial; + +@singleton() +export class UserWalletRepository { + constructor( + @InjectPg() private readonly pg: ApiPgDatabase, + @InjectUserWalletSchema() private readonly userWallet: UserWalletSchema, + private readonly txManager: TxService + ) {} + + async create(input: Pick) { + const pg = this.txManager.getPgTx() || this.pg; + return first( + await pg + .insert(this.userWallet) + .values({ + userId: input.userId, + address: input.address + }) + .returning() + ); + } + + async updateById( + id: UserWalletSchema["$inferSelect"]["id"], + payload: Partial, + options?: { returning: R } + ): Promise { + const pg = this.txManager.getPgTx() || this.pg; + const cursor = pg.update(this.userWallet).set(payload).where(eq(this.userWallet.id, id)); + + if (options?.returning === true) { + first(await cursor.returning()); + } + + await cursor; + + return undefined; + } + + async find() { + return await this.pg.query.userWalletSchema.findMany(); + } +} diff --git a/apps/api/src/billing/routes/index.ts b/apps/api/src/billing/routes/index.ts new file mode 100644 index 000000000..4dae6508c --- /dev/null +++ b/apps/api/src/billing/routes/index.ts @@ -0,0 +1 @@ +export * from "./wallet/wallet.router"; diff --git a/apps/api/src/billing/routes/wallet/wallet.router.ts b/apps/api/src/billing/routes/wallet/wallet.router.ts new file mode 100644 index 000000000..74d6960a0 --- /dev/null +++ b/apps/api/src/billing/routes/wallet/wallet.router.ts @@ -0,0 +1,40 @@ +import { createRoute, OpenAPIHono } from "@hono/zod-openapi"; +import { container } from "tsyringe"; +import { z } from "zod"; + +import { WalletController } from "@src/billing/controllers/wallet/wallet.controller"; + +export const CreateWalletInputSchema = z.object({ + userId: z.string().openapi({}) +}); + +export const CreateWalletOutputSchema = z.void(); +export type CreateWalletInput = z.infer; +export type CreateWalletOutput = z.infer; + +const route = createRoute({ + method: "post", + path: "/v1/wallets", + summary: "Creates a managed wallet for a user", + tags: ["Wallets"], + request: { + body: { + content: { + "application/json": { + schema: CreateWalletInputSchema + } + } + } + }, + responses: { + 200: { + description: "Returns a created wallet" + } + } +}); +export const walletRouter = new OpenAPIHono(); + +walletRouter.openapi(route, async function routeWallet(c) { + await container.resolve(WalletController).create(c.req.valid("json")); + return c.json(undefined, 200); +}); diff --git a/apps/api/src/billing/services/index.ts b/apps/api/src/billing/services/index.ts new file mode 100644 index 000000000..563ab0262 --- /dev/null +++ b/apps/api/src/billing/services/index.ts @@ -0,0 +1,3 @@ +export * from "@src/billing/services/wallet/wallet.service"; +export * from "./rpc-message-service/rpc-message.service"; +export * from "./wallet-initializer/wallet-initializer.service"; diff --git a/apps/api/src/billing/services/rpc-message-service/rpc-message.service.ts b/apps/api/src/billing/services/rpc-message-service/rpc-message.service.ts new file mode 100644 index 000000000..fe7d3accb --- /dev/null +++ b/apps/api/src/billing/services/rpc-message-service/rpc-message.service.ts @@ -0,0 +1,74 @@ +import { DepositDeploymentAuthorization } from "@akashnetwork/akash-api/v1beta3"; +import { BasicAllowance } from "cosmjs-types/cosmos/feegrant/v1beta1/feegrant"; +import { MsgGrantAllowance } from "cosmjs-types/cosmos/feegrant/v1beta1/tx"; +import Long from "long"; +import { singleton } from "tsyringe"; + +interface SpendingAuthorizationOptions { + granter: string; + grantee: string; + denom: string; + limit: number; + expiration?: Date; +} + +@singleton() +export class RpcMessageService { + getGrantBasicAllowanceMsg({ denom, limit, expiration, granter, grantee }: SpendingAuthorizationOptions) { + const allowance = { + typeUrl: "/cosmos.feegrant.v1beta1.BasicAllowance", + value: Uint8Array.from( + BasicAllowance.encode({ + spendLimit: [ + { + denom: denom, + amount: limit.toString() + } + ], + expiration: expiration + ? { + seconds: Long.fromInt(Math.floor(expiration.getTime() / 1_000)) as unknown as Long, + nanos: Math.floor((expiration.getTime() % 1_000) * 1_000_000) + } + : undefined + }).finish() + ) + }; + + return { + typeUrl: "/cosmos.feegrant.v1beta1.MsgGrantAllowance", + value: MsgGrantAllowance.fromPartial({ + granter: granter, + grantee: grantee, + allowance: allowance + }) + }; + } + + getGrantMsg({ denom, limit, expiration, granter, grantee }: SpendingAuthorizationOptions) { + return { + typeUrl: "/cosmos.authz.v1beta1.MsgGrant", + value: { + granter: granter, + grantee: grantee, + grant: { + authorization: { + typeUrl: "/akash.deployment.v1beta3.DepositDeploymentAuthorization", + value: DepositDeploymentAuthorization.encode( + DepositDeploymentAuthorization.fromPartial({ + spendLimit: { + denom: denom, + amount: limit.toString() + } + }) + ).finish() + }, + expiration: { + seconds: Math.floor(expiration.getTime() / 1_000), + nanos: Math.floor((expiration.getTime() % 1_000) * 1_000_000) + } + } + } + }; + } +} diff --git a/apps/api/src/billing/services/wallet-initializer/wallet-initializer.service.ts b/apps/api/src/billing/services/wallet-initializer/wallet-initializer.service.ts new file mode 100644 index 000000000..aa2bd8a96 --- /dev/null +++ b/apps/api/src/billing/services/wallet-initializer/wallet-initializer.service.ts @@ -0,0 +1,20 @@ +import { singleton } from "tsyringe"; + +import { UserInput, UserWalletRepository } from "@src/billing/repositories"; +import { WalletService } from "@src/billing/services"; +import { WithTransaction } from "@src/core/services"; + +@singleton() +export class WalletInitializerService { + constructor( + private readonly walletManager: WalletService, + private readonly userWalletRepository: UserWalletRepository + ) {} + + @WithTransaction() + async initialize(userId: UserInput["userId"]) { + const userWallet = await this.userWalletRepository.create({ userId }); + const wallet = await this.walletManager.createAndAuthorizeTrialSpending({ addressIndex: userWallet.id }); + await this.userWalletRepository.updateById(userWallet.id, { address: wallet.address }); + } +} diff --git a/apps/api/src/billing/services/wallet/wallet.service.ts b/apps/api/src/billing/services/wallet/wallet.service.ts new file mode 100644 index 000000000..9269a98e0 --- /dev/null +++ b/apps/api/src/billing/services/wallet/wallet.service.ts @@ -0,0 +1,131 @@ +import { stringToPath } from "@cosmjs/crypto"; +import { DirectSecp256k1HdWallet, EncodeObject } from "@cosmjs/proto-signing"; +import { calculateFee, GasPrice, SigningStargateClient } from "@cosmjs/stargate"; +import add from "date-fns/add"; +import { singleton } from "tsyringe"; + +import { BillingConfig, InjectBillingConfig } from "@src/billing/providers"; +import { RpcMessageService } from "@src/billing/services/rpc-message-service/rpc-message.service"; +import { ContextualLoggerService } from "@src/core/services"; + +interface SpendingAuthorizationOptions { + address: string; + limits: { + deployment: number; + fees: number; + }; + expiration: Date; +} + +@singleton() +export class WalletService { + private readonly PREFIX = "akash"; + + private readonly HD_PATH = "m/44'/118'/0'/0"; + + private masterWallet: DirectSecp256k1HdWallet; + + private client: SigningStargateClient; + + constructor( + @InjectBillingConfig() private readonly config: BillingConfig, + private readonly rpcMessageService: RpcMessageService, + private readonly logger: ContextualLoggerService + ) { + this.logger.setContext({ context: WalletService.name }); + } + + async createAndAuthorizeTrialSpending({ addressIndex }: { addressIndex: number }) { + const { address } = await this.createWallet({ addressIndex }); + await this.authorizeSpending({ + address, + limits: { + deployment: this.config.TRIAL_DEPLOYMENT_ALLOWANCE_AMOUNT, + fees: this.config.TRIAL_FEES_ALLOWANCE_AMOUNT + }, + expiration: add(new Date(), { days: this.config.TRIAL_ALLOWANCE_EXPIRATION_DAYS }) + }); + + return { address }; + } + + async createWallet({ addressIndex }: { addressIndex: number }) { + const hdPath = stringToPath(`${this.HD_PATH}/${addressIndex}`); + const wallet = await DirectSecp256k1HdWallet.fromMnemonic(this.config.MASTER_WALLET_MNEMONIC, { + prefix: this.PREFIX, + hdPaths: [hdPath] + }); + const [account] = await wallet.getAccounts(); + this.logger.debug({ event: "WALLET_CREATED", address: account.address }); + + return { address: account.address }; + } + + async authorizeSpending(options: SpendingAuthorizationOptions) { + try { + const masterWalletAddress = await this.getMasterWalletAddress(); + const messageParams = { + granter: masterWalletAddress, + grantee: options.address, + denom: this.config.TRIAL_ALLOWANCE_DENOM, + expiration: options.expiration + }; + const messages = [ + this.rpcMessageService.getGrantMsg({ + ...messageParams, + limit: options.limits.deployment + }), + this.rpcMessageService.getGrantBasicAllowanceMsg({ + ...messageParams, + limit: options.limits.fees + }) + ]; + const client = await this.getClient(); + const fee = await this.estimateFee(messages, this.config.TRIAL_ALLOWANCE_DENOM); + await client.signAndBroadcast(masterWalletAddress, messages, fee); + this.logger.debug({ event: "SPENDING_AUTHORIZED", address: options.address }); + } catch (error) { + if (error.message.includes("fee allowance already exists")) { + this.logger.debug({ event: "SPENDING_ALREADY_AUTHORIZED", address: options.address }); + } else { + error.message = `Failed to authorize spending for address ${options.address}: ${error.message}`; + this.logger.error(error); + throw error; + } + } + } + + private async estimateFee(messages: readonly EncodeObject[], denom: string) { + const client = await this.getClient(); + const address = await this.getMasterWalletAddress(); + const gasEstimation = await client.simulate(address, messages, "allowance grant"); + const estimatedGas = Math.round(gasEstimation * this.config.GAS_SAFETY_MULTIPLIER); + + return calculateFee(estimatedGas, GasPrice.fromString(`0.025${denom}`)); + } + + async refill(wallet: any) { + return wallet; + } + + private async getMasterWalletAddress() { + const masterWallet = await this.getMasterWallet(); + const [account] = await masterWallet.getAccounts(); + return account.address; + } + + private async getMasterWallet() { + if (!this.masterWallet) { + this.masterWallet = await DirectSecp256k1HdWallet.fromMnemonic(this.config.MASTER_WALLET_MNEMONIC, { prefix: this.PREFIX }); + } + + return this.masterWallet; + } + + private async getClient() { + if (!this.client) { + this.client = await SigningStargateClient.connectWithSigner(this.config.RPC_NODE_ENDPOINT, await this.getMasterWallet()); + } + return this.client; + } +} diff --git a/apps/api/src/console.ts b/apps/api/src/console.ts new file mode 100644 index 000000000..319e014ef --- /dev/null +++ b/apps/api/src/console.ts @@ -0,0 +1,21 @@ +import "reflect-metadata"; + +import { Command } from "commander"; +import { container } from "tsyringe"; + +import { WalletController } from "@src/billing/controllers/wallet/wallet.controller"; +import { PostgresMigratorService } from "@src/core"; + +const program = new Command(); + +program.name("API Console").description("CLI to run API commands").version("0.0.0"); + +program + .command("refill-wallets") + .description("Refill draining wallets") + .action(async () => { + await container.resolve(PostgresMigratorService).migrate(); + await container.resolve(WalletController).refillAll(); + }); + +program.parse(); diff --git a/apps/api/src/core/config/env.config.ts b/apps/api/src/core/config/env.config.ts new file mode 100644 index 000000000..bc453d59e --- /dev/null +++ b/apps/api/src/core/config/env.config.ts @@ -0,0 +1,14 @@ +import dotenv from "dotenv"; +import { z } from "zod"; + +dotenv.config({ path: ".env.local" }); +dotenv.config(); + +const envSchema = z.object({ + LOG_LEVEL: z.enum(["fatal", "error", "warn", "info", "debug", "trace"]).optional().default("info"), + LOG_FORMAT: z.enum(["json", "pretty"]).optional().default("json"), + // TODO: make required once billing is in prod + POSTGRES_DB_URI: z.string().optional() +}); + +export const envConfig = envSchema.parse(process.env); diff --git a/apps/api/src/core/config/index.ts b/apps/api/src/core/config/index.ts new file mode 100644 index 000000000..70d5f7bd2 --- /dev/null +++ b/apps/api/src/core/config/index.ts @@ -0,0 +1,5 @@ +import { envConfig } from "./env.config"; + +export const config = { + ...envConfig +}; diff --git a/apps/api/src/core/index.ts b/apps/api/src/core/index.ts new file mode 100644 index 000000000..6928ee035 --- /dev/null +++ b/apps/api/src/core/index.ts @@ -0,0 +1,3 @@ +export * from "./providers"; + +export * from "./services"; diff --git a/apps/api/src/core/providers/config.provider.ts b/apps/api/src/core/providers/config.provider.ts new file mode 100644 index 000000000..f8471d4cb --- /dev/null +++ b/apps/api/src/core/providers/config.provider.ts @@ -0,0 +1,9 @@ +import { container } from "tsyringe"; + +import { config } from "@src/core/config"; + +export const CORE_CONFIG = "CORE_CONFIG"; + +container.register(CORE_CONFIG, { useValue: config }); + +export type CoreConfig = typeof config; diff --git a/apps/api/src/core/providers/index.ts b/apps/api/src/core/providers/index.ts new file mode 100644 index 000000000..bf289a425 --- /dev/null +++ b/apps/api/src/core/providers/index.ts @@ -0,0 +1,2 @@ +export * from "./postgres.provider"; +export * from "./config.provider"; diff --git a/apps/api/src/core/providers/postgres.provider.ts b/apps/api/src/core/providers/postgres.provider.ts new file mode 100644 index 000000000..1f70af22e --- /dev/null +++ b/apps/api/src/core/providers/postgres.provider.ts @@ -0,0 +1,22 @@ +import { DefaultLogger } from "drizzle-orm/logger"; +import { drizzle } from "drizzle-orm/node-postgres"; +import { Pool } from "pg"; +import { container, inject } from "tsyringe"; + +import * as billingSchemas from "@src/billing/model-schemas"; +import { config } from "@src/core/config"; +import { PostgresLoggerService } from "@src/core/services/postgres-logger/postgres-logger.service"; + +const pool = new Pool({ + connectionString: config.POSTGRES_DB_URI +}); + +export const pgDatabase = drizzle(pool, { logger: new DefaultLogger({ writer: container.resolve(PostgresLoggerService) }), schema: billingSchemas }); + +export type ApiPgSchema = typeof billingSchemas; + +export const POSTGRES_DB = "POSTGRES_DB"; +container.register(POSTGRES_DB, { useValue: pgDatabase }); + +export const InjectPg = () => inject(POSTGRES_DB); +export type ApiPgDatabase = typeof pgDatabase; diff --git a/apps/api/src/core/services/contextual-logger/contextual-logger.service.ts b/apps/api/src/core/services/contextual-logger/contextual-logger.service.ts new file mode 100644 index 000000000..1e51c4b00 --- /dev/null +++ b/apps/api/src/core/services/contextual-logger/contextual-logger.service.ts @@ -0,0 +1,32 @@ +import { Bindings } from "pino"; +import { injectable } from "tsyringe"; + +import { LoggerService } from "@src/core/services/logger/logger.service"; +import { RequestStorageService } from "@src/core/services/request-storage/request-storage.service"; + +@injectable() +export class ContextualLoggerService extends LoggerService { + constructor(private readonly requestStorage: RequestStorageService) { + super(); + } + + setContext(bindings: Bindings) { + this.pino = this.pino.child(bindings); + } + + protected toLoggableInput(message: any) { + const { context } = this.requestStorage; + const requestId = context?.get("requestId"); + const loggableMessage = super.toLoggableInput(message); + + if (!requestId) { + return loggableMessage; + } + + if (typeof loggableMessage === "object" && !Array.isArray(loggableMessage)) { + return { requestId, ...loggableMessage }; + } + + return { requestId, msg: loggableMessage }; + } +} diff --git a/apps/api/src/core/services/http-logger/http-logger.service.ts b/apps/api/src/core/services/http-logger/http-logger.service.ts new file mode 100644 index 000000000..3fc7061ed --- /dev/null +++ b/apps/api/src/core/services/http-logger/http-logger.service.ts @@ -0,0 +1,29 @@ +import { Context, Next } from "hono"; +import { singleton } from "tsyringe"; + +import { ContextualLoggerService } from "@src/core/services"; +import type { HonoInterceptor } from "@src/core/types/hono-interceptor.type"; + +@singleton() +export class HttpLoggerService implements HonoInterceptor { + constructor(private readonly logger: ContextualLoggerService) { + logger.setContext({ context: "HTTP" }); + } + + intercept() { + return async (c: Context, next: Next) => { + const timer = performance.now(); + this.logger.info({ + method: c.req.method, + url: c.req.url + }); + + await next(); + + this.logger.info({ + status: c.res.status, + duration: `${(performance.now() - timer).toFixed(3)}ms` + }); + }; + } +} diff --git a/apps/api/src/core/services/index.ts b/apps/api/src/core/services/index.ts new file mode 100644 index 000000000..6412e53d5 --- /dev/null +++ b/apps/api/src/core/services/index.ts @@ -0,0 +1,6 @@ +export * from "@src/core/services/tx/tx.service"; +export * from "./logger/logger.service"; +export * from "./contextual-logger/contextual-logger.service"; +export * from "@src/core/services/http-logger/http-logger.service"; +export * from "./request-storage/request-storage.service"; +export * from "./postgres-migrator/postgres-migrator.service"; diff --git a/apps/api/src/core/services/logger/logger.service.ts b/apps/api/src/core/services/logger/logger.service.ts new file mode 100644 index 000000000..32052addc --- /dev/null +++ b/apps/api/src/core/services/logger/logger.service.ts @@ -0,0 +1,50 @@ +import pino, { Bindings, LoggerOptions } from "pino"; + +import { config } from "@src/core/config"; + +export class LoggerService { + protected pino: pino.Logger; + + readonly isPretty = config.LOG_FORMAT === "pretty"; + + constructor(bindings?: Bindings) { + const options: LoggerOptions = { level: config.LOG_LEVEL }; + + if (this.isPretty) { + options.transport = { + target: "pino-pretty" + }; + } + + this.pino = pino(options); + + if (bindings) { + this.pino = this.pino.child(bindings); + } + } + + info(message: any) { + message = this.toLoggableInput(message); + return this.pino.info(message); + } + + error(message: any) { + this.pino.error(this.toLoggableInput(message)); + } + + warn(message: any) { + return this.pino.warn(this.toLoggableInput(message)); + } + + debug(message: any) { + return this.pino.debug(this.toLoggableInput(message)); + } + + protected toLoggableInput(message: any) { + if (message instanceof Error) { + return message.stack; + } + + return message; + } +} diff --git a/apps/api/src/core/services/postgres-logger/postgres-logger.service.ts b/apps/api/src/core/services/postgres-logger/postgres-logger.service.ts new file mode 100644 index 000000000..88202d650 --- /dev/null +++ b/apps/api/src/core/services/postgres-logger/postgres-logger.service.ts @@ -0,0 +1,22 @@ +import { LogWriter } from "drizzle-orm/logger"; +import { format } from "sql-formatter"; +import { singleton } from "tsyringe"; + +import { ContextualLoggerService } from "@src/core/services/contextual-logger/contextual-logger.service"; + +@singleton() +export class PostgresLoggerService implements LogWriter { + constructor(private readonly logger: ContextualLoggerService) { + logger.setContext({ context: "POSTGRES" }); + } + + write(message: string) { + let formatted = message.replace(/^Query: /, ""); + + if (this.logger.isPretty) { + formatted = format(message, { language: "postgresql" }); + } + + this.logger.debug(formatted); + } +} diff --git a/apps/api/src/core/services/postgres-migrator/postgres-migrator.service.ts b/apps/api/src/core/services/postgres-migrator/postgres-migrator.service.ts new file mode 100644 index 000000000..817ccf5b9 --- /dev/null +++ b/apps/api/src/core/services/postgres-migrator/postgres-migrator.service.ts @@ -0,0 +1,19 @@ +import { migrate } from "drizzle-orm/node-postgres/migrator"; +import { inject, singleton } from "tsyringe"; + +import { ApiPgDatabase, CORE_CONFIG, CoreConfig, InjectPg } from "@src/core/providers"; + +@singleton() +export class PostgresMigratorService { + constructor( + @InjectPg() private readonly pg: ApiPgDatabase, + @inject(CORE_CONFIG) private readonly config: CoreConfig + ) {} + + async migrate() { + // TODO: remove condition once billing is in prod + if (this.config.POSTGRES_DB_URI) { + return migrate(this.pg, { migrationsFolder: "./drizzle" }); + } + } +} diff --git a/apps/api/src/core/services/request-storage/request-storage.service.ts b/apps/api/src/core/services/request-storage/request-storage.service.ts new file mode 100644 index 000000000..19444b79b --- /dev/null +++ b/apps/api/src/core/services/request-storage/request-storage.service.ts @@ -0,0 +1,35 @@ +import { Context, Next } from "hono"; +import { AsyncLocalStorage } from "node:async_hooks"; +import { singleton } from "tsyringe"; +import { v4 as uuid } from "uuid"; + +import type { HonoInterceptor } from "@src/core/types/hono-interceptor.type"; + +@singleton() +export class RequestStorageService implements HonoInterceptor { + private readonly CONTEXT_KEY = "CONTEXT"; + + private readonly storage = new AsyncLocalStorage>(); + + get context() { + return this.storage.getStore()?.get(this.CONTEXT_KEY); + } + + intercept() { + return async (c: Context, next: Next) => { + const requestId = c.req.header("X-Request-Id") || uuid(); + c.set("requestId", requestId); + + await this.runWithContext(c, next); + }; + } + + private async runWithContext(context: Context, cb: () => Promise) { + return await new Promise((resolve, reject) => { + this.storage.run(new Map(), () => { + this.storage.getStore().set(this.CONTEXT_KEY, context); + cb().then(resolve).catch(reject); + }); + }); + } +} diff --git a/apps/api/src/core/services/tx/tx.service.ts b/apps/api/src/core/services/tx/tx.service.ts new file mode 100644 index 000000000..803af9418 --- /dev/null +++ b/apps/api/src/core/services/tx/tx.service.ts @@ -0,0 +1,44 @@ +import type { ExtractTablesWithRelations } from "drizzle-orm"; +import type { NodePgQueryResultHKT } from "drizzle-orm/node-postgres"; +import type { PgTransaction } from "drizzle-orm/pg-core"; +import { AsyncLocalStorage } from "node:async_hooks"; +import { container, singleton } from "tsyringe"; + +import { ApiPgDatabase, ApiPgSchema, InjectPg } from "@src/core/providers/postgres.provider"; + +type TxType = "PG_TX"; + +@singleton() +export class TxService { + private readonly storage = new AsyncLocalStorage>>>(); + + constructor(@InjectPg() private readonly pg: ApiPgDatabase) {} + + async transaction(cb: () => Promise) { + await this.pg.transaction(async tx => { + await new Promise((resolve, reject) => { + this.storage.run(new Map(), () => { + this.storage.getStore().set("PG_TX", tx); + cb().then(resolve).catch(reject); + }); + }); + }); + } + + getPgTx() { + return this.storage.getStore()?.get("PG_TX"); + } +} + +export function WithTransaction() { + return function (target: unknown, propertyKey: string, descriptor: PropertyDescriptor) { + const originalMethod = descriptor.value; + + descriptor.value = async function (...args: unknown[]) { + const txManager = container.resolve(TxService); + return txManager.transaction(() => originalMethod.apply(this, args)); + }; + + return descriptor; + }; +} diff --git a/apps/api/src/core/types/hono-interceptor.type.ts b/apps/api/src/core/types/hono-interceptor.type.ts new file mode 100644 index 000000000..ae3a32e49 --- /dev/null +++ b/apps/api/src/core/types/hono-interceptor.type.ts @@ -0,0 +1,5 @@ +import type { MiddlewareHandler } from "hono"; + +export interface HonoInterceptor { + intercept(): MiddlewareHandler; +} diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts index 4da9c5f15..041d547ea 100644 --- a/apps/api/src/index.ts +++ b/apps/api/src/index.ts @@ -1,3 +1,11 @@ +import "reflect-metadata"; + +import { container } from "tsyringe"; + import { initApp } from "@src/app"; +import { PostgresMigratorService } from "@src/core"; -initApp(); +container + .resolve(PostgresMigratorService) + .migrate() + .then(() => initApp()); diff --git a/apps/api/test/functional/create-wallet.spec.ts b/apps/api/test/functional/create-wallet.spec.ts new file mode 100644 index 000000000..85e2b5b3c --- /dev/null +++ b/apps/api/test/functional/create-wallet.spec.ts @@ -0,0 +1,47 @@ +import { faker } from "@faker-js/faker"; +import { eq } from "drizzle-orm"; +import { container } from "tsyringe"; + +import { app, initDb } from "@src/app"; +import { USER_WALLET_SCHEMA, UserWalletSchema } from "@src/billing/providers"; +import { ApiPgDatabase, POSTGRES_DB } from "@src/core"; +import { closeConnections } from "@src/db/dbConnection"; + +describe("wallets", () => { + const schema = container.resolve(USER_WALLET_SCHEMA); + const db = container.resolve(POSTGRES_DB); + const userWalletsTable = db.query.userWalletSchema; + + beforeAll(async () => { + await initDb(); + }); + + afterAll(async () => { + await closeConnections(); + }); + + afterEach(async () => { + await db.delete(schema); + }); + + describe("POST /v1/wallets", () => { + it("should create a wallet for a user", async () => { + jest.setTimeout(10000); + + const userId = faker.string.uuid(); + const res = await app.request("/v1/wallets", { + method: "POST", + body: JSON.stringify({ userId }), + headers: new Headers({ "Content-Type": "application/json" }) + }); + const userWallet = await userWalletsTable.findFirst({ where: eq(schema.userId, userId) }); + + expect(res.status).toBe(200); + expect(userWallet).toMatchObject({ + userId, + address: expect.any(String), + creditAmount: "0.00" + }); + }); + }); +}); diff --git a/apps/api/test/setup-functional-tests.ts b/apps/api/test/setup-functional-tests.ts index 6a358f19d..d5e7ec147 100644 --- a/apps/api/test/setup-functional-tests.ts +++ b/apps/api/test/setup-functional-tests.ts @@ -1,3 +1,12 @@ +import "reflect-metadata"; + import dotenv from "dotenv"; +import { container } from "tsyringe"; + +import { PostgresMigratorService } from "@src/core"; dotenv.config({ path: ".env.functional.test" }); + +beforeAll(async () => { + await container.resolve(PostgresMigratorService).migrate(); +}); diff --git a/docker/bin/check-postgres-init.sh b/docker/bin/check-postgres-init.sh old mode 100755 new mode 100644 diff --git a/docker/bin/prepare-and-seed-postgres.sh b/docker/bin/prepare-and-seed-postgres.sh old mode 100755 new mode 100644 diff --git a/package-lock.json b/package-lock.json index e53f41dff..498a4fde9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,10 @@ "./packages/*", "./apps/*" ], + "dependencies": { + "drizzle-orm": "^0.31.2", + "pg": "^8.11.6" + }, "devDependencies": { "@akashnetwork/dev-config": "*", "cross-env": "^7.0.3", @@ -38,27 +42,34 @@ "@hono/zod-openapi": "0.9.5", "@octokit/rest": "^18.12.0", "@sentry/node": "^7.55.2", + "@supercharge/promise-pool": "^3.2.0", "axios": "^0.27.2", + "commander": "^12.1.0", "cosmjs-types": "^0.5.0", "date-fns": "^2.29.2", "date-fns-tz": "^1.3.6", "dotenv": "^12.0.4", + "drizzle-orm": "^0.31.2", "hono": "3.12.0", "human-interval": "^2.0.1", "js-sha256": "^0.9.0", "lodash": "^4.17.21", "markdown-to-txt": "^2.0.1", "memory-cache": "^0.2.0", + "node-dependency-injection": "^3.1.2", "node-fetch": "^2.6.1", - "pg": "^8.7.3", + "pg": "^8.12.0", "pg-hstore": "^2.3.4", + "pino": "^9.2.0", + "pino-pretty": "^11.2.1", "protobufjs": "^6.11.2", "semver": "^7.3.8", "sequelize": "^6.21.3", "sequelize-typescript": "^2.1.5", + "sql-formatter": "^15.3.2", "stripe": "^10.14.0", - "uuid": "^9.0.1", - "zod": "^3.22.4" + "tsyringe": "^4.8.0", + "uuid": "^9.0.1" }, "devDependencies": { "@akashnetwork/dev-config": "*", @@ -68,11 +79,12 @@ "@types/memory-cache": "^0.2.2", "@types/node": "20.14.0", "@types/node-fetch": "^2.6.2", - "@types/pg": "^8.6.5", + "@types/pg": "^8.11.6", "@types/semver": "^7.5.2", "@types/uuid": "^8.3.1", "@typescript-eslint/eslint-plugin": "^7.12.0", "alias-hq": "^5.1.6", + "drizzle-kit": "^0.22.7", "eslint": "^8.57.0", "eslint-config-next": "^14.2.3", "eslint-plugin-simple-import-sort": "^12.1.0", @@ -91,6 +103,109 @@ "webpack-node-externals": "^3.0.0" } }, + "apps/api/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "engines": { + "node": ">=18" + } + }, + "apps/api/node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "engines": { + "node": ">=14.0.0" + } + }, + "apps/api/node_modules/pino": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-9.2.0.tgz", + "integrity": "sha512-g3/hpwfujK5a4oVbaefoJxezLzsDgLcNJeITvC6yrfwYeT9la+edCK42j5QpEQSQCZgTKapXvnQIdgZwvRaZug==", + "dependencies": { + "atomic-sleep": "^1.0.0", + "fast-redact": "^3.1.1", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^1.2.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^3.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^3.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "apps/api/node_modules/pino-abstract-transport": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz", + "integrity": "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==", + "dependencies": { + "readable-stream": "^4.0.0", + "split2": "^4.0.0" + } + }, + "apps/api/node_modules/pino-std-serializers": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz", + "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==" + }, + "apps/api/node_modules/process-warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", + "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==" + }, + "apps/api/node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "apps/api/node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "engines": { + "node": ">= 12.13.0" + } + }, + "apps/api/node_modules/sonic-boom": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.0.1.tgz", + "integrity": "sha512-hTSD/6JMLyT4r9zeof6UtuBDpjJ9sO08/nmS5djaA9eozT9oOlNdpXSnzcgj4FTqpk3nkLrs61l4gip9r1HCrQ==", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, + "apps/api/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "apps/api/node_modules/thread-stream": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", + "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==", + "dependencies": { + "real-require": "^0.2.0" + } + }, "apps/deploy-web": { "name": "akash-console", "version": "2.10.0", @@ -2367,6 +2482,33 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.24.7.tgz", + "integrity": "sha512-YqXjrk4C+a1kZjewqt+Mmu2UuV1s07y8kqcUf4qYLnoqemhR4gRQikhdAhSVJioMjVTu6Mo6pAbaypEA3jY6fw==", + "dependencies": { + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.1", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@babel/plugin-transform-shorthand-properties": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.7.tgz", @@ -4495,6 +4637,808 @@ "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" }, + "node_modules/@esbuild-kit/core-utils": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@esbuild-kit/core-utils/-/core-utils-3.3.2.tgz", + "integrity": "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==", + "dev": true, + "dependencies": { + "esbuild": "~0.18.20", + "source-map-support": "^0.5.21" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/darwin-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/@esbuild-kit/core-utils/node_modules/esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/@esbuild-kit/esm-loader": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/@esbuild-kit/esm-loader/-/esm-loader-2.6.5.tgz", + "integrity": "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==", + "dev": true, + "dependencies": { + "@esbuild-kit/core-utils": "^3.3.2", + "get-tsconfig": "^4.7.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", + "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", + "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", + "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", + "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", + "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", + "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", + "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", + "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", + "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", + "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", + "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", + "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", + "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", + "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", + "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", + "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", + "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", + "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", + "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", + "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", + "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", + "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -5633,7 +6577,7 @@ "zod": "3.*" } }, - "node_modules/@hono/zod-validator": { + "node_modules/@hono/zod-openapi/node_modules/@hono/zod-validator": { "version": "0.1.11", "resolved": "https://registry.npmjs.org/@hono/zod-validator/-/zod-validator-0.1.11.tgz", "integrity": "sha512-PQXeHUP0+36qpRt8yfeD7N2jbK3ETlGvSN6dMof/HwUC/APRokQRjpXZm4rrlG71Ft0aWE01+Bm4XejqPie5Uw==", @@ -14056,6 +15000,14 @@ "resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-1.54.2.tgz", "integrity": "sha512-R1PwtDvUfs99cAjfuQ/WpwJ3c92+DAMy9xGApjqlWQMj0FKQabUAys2swfTRNzuYAYJh7NqK2dzcYVNkKLEKUg==" }, + "node_modules/@supercharge/promise-pool": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@supercharge/promise-pool/-/promise-pool-3.2.0.tgz", + "integrity": "sha512-pj0cAALblTZBPtMltWOlZTQSLT07jIaFNeM8TWoJD1cQMgDB9mcMlVMoetiB35OzNJpqQ2b+QEtwiR9f20mADg==", + "engines": { + "node": ">=8" + } + }, "node_modules/@surma/rollup-plugin-off-main-thread": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", @@ -14681,7 +15633,7 @@ "version": "8.11.6", "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.6.tgz", "integrity": "sha512-/2WmmBXHLsfRqzfHW7BNZ8SbYzE8OSk7i3WjFYvfgRHj7S1xj+16Je5fUKv3lVdVzk/zn9TXOqf+avFCFIE0yQ==", - "dev": true, + "devOptional": true, "dependencies": { "@types/node": "*", "pg-protocol": "*", @@ -16488,6 +17440,17 @@ } } }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/abstract-level": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/abstract-level/-/abstract-level-1.0.4.tgz", @@ -17899,6 +18862,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/builtins": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", + "integrity": "sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==" + }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -18463,6 +19431,15 @@ "node": ">=12" } }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "optional": true, + "engines": { + "node": ">=0.8" + } + }, "node_modules/clone-deep": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", @@ -18579,8 +19556,7 @@ "node_modules/colorette": { "version": "2.0.20", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" }, "node_modules/colors": { "version": "1.4.0", @@ -18676,6 +19652,17 @@ "resolved": "apps/provider-proxy", "link": true }, + "node_modules/console.table": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/console.table/-/console.table-0.10.0.tgz", + "integrity": "sha512-dPyZofqggxuvSf7WXvNjuRfnsOk1YazkVP8FdxH4tcH2c37wc79/Yl6Bhr7Lsu00KMgy2ql/qCMuNu8xctZM8g==", + "dependencies": { + "easy-table": "1.1.0" + }, + "engines": { + "node": "> 0.10" + } + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -19555,6 +20542,14 @@ "date-fns": ">=2.0.0" } }, + "node_modules/dateformat": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", + "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==", + "engines": { + "node": "*" + } + }, "node_modules/de-indent": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", @@ -19664,6 +20659,18 @@ "node": ">=0.10.0" } }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "optional": true, + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -19969,6 +20976,11 @@ "node": ">=8" } }, + "node_modules/discontinuous-range": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz", + "integrity": "sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==" + }, "node_modules/dlv": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", @@ -20008,6 +21020,133 @@ "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.6.tgz", "integrity": "sha512-iGCHkfUc5kFekGiqhe8B/mdaurD+lakO9txNnTvKtA6PISrw86LgqHvRzWYPyoE2Ph5aMIrCw9/uko6XHTKCwA==" }, + "node_modules/drizzle-kit": { + "version": "0.22.7", + "resolved": "https://registry.npmjs.org/drizzle-kit/-/drizzle-kit-0.22.7.tgz", + "integrity": "sha512-9THPCb2l1GPt7wxhws9LvTR0YG565ZlVgTuqGMwjs590Kch1pXu4GyjEArVijSF5m0OBj3qgdeKmuJXhKXgWFw==", + "dev": true, + "dependencies": { + "@esbuild-kit/esm-loader": "^2.5.5", + "esbuild": "^0.19.7", + "esbuild-register": "^3.5.0" + }, + "bin": { + "drizzle-kit": "bin.cjs" + } + }, + "node_modules/drizzle-orm": { + "version": "0.31.2", + "resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.31.2.tgz", + "integrity": "sha512-QnenevbnnAzmbNzQwbhklvIYrDE8YER8K7kSrAWQSV1YvFCdSQPzj+jzqRdTSsV2cDqSpQ0NXGyL1G9I43LDLg==", + "peerDependencies": { + "@aws-sdk/client-rds-data": ">=3", + "@cloudflare/workers-types": ">=3", + "@electric-sql/pglite": ">=0.1.1", + "@libsql/client": "*", + "@neondatabase/serverless": ">=0.1", + "@op-engineering/op-sqlite": ">=2", + "@opentelemetry/api": "^1.4.1", + "@planetscale/database": ">=1", + "@tidbcloud/serverless": "*", + "@types/better-sqlite3": "*", + "@types/pg": "*", + "@types/react": ">=18", + "@types/sql.js": "*", + "@vercel/postgres": ">=0.8.0", + "@xata.io/client": "*", + "better-sqlite3": ">=7", + "bun-types": "*", + "expo-sqlite": ">=13.2.0", + "knex": "*", + "kysely": "*", + "mysql2": ">=2", + "pg": ">=8", + "postgres": ">=3", + "react": ">=18", + "sql.js": ">=1", + "sqlite3": ">=5" + }, + "peerDependenciesMeta": { + "@aws-sdk/client-rds-data": { + "optional": true + }, + "@cloudflare/workers-types": { + "optional": true + }, + "@electric-sql/pglite": { + "optional": true + }, + "@libsql/client": { + "optional": true + }, + "@neondatabase/serverless": { + "optional": true + }, + "@op-engineering/op-sqlite": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@planetscale/database": { + "optional": true + }, + "@tidbcloud/serverless": { + "optional": true + }, + "@types/better-sqlite3": { + "optional": true + }, + "@types/pg": { + "optional": true + }, + "@types/react": { + "optional": true + }, + "@types/sql.js": { + "optional": true + }, + "@vercel/postgres": { + "optional": true + }, + "@xata.io/client": { + "optional": true + }, + "better-sqlite3": { + "optional": true + }, + "bun-types": { + "optional": true + }, + "expo-sqlite": { + "optional": true + }, + "knex": { + "optional": true + }, + "kysely": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "pg": { + "optional": true + }, + "postgres": { + "optional": true + }, + "react": { + "optional": true + }, + "sql.js": { + "optional": true + }, + "sqlite3": { + "optional": true + } + } + }, "node_modules/duplexer": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", @@ -20091,6 +21230,14 @@ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, + "node_modules/easy-table": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.1.0.tgz", + "integrity": "sha512-oq33hWOSSnl2Hoh00tZWaIPi1ievrD9aFG82/IgjlycAnW9hHx5PkJiXpxPsgEE+H7BsbVQXFVFST8TEXS6/pA==", + "optionalDependencies": { + "wcwidth": ">=1.0.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -20367,6 +21514,56 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/esbuild": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" + } + }, + "node_modules/esbuild-register": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.5.0.tgz", + "integrity": "sha512-+4G/XmakeBAsvJuDugJvtyF1x+XJT4FMocynNpxrvEBViirpfUn2PgNpCHedfWhF4WokNsO/OvMKrmJOIJsI5A==", + "dev": true, + "dependencies": { + "debug": "^4.3.4" + }, + "peerDependencies": { + "esbuild": ">=0.12 <1" + } + }, "node_modules/escalade": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", @@ -21014,6 +22211,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", + "optional": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", @@ -21261,6 +22468,14 @@ "@ethersproject/wordlists": "5.7.0" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "engines": { + "node": ">=6" + } + }, "node_modules/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", @@ -21547,6 +22762,11 @@ "node": ">=0.10.0" } }, + "node_modules/fast-copy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.2.tgz", + "integrity": "sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -22201,7 +23421,7 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, + "devOptional": true, "engines": { "node": ">=8.0.0" } @@ -22211,6 +23431,17 @@ "resolved": "https://registry.npmjs.org/get-port-please/-/get-port-please-3.1.2.tgz", "integrity": "sha512-Gxc29eLs1fbn6LQ4jSU4vXjlwyZhF5HsGuMAa7gqBP4Rw4yxxltyDUuF5MBclFzDTXO+ACchGQoeela4DSfzdQ==" }, + "node_modules/get-stdin": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", + "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -22259,6 +23490,13 @@ "node": ">=0.10.0" } }, + "node_modules/getopts": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/getopts/-/getopts-2.3.0.tgz", + "integrity": "sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA==", + "optional": true, + "peer": true + }, "node_modules/github-buttons": { "version": "2.28.0", "resolved": "https://registry.npmjs.org/github-buttons/-/github-buttons-2.28.0.tgz", @@ -22650,6 +23888,11 @@ "he": "bin/he" } }, + "node_modules/help-me": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", + "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==" + }, "node_modules/hexoid": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", @@ -23110,7 +24353,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", - "dev": true, + "devOptional": true, "engines": { "node": ">= 0.10" } @@ -25689,6 +26932,14 @@ } } }, + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "engines": { + "node": ">=10" + } + }, "node_modules/js-crypto-env": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/js-crypto-env/-/js-crypto-env-0.3.2.tgz", @@ -26290,6 +27541,123 @@ "node": ">=6" } }, + "node_modules/knex": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/knex/-/knex-3.1.0.tgz", + "integrity": "sha512-GLoII6hR0c4ti243gMs5/1Rb3B+AjwMOfjYm97pu0FOQa7JH56hgBxYf5WK2525ceSbBY1cjeZ9yk99GPMB6Kw==", + "optional": true, + "peer": true, + "dependencies": { + "colorette": "2.0.19", + "commander": "^10.0.0", + "debug": "4.3.4", + "escalade": "^3.1.1", + "esm": "^3.2.25", + "get-package-type": "^0.1.0", + "getopts": "2.3.0", + "interpret": "^2.2.0", + "lodash": "^4.17.21", + "pg-connection-string": "2.6.2", + "rechoir": "^0.8.0", + "resolve-from": "^5.0.0", + "tarn": "^3.0.2", + "tildify": "2.0.0" + }, + "bin": { + "knex": "bin/cli.js" + }, + "engines": { + "node": ">=16" + }, + "peerDependenciesMeta": { + "better-sqlite3": { + "optional": true + }, + "mysql": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-native": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "tedious": { + "optional": true + } + } + }, + "node_modules/knex/node_modules/colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", + "optional": true, + "peer": true + }, + "node_modules/knex/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "optional": true, + "peer": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/knex/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "optional": true, + "peer": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/knex/node_modules/pg-connection-string": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", + "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==", + "optional": true, + "peer": true + }, + "node_modules/knex/node_modules/rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "optional": true, + "peer": true, + "dependencies": { + "resolve": "^1.20.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/knex/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "optional": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, "node_modules/language-subtag-registry": { "version": "0.3.23", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", @@ -27802,6 +29170,11 @@ "integrity": "sha512-2I8/T3X/hLxB2oPHgqcNYUVdA/ZEFShT7IAujifIPMfKkNbLOqY8XCoyHCXrsdjb36dW9MwoTwBCFpXKMwNwaQ==", "peer": true }, + "node_modules/moo": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz", + "integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==" + }, "node_modules/motion": { "version": "10.16.2", "resolved": "https://registry.npmjs.org/motion/-/motion-10.16.2.tgz", @@ -27935,6 +29308,32 @@ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" }, + "node_modules/nearley": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.20.1.tgz", + "integrity": "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==", + "dependencies": { + "commander": "^2.19.0", + "moo": "^0.5.0", + "railroad-diagrams": "^1.0.0", + "randexp": "0.4.6" + }, + "bin": { + "nearley-railroad": "bin/nearley-railroad.js", + "nearley-test": "bin/nearley-test.js", + "nearley-unparse": "bin/nearley-unparse.js", + "nearleyc": "bin/nearleyc.js" + }, + "funding": { + "type": "individual", + "url": "https://nearley.js.org/#give-to-nearley" + } + }, + "node_modules/nearley/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -28289,6 +29688,154 @@ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==" }, + "node_modules/node-dependency-injection": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/node-dependency-injection/-/node-dependency-injection-3.1.2.tgz", + "integrity": "sha512-Nl1pSjjKT+Sx7jTt0x3Nh+X4zOa5lieNcq5ynW+ro9KusXjzkAz2TSVuSl5rcQdSiXyt6UZNgslUGfoJx/Nkpw==", + "dependencies": { + "@babel/plugin-transform-runtime": "^7.17.10", + "@babel/runtime": "^7.17.9", + "@typescript-eslint/typescript-estree": "^5.23.0", + "chalk": "^4.1.0", + "commander": "^8.3.0", + "console.table": "^0.10.0", + "js-yaml": "^4.0.0", + "json5": "^2.2.2", + "validate-npm-package-name": "^3.0.0" + }, + "bin": { + "ndi": "dist/bin/ndi.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/node-dependency-injection/node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/node-dependency-injection/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/node-dependency-injection/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/node-dependency-injection/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/node-dependency-injection/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/node-dependency-injection/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/node-dependency-injection/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/node-dependency-injection/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/node-dependency-injection/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/node-dependency-injection/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/node-dir": { "version": "0.1.17", "resolved": "https://registry.npmjs.org/node-dir/-/node-dir-0.1.17.tgz", @@ -28827,7 +30374,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", - "dev": true + "devOptional": true }, "node_modules/ofetch": { "version": "1.3.4", @@ -29239,7 +30786,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/pg-numeric/-/pg-numeric-1.0.2.tgz", "integrity": "sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==", - "dev": true, + "devOptional": true, "engines": { "node": ">=4" } @@ -29261,7 +30808,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-4.0.2.tgz", "integrity": "sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==", - "dev": true, + "devOptional": true, "dependencies": { "pg-int8": "1.0.1", "pg-numeric": "1.0.2", @@ -29414,6 +30961,78 @@ "split2": "^4.0.0" } }, + "node_modules/pino-pretty": { + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-11.2.1.tgz", + "integrity": "sha512-O05NuD9tkRasFRWVaF/uHLOvoRDFD7tb5VMertr78rbsYFjYp48Vg3477EshVAF5eZaEw+OpDl/tu+B0R5o+7g==", + "dependencies": { + "colorette": "^2.0.7", + "dateformat": "^4.6.3", + "fast-copy": "^3.0.2", + "fast-safe-stringify": "^2.1.1", + "help-me": "^5.0.0", + "joycon": "^3.1.1", + "minimist": "^1.2.6", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^1.0.0", + "pump": "^3.0.0", + "readable-stream": "^4.0.0", + "secure-json-parse": "^2.4.0", + "sonic-boom": "^4.0.1", + "strip-json-comments": "^3.1.1" + }, + "bin": { + "pino-pretty": "bin.js" + } + }, + "node_modules/pino-pretty/node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/pino-pretty/node_modules/pino-abstract-transport": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz", + "integrity": "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==", + "dependencies": { + "readable-stream": "^4.0.0", + "split2": "^4.0.0" + } + }, + "node_modules/pino-pretty/node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/pino-pretty/node_modules/sonic-boom": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.0.1.tgz", + "integrity": "sha512-hTSD/6JMLyT4r9zeof6UtuBDpjJ9sO08/nmS5djaA9eozT9oOlNdpXSnzcgj4FTqpk3nkLrs61l4gip9r1HCrQ==", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, + "node_modules/pino-pretty/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/pino-std-serializers": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-4.0.0.tgz", @@ -29805,7 +31424,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.2.tgz", "integrity": "sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==", - "dev": true, + "devOptional": true, "engines": { "node": ">=12" } @@ -29814,7 +31433,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz", "integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==", - "dev": true, + "devOptional": true, "dependencies": { "obuf": "~1.1.2" }, @@ -29826,7 +31445,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-2.1.0.tgz", "integrity": "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==", - "dev": true, + "devOptional": true, "engines": { "node": ">=12" } @@ -29835,7 +31454,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz", "integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==", - "dev": true, + "devOptional": true, "engines": { "node": ">=12" } @@ -29844,7 +31463,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/postgres-range/-/postgres-range-1.1.4.tgz", "integrity": "sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==", - "dev": true + "devOptional": true }, "node_modules/preact": { "version": "10.22.0", @@ -30415,6 +32034,11 @@ "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz", "integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==" }, + "node_modules/railroad-diagrams": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz", + "integrity": "sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==" + }, "node_modules/rainbow-sprinkles": { "version": "0.17.1", "resolved": "https://registry.npmjs.org/rainbow-sprinkles/-/rainbow-sprinkles-0.17.1.tgz", @@ -30425,6 +32049,18 @@ "@vanilla-extract/dynamic": "^2" } }, + "node_modules/randexp": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz", + "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==", + "dependencies": { + "discontinuous-range": "1.0.0", + "ret": "~0.1.10" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -31293,7 +32929,6 @@ "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true, "engines": { "node": ">=0.12" } @@ -31695,6 +33330,11 @@ "pbts": "bin/pbts" } }, + "node_modules/secure-json-parse": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", + "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==" + }, "node_modules/secure-random": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/secure-random/-/secure-random-1.1.2.tgz", @@ -32493,6 +34133,19 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, + "node_modules/sql-formatter": { + "version": "15.3.2", + "resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-15.3.2.tgz", + "integrity": "sha512-pNxSMf5DtwhpZ8gUcOGCGZIWtCcyAUx9oLgAtlO4ag7DvlfnETL0BGqXaISc84pNrXvTWmt8Wal1FWKxdTsL3Q==", + "dependencies": { + "argparse": "^2.0.1", + "get-stdin": "=8.0.0", + "nearley": "^2.20.1" + }, + "bin": { + "sql-formatter": "bin/sql-formatter-cli.cjs" + } + }, "node_modules/stack-utils": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", @@ -33302,6 +34955,16 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/tarn": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tarn/-/tarn-3.0.2.tgz", + "integrity": "sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==", + "optional": true, + "peer": true, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/temp": { "version": "0.8.4", "resolved": "https://registry.npmjs.org/temp/-/temp-0.8.4.tgz", @@ -33640,6 +35303,16 @@ "xtend": "~2.1.1" } }, + "node_modules/tildify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tildify/-/tildify-2.0.0.tgz", + "integrity": "sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==", + "optional": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, "node_modules/tiny-secp256k1": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.1.6.tgz", @@ -34149,6 +35822,41 @@ } } }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/tsyringe": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/tsyringe/-/tsyringe-4.8.0.tgz", + "integrity": "sha512-YB1FG+axdxADa3ncEtRnQCFq/M0lALGLxSZeVNbTU8NqhOVc51nnv2CISTcvc1kyv6EGPtXVr0v6lWeDxiijOA==", + "dependencies": { + "tslib": "^1.9.3" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/tsyringe/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -35103,6 +36811,14 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, + "node_modules/validate-npm-package-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", + "integrity": "sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==", + "dependencies": { + "builtins": "^1.0.3" + } + }, "node_modules/validator": { "version": "13.12.0", "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", @@ -35377,6 +37093,15 @@ "node": ">=10.13.0" } }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "optional": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", diff --git a/package.json b/package.json index 9621fa1ea..eeb7d7d45 100644 --- a/package.json +++ b/package.json @@ -37,5 +37,9 @@ "volta": { "node": "20.14.0", "npm": "10.7.0" + }, + "dependencies": { + "drizzle-orm": "^0.31.2", + "pg": "^8.11.6" } } diff --git a/packages/dev-config/.eslintrc.base.js b/packages/dev-config/.eslintrc.base.js index 88b665e7b..a8d461aa5 100644 --- a/packages/dev-config/.eslintrc.base.js +++ b/packages/dev-config/.eslintrc.base.js @@ -11,7 +11,7 @@ module.exports = { "simple-import-sort/imports": [ "error", { - groups: [["^react", "^(?!@src)@?\\w"], ["^@src", "^\\.\\.(?!/?$)", "^\\.\\./?$", "^\\./(?=.*/)(?!/?$)", "^\\.(?!/?$)", "^\\./?$"], ["^\\u0000"]] + groups: [["^\\u0000"], ["^react", "^(?!@src)@?\\w"], ["^@src", "^\\.\\.(?!/?$)", "^\\.\\./?$", "^\\./(?=.*/)(?!/?$)", "^\\.(?!/?$)", "^\\./?$"]] } ], "space-infix-ops": ["error", { int32Hint: false }],