diff --git a/AGENTS.md b/AGENTS.md index 60da525..8625fb3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,7 +4,7 @@ Guidelines for AI coding agents working on this repository. ## Project Overview -TypeScript-based scraper for Russian supermarkets (Magnit). Uses Playwright for sessions, Axios for API, PostgreSQL with Prisma ORM. +TypeScript-based scraper for Russian supermarkets (Magnit). Uses Playwright for sessions, Axios for API, PostgreSQL with Drizzle ORM. ## Build & Run Commands @@ -20,12 +20,13 @@ pnpm enrich # Run product enrichment pnpm test-db # Test database connection ``` -### Prisma Commands +### Drizzle Commands ```bash -pnpm prisma:generate # Generate client after schema changes -pnpm prisma:migrate # Create and apply migrations -pnpm prisma:studio # Open database GUI +pnpm db:generate # Generate migration files +pnpm db:migrate # Apply migrations +pnpm db:push # Push schema changes directly (dev only) +pnpm db:studio # Open database GUI ``` ### Running Scripts Directly @@ -45,13 +46,15 @@ No test framework configured. Manual testing via `pnpm test-db`, `pnpm dev`, Pri 1. External packages first, then internal modules 2. **Always include `.js` extension** for local imports (ESM) -3. Use named imports from Prisma client +3. Use named imports from Drizzle schema ```typescript import { chromium, Browser } from 'playwright'; import axios from 'axios'; import { Logger } from '../../../utils/logger.js'; -import { PrismaClient } from '../../../../generated/prisma/client.js'; +import { db } from '../../../config/database.js'; +import { products, stores, categories } from '../../../db/schema.js'; +import { eq, and, asc } from 'drizzle-orm'; ``` ### Naming Conventions @@ -113,15 +116,19 @@ Logger.debug('Debug'); // Only when DEBUG=true ### Services Pattern -- Services receive `PrismaClient` via constructor (DI) +- Services receive `db` (Drizzle instance) via constructor (DI) - Use `getOrCreate` for idempotent operations -- Never call Prisma directly from scrapers +- Never call Drizzle directly from scrapers ### Database Patterns -- Upsert via composite unique `(externalId, storeId)` +- Upsert via composite unique constraint on `(externalId, storeId)` - Batch processing: 50 items per batch -- Prices: Float (rubles), converted from kopecks +- Prices: Decimal (rubles), stored as decimal type +- Use `.select().from().where()` for queries +- Use `.insert().values()` for inserts +- Use `.update().set().where()` for updates +- Use `.delete().where()` for deletes ### Comments diff --git a/drizzle.config.ts b/drizzle.config.ts new file mode 100644 index 0000000..2b03bf4 --- /dev/null +++ b/drizzle.config.ts @@ -0,0 +1,11 @@ +import 'dotenv/config'; +import { defineConfig } from 'drizzle-kit'; + +export default defineConfig({ + schema: './src/db/schema.ts', + out: './drizzle', + dialect: 'postgresql', + dbCredentials: { + url: process.env.DATABASE_URL!, + }, +}); diff --git a/drizzle/0000_common_mole_man.sql b/drizzle/0000_common_mole_man.sql new file mode 100644 index 0000000..ff2a21c --- /dev/null +++ b/drizzle/0000_common_mole_man.sql @@ -0,0 +1,71 @@ +CREATE TABLE "categories" ( + "id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "categories_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1), + "externalId" integer, + "name" varchar(255) NOT NULL, + "parentId" integer, + "description" text, + "createdAt" timestamp DEFAULT now() NOT NULL, + "updatedAt" timestamp DEFAULT now() +); +--> statement-breakpoint +CREATE TABLE "products" ( + "id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "products_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1), + "externalId" varchar(50) NOT NULL, + "storeId" integer NOT NULL, + "categoryId" integer, + "name" varchar(500) NOT NULL, + "description" text, + "url" text, + "imageUrl" text, + "currentPrice" numeric(10, 2) NOT NULL, + "unit" varchar(50), + "weight" varchar(100), + "brand" varchar(255), + "oldPrice" numeric(10, 2), + "discountPercent" numeric(5, 2), + "promotionEndDate" timestamp, + "rating" numeric(3, 2), + "scoresCount" integer, + "commentsCount" integer, + "quantity" integer, + "badges" text, + "isDetailsFetched" boolean DEFAULT false NOT NULL, + "createdAt" timestamp DEFAULT now() NOT NULL, + "updatedAt" timestamp DEFAULT now(), + CONSTRAINT "products_externalId_storeId_unique" UNIQUE("externalId","storeId") +); +--> statement-breakpoint +CREATE TABLE "scraping_sessions" ( + "id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "scraping_sessions_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1), + "storeId" integer NOT NULL, + "sourceType" varchar(50) NOT NULL, + "status" varchar(50) NOT NULL, + "startedAt" timestamp DEFAULT now() NOT NULL, + "finishedAt" timestamp, + "error" text +); +--> statement-breakpoint +CREATE TABLE "stores" ( + "id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "stores_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1), + "name" varchar(255) NOT NULL, + "type" varchar(50) NOT NULL, + "code" varchar(50), + "url" text, + "region" varchar(255), + "address" text, + "createdAt" timestamp DEFAULT now() NOT NULL, + "updatedAt" timestamp DEFAULT now() +); +--> statement-breakpoint +ALTER TABLE "products" ADD CONSTRAINT "products_storeId_stores_id_fk" FOREIGN KEY ("storeId") REFERENCES "public"."stores"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "products" ADD CONSTRAINT "products_categoryId_categories_id_fk" FOREIGN KEY ("categoryId") REFERENCES "public"."categories"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "scraping_sessions" ADD CONSTRAINT "scraping_sessions_storeId_stores_id_fk" FOREIGN KEY ("storeId") REFERENCES "public"."stores"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +CREATE INDEX "categories_externalId_idx" ON "categories" USING btree ("externalId");--> statement-breakpoint +CREATE INDEX "categories_parentId_idx" ON "categories" USING btree ("parentId");--> statement-breakpoint +CREATE INDEX "products_storeId_idx" ON "products" USING btree ("storeId");--> statement-breakpoint +CREATE INDEX "products_categoryId_idx" ON "products" USING btree ("categoryId");--> statement-breakpoint +CREATE INDEX "products_externalId_idx" ON "products" USING btree ("externalId");--> statement-breakpoint +CREATE INDEX "scraping_sessions_storeId_idx" ON "scraping_sessions" USING btree ("storeId");--> statement-breakpoint +CREATE INDEX "scraping_sessions_status_idx" ON "scraping_sessions" USING btree ("status");--> statement-breakpoint +CREATE INDEX "scraping_sessions_startedAt_idx" ON "scraping_sessions" USING btree ("startedAt");--> statement-breakpoint +CREATE INDEX "stores_code_idx" ON "stores" USING btree ("code"); \ No newline at end of file diff --git a/drizzle/meta/0000_snapshot.json b/drizzle/meta/0000_snapshot.json new file mode 100644 index 0000000..b4f05ad --- /dev/null +++ b/drizzle/meta/0000_snapshot.json @@ -0,0 +1,588 @@ +{ + "id": "0c32aa7e-a09f-4c32-a8bd-35af715f5fde", + "prevId": "00000000-0000-0000-0000-000000000000", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.categories": { + "name": "categories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "categories_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "externalId": { + "name": "externalId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "parentId": { + "name": "parentId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "categories_externalId_idx": { + "name": "categories_externalId_idx", + "columns": [ + { + "expression": "externalId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "categories_parentId_idx": { + "name": "categories_parentId_idx", + "columns": [ + { + "expression": "parentId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.products": { + "name": "products", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "products_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "externalId": { + "name": "externalId", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "storeId": { + "name": "storeId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "categoryId": { + "name": "categoryId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "imageUrl": { + "name": "imageUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "currentPrice": { + "name": "currentPrice", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": true + }, + "unit": { + "name": "unit", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "weight": { + "name": "weight", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "brand": { + "name": "brand", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "oldPrice": { + "name": "oldPrice", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "discountPercent": { + "name": "discountPercent", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": false + }, + "promotionEndDate": { + "name": "promotionEndDate", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "rating": { + "name": "rating", + "type": "numeric(3, 2)", + "primaryKey": false, + "notNull": false + }, + "scoresCount": { + "name": "scoresCount", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "commentsCount": { + "name": "commentsCount", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "quantity": { + "name": "quantity", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "badges": { + "name": "badges", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "isDetailsFetched": { + "name": "isDetailsFetched", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "products_storeId_idx": { + "name": "products_storeId_idx", + "columns": [ + { + "expression": "storeId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "products_categoryId_idx": { + "name": "products_categoryId_idx", + "columns": [ + { + "expression": "categoryId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "products_externalId_idx": { + "name": "products_externalId_idx", + "columns": [ + { + "expression": "externalId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "products_storeId_stores_id_fk": { + "name": "products_storeId_stores_id_fk", + "tableFrom": "products", + "tableTo": "stores", + "columnsFrom": [ + "storeId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "products_categoryId_categories_id_fk": { + "name": "products_categoryId_categories_id_fk", + "tableFrom": "products", + "tableTo": "categories", + "columnsFrom": [ + "categoryId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "products_externalId_storeId_unique": { + "name": "products_externalId_storeId_unique", + "nullsNotDistinct": false, + "columns": [ + "externalId", + "storeId" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.scraping_sessions": { + "name": "scraping_sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "scraping_sessions_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "storeId": { + "name": "storeId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "sourceType": { + "name": "sourceType", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "startedAt": { + "name": "startedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "finishedAt": { + "name": "finishedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "scraping_sessions_storeId_idx": { + "name": "scraping_sessions_storeId_idx", + "columns": [ + { + "expression": "storeId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "scraping_sessions_status_idx": { + "name": "scraping_sessions_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "scraping_sessions_startedAt_idx": { + "name": "scraping_sessions_startedAt_idx", + "columns": [ + { + "expression": "startedAt", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "scraping_sessions_storeId_stores_id_fk": { + "name": "scraping_sessions_storeId_stores_id_fk", + "tableFrom": "scraping_sessions", + "tableTo": "stores", + "columnsFrom": [ + "storeId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.stores": { + "name": "stores", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "stores_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "region": { + "name": "region", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "address": { + "name": "address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "stores_code_idx": { + "name": "stores_code_idx", + "columns": [ + { + "expression": "code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json new file mode 100644 index 0000000..c4a5e0f --- /dev/null +++ b/drizzle/meta/_journal.json @@ -0,0 +1,13 @@ +{ + "version": "7", + "dialect": "postgresql", + "entries": [ + { + "idx": 0, + "version": "7", + "when": 1769461513369, + "tag": "0000_common_mole_man", + "breakpoints": true + } + ] +} \ No newline at end of file diff --git a/package.json b/package.json index f11e795..4a522fa 100644 --- a/package.json +++ b/package.json @@ -10,10 +10,10 @@ "dev": "tsx src/scripts/scrape-magnit-products.ts", "enrich": "tsx src/scripts/enrich-product-details.ts", "test-db": "tsx src/scripts/test-db-connection.ts", - "prisma:generate": "prisma generate", - "prisma:migrate": "prisma migrate dev", - "prisma:studio": "prisma studio --config=prisma.config.ts", - "prisma:format": "prisma format" + "db:generate": "drizzle-kit generate", + "db:migrate": "drizzle-kit migrate", + "db:push": "drizzle-kit push", + "db:studio": "drizzle-kit studio" }, "keywords": [ "scraper", @@ -23,17 +23,16 @@ "author": "", "license": "ISC", "dependencies": { - "@prisma/adapter-pg": "^7.2.0", - "@prisma/client": "^7.2.0", "axios": "^1.13.2", "dotenv": "^17.2.3", + "drizzle-orm": "^0.45.1", "pg": "^8.16.3", "playwright": "^1.57.0" }, "devDependencies": { "@types/node": "^25.0.3", "@types/pg": "^8.16.0", - "prisma": "^7.2.0", + "drizzle-kit": "^0.31.8", "ts-node": "^10.9.2", "tsx": "^4.21.0", "typescript": "^5.9.3" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5048d59..1fbb081 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,18 +8,15 @@ importers: .: dependencies: - '@prisma/adapter-pg': - specifier: ^7.2.0 - version: 7.2.0 - '@prisma/client': - specifier: ^7.2.0 - version: 7.2.0(prisma@7.2.0(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(typescript@5.9.3) axios: specifier: ^1.13.2 version: 1.13.2 dotenv: specifier: ^17.2.3 version: 17.2.3 + drizzle-orm: + specifier: ^0.45.1 + version: 0.45.1(@types/pg@8.16.0)(pg@8.16.3) pg: specifier: ^8.16.3 version: 8.16.3 @@ -33,9 +30,9 @@ importers: '@types/pg': specifier: ^8.16.0 version: 8.16.0 - prisma: - specifier: ^7.2.0 - version: 7.2.0(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + drizzle-kit: + specifier: ^0.31.8 + version: 0.31.8 ts-node: specifier: ^10.9.2 version: 10.9.2(@types/node@25.0.3)(typescript@5.9.3) @@ -48,35 +45,26 @@ importers: packages: - '@chevrotain/cst-dts-gen@10.5.0': - resolution: {integrity: sha512-lhmC/FyqQ2o7pGK4Om+hzuDrm9rhFYIJ/AXoQBeongmn870Xeb0L6oGEiuR8nohFNL5sMaQEJWCxr1oIVIVXrw==} - - '@chevrotain/gast@10.5.0': - resolution: {integrity: sha512-pXdMJ9XeDAbgOWKuD1Fldz4ieCs6+nLNmyVhe2gZVqoO7v8HXuHYs5OV2EzUtbuai37TlOAQHrTDvxMnvMJz3A==} - - '@chevrotain/types@10.5.0': - resolution: {integrity: sha512-f1MAia0x/pAVPWH/T73BJVyO2XU5tI4/iE7cnxb7tqdNTNhQI3Uq3XkqcoteTmD4t1aM0LbHCJOhgIDn07kl2A==} - - '@chevrotain/utils@10.5.0': - resolution: {integrity: sha512-hBzuU5+JjB2cqNZyszkDHZgOSrUUT8V3dhgRl8Q9Gp6dAj/H5+KILGjbhDpc3Iy9qmqlm/akuOI2ut9VUtzJxQ==} - '@cspotcode/source-map-support@0.8.1': resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} - '@electric-sql/pglite-socket@0.0.6': - resolution: {integrity: sha512-6RjmgzphIHIBA4NrMGJsjNWK4pu+bCWJlEWlwcxFTVY3WT86dFpKwbZaGWZV6C5Rd7sCk1Z0CI76QEfukLAUXw==} - hasBin: true - peerDependencies: - '@electric-sql/pglite': 0.3.2 + '@drizzle-team/brocli@0.10.2': + resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==} - '@electric-sql/pglite-tools@0.2.7': - resolution: {integrity: sha512-9dAccClqxx4cZB+Ar9B+FZ5WgxDc/Xvl9DPrTWv+dYTf0YNubLzi4wHHRGRGhrJv15XwnyKcGOZAP1VXSneSUg==} - peerDependencies: - '@electric-sql/pglite': 0.3.2 + '@esbuild-kit/core-utils@3.3.2': + resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==} + deprecated: 'Merged into tsx: https://tsx.is' - '@electric-sql/pglite@0.3.2': - resolution: {integrity: sha512-zfWWa+V2ViDCY/cmUfRqeWY1yLto+EpxjXnZzenB1TyxsTiXaTWeZFIZw6mac52BsuQm0RjCnisjBtdBaXOI6w==} + '@esbuild-kit/esm-loader@2.6.5': + resolution: {integrity: sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==} + deprecated: 'Merged into tsx: https://tsx.is' + + '@esbuild/aix-ppc64@0.25.12': + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] '@esbuild/aix-ppc64@0.27.2': resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==} @@ -84,162 +72,438 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/android-arm64@0.18.20': + resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm64@0.25.12': + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm64@0.27.2': resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==} engines: {node: '>=18'} cpu: [arm64] os: [android] + '@esbuild/android-arm@0.18.20': + resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-arm@0.25.12': + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-arm@0.27.2': resolution: {integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==} engines: {node: '>=18'} cpu: [arm] os: [android] + '@esbuild/android-x64@0.18.20': + resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/android-x64@0.25.12': + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/android-x64@0.27.2': resolution: {integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==} engines: {node: '>=18'} cpu: [x64] os: [android] + '@esbuild/darwin-arm64@0.18.20': + resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-arm64@0.25.12': + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-arm64@0.27.2': resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] + '@esbuild/darwin-x64@0.18.20': + resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.12': + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/darwin-x64@0.27.2': resolution: {integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==} engines: {node: '>=18'} cpu: [x64] os: [darwin] + '@esbuild/freebsd-arm64@0.18.20': + resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-arm64@0.25.12': + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-arm64@0.27.2': resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-x64@0.18.20': + resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.12': + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/freebsd-x64@0.27.2': resolution: {integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] + '@esbuild/linux-arm64@0.18.20': + resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm64@0.25.12': + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm64@0.27.2': resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==} engines: {node: '>=18'} cpu: [arm64] os: [linux] + '@esbuild/linux-arm@0.18.20': + resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-arm@0.25.12': + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-arm@0.27.2': resolution: {integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==} engines: {node: '>=18'} cpu: [arm] os: [linux] + '@esbuild/linux-ia32@0.18.20': + resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-ia32@0.25.12': + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-ia32@0.27.2': resolution: {integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==} engines: {node: '>=18'} cpu: [ia32] os: [linux] + '@esbuild/linux-loong64@0.18.20': + resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-loong64@0.25.12': + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-loong64@0.27.2': resolution: {integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==} engines: {node: '>=18'} cpu: [loong64] os: [linux] + '@esbuild/linux-mips64el@0.18.20': + resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-mips64el@0.25.12': + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-mips64el@0.27.2': resolution: {integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] + '@esbuild/linux-ppc64@0.18.20': + resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-ppc64@0.25.12': + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-ppc64@0.27.2': resolution: {integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] + '@esbuild/linux-riscv64@0.18.20': + resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.12': + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-riscv64@0.27.2': resolution: {integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] + '@esbuild/linux-s390x@0.18.20': + resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-s390x@0.25.12': + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-s390x@0.27.2': resolution: {integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==} engines: {node: '>=18'} cpu: [s390x] os: [linux] + '@esbuild/linux-x64@0.18.20': + resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/linux-x64@0.25.12': + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + '@esbuild/linux-x64@0.27.2': resolution: {integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==} engines: {node: '>=18'} cpu: [x64] os: [linux] + '@esbuild/netbsd-arm64@0.25.12': + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-arm64@0.27.2': resolution: {integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] + '@esbuild/netbsd-x64@0.18.20': + resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.12': + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + '@esbuild/netbsd-x64@0.27.2': resolution: {integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] + '@esbuild/openbsd-arm64@0.25.12': + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-arm64@0.27.2': resolution: {integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-x64@0.18.20': + resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.12': + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/openbsd-x64@0.27.2': resolution: {integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] + '@esbuild/openharmony-arm64@0.25.12': + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + '@esbuild/openharmony-arm64@0.27.2': resolution: {integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] + '@esbuild/sunos-x64@0.18.20': + resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/sunos-x64@0.25.12': + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/sunos-x64@0.27.2': resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==} engines: {node: '>=18'} cpu: [x64] os: [sunos] + '@esbuild/win32-arm64@0.18.20': + resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-arm64@0.25.12': + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-arm64@0.27.2': resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==} engines: {node: '>=18'} cpu: [arm64] os: [win32] + '@esbuild/win32-ia32@0.18.20': + resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-ia32@0.25.12': + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-ia32@0.27.2': resolution: {integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==} engines: {node: '>=18'} cpu: [ia32] os: [win32] + '@esbuild/win32-x64@0.18.20': + resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.25.12': + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@esbuild/win32-x64@0.27.2': resolution: {integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==} engines: {node: '>=18'} cpu: [x64] os: [win32] - '@hono/node-server@1.19.6': - resolution: {integrity: sha512-Shz/KjlIeAhfiuE93NDKVdZ7HdBVLQAfdbaXEaoAVO3ic9ibRSLGIQGkcBbFyuLr+7/1D5ZCINM8B+6IvXeMtw==} - engines: {node: '>=18.14.1'} - peerDependencies: - hono: ^4 - '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} @@ -250,71 +514,6 @@ packages: '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - '@mrleebo/prisma-ast@0.12.1': - resolution: {integrity: sha512-JwqeCQ1U3fvccttHZq7Tk0m/TMC6WcFAQZdukypW3AzlJYKYTGNVd1ANU2GuhKnv4UQuOFj3oAl0LLG/gxFN1w==} - engines: {node: '>=16'} - - '@prisma/adapter-pg@7.2.0': - resolution: {integrity: sha512-euIdQ13cRB2wZ3jPsnDnFhINquo1PYFPCg6yVL8b2rp3EdinQHsX9EDdCtRr489D5uhphcRk463OdQAFlsCr0w==} - - '@prisma/client-runtime-utils@7.2.0': - resolution: {integrity: sha512-dn7oB53v0tqkB0wBdMuTNFNPdEbfICEUe82Tn9FoKAhJCUkDH+fmyEp0ClciGh+9Hp2Tuu2K52kth2MTLstvmA==} - - '@prisma/client@7.2.0': - resolution: {integrity: sha512-JdLF8lWZ+LjKGKpBqyAlenxd/kXjd1Abf/xK+6vUA7R7L2Suo6AFTHFRpPSdAKCan9wzdFApsUpSa/F6+t1AtA==} - engines: {node: ^20.19 || ^22.12 || >=24.0} - peerDependencies: - prisma: '*' - typescript: '>=5.4.0' - peerDependenciesMeta: - prisma: - optional: true - typescript: - optional: true - - '@prisma/config@7.2.0': - resolution: {integrity: sha512-qmvSnfQ6l/srBW1S7RZGfjTQhc44Yl3ldvU6y3pgmuLM+83SBDs6UQVgMtQuMRe9J3gGqB0RF8wER6RlXEr6jQ==} - - '@prisma/debug@6.8.2': - resolution: {integrity: sha512-4muBSSUwJJ9BYth5N8tqts8JtiLT8QI/RSAzEogwEfpbYGFo9mYsInsVo8dqXdPO2+Rm5OG5q0qWDDE3nyUbVg==} - - '@prisma/debug@7.2.0': - resolution: {integrity: sha512-YSGTiSlBAVJPzX4ONZmMotL+ozJwQjRmZweQNIq/ER0tQJKJynNkRB3kyvt37eOfsbMCXk3gnLF6J9OJ4QWftw==} - - '@prisma/dev@0.17.0': - resolution: {integrity: sha512-6sGebe5jxX+FEsQTpjHLzvOGPn6ypFQprcs3jcuIWv1Xp/5v6P/rjfdvAwTkP2iF6pDx2tCd8vGLNWcsWzImTA==} - - '@prisma/driver-adapter-utils@7.2.0': - resolution: {integrity: sha512-gzrUcbI9VmHS24Uf+0+7DNzdIw7keglJsD5m/MHxQOU68OhGVzlphQRobLiDMn8CHNA2XN8uugwKjudVtnfMVQ==} - - '@prisma/engines-version@7.2.0-4.0c8ef2ce45c83248ab3df073180d5eda9e8be7a3': - resolution: {integrity: sha512-KezsjCZDsbjNR7SzIiVlUsn9PnLePI7r5uxABlwL+xoerurZTfgQVbIjvjF2sVr3Uc0ZcsnREw3F84HvbggGdA==} - - '@prisma/engines@7.2.0': - resolution: {integrity: sha512-HUeOI/SvCDsHrR9QZn24cxxZcujOjcS3w1oW/XVhnSATAli5SRMOfp/WkG3TtT5rCxDA4xOnlJkW7xkho4nURA==} - - '@prisma/fetch-engine@7.2.0': - resolution: {integrity: sha512-Z5XZztJ8Ap+wovpjPD2lQKnB8nWFGNouCrglaNFjxIWAGWz0oeHXwUJRiclIoSSXN/ptcs9/behptSk8d0Yy6w==} - - '@prisma/get-platform@6.8.2': - resolution: {integrity: sha512-vXSxyUgX3vm1Q70QwzwkjeYfRryIvKno1SXbIqwSptKwqKzskINnDUcx85oX+ys6ooN2ATGSD0xN2UTfg6Zcow==} - - '@prisma/get-platform@7.2.0': - resolution: {integrity: sha512-k1V0l0Td1732EHpAfi2eySTezyllok9dXb6UQanajkJQzPUGi3vO2z7jdkz67SypFTdmbnyGYxvEvYZdZsMAVA==} - - '@prisma/query-plan-executor@6.18.0': - resolution: {integrity: sha512-jZ8cfzFgL0jReE1R10gT8JLHtQxjWYLiQ//wHmVYZ2rVkFHoh0DT8IXsxcKcFlfKN7ak7k6j0XMNn2xVNyr5cA==} - - '@prisma/studio-core@0.9.0': - resolution: {integrity: sha512-xA2zoR/ADu/NCSQuriBKTh6Ps4XjU0bErkEcgMfnSGh346K1VI7iWKnoq1l2DoxUqiddPHIEWwtxJ6xCHG6W7g==} - peerDependencies: - '@types/react': ^18.0.0 || ^19.0.0 - react: ^18.0.0 || ^19.0.0 - react-dom: ^18.0.0 || ^19.0.0 - - '@standard-schema/spec@1.1.0': - resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - '@tsconfig/node10@1.0.12': resolution: {integrity: sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==} @@ -333,9 +532,6 @@ packages: '@types/pg@8.16.0': resolution: {integrity: sha512-RmhMd/wD+CF8Dfo+cVIy3RR5cl8CyfXQ0tGgW6XBL8L4LM/UTEbNXYRbLwU6w+CgrKBNbrQWt4FUtTfaU5jSYQ==} - '@types/react@19.2.7': - resolution: {integrity: sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==} - acorn-walk@8.3.4: resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} engines: {node: '>=0.4.0'} @@ -351,97 +547,144 @@ packages: asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - aws-ssl-profiles@1.1.2: - resolution: {integrity: sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==} - engines: {node: '>= 6.0.0'} - axios@1.13.2: resolution: {integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==} - c12@3.1.0: - resolution: {integrity: sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==} - peerDependencies: - magicast: ^0.3.5 - peerDependenciesMeta: - magicast: - optional: true + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} - chevrotain@10.5.0: - resolution: {integrity: sha512-Pkv5rBY3+CsHOYfV5g/Vs5JY9WTHHDEKOlohI2XeygaZhUeqhAlldZ8Hz9cRmxu709bvS08YzxHdTPHhffc13A==} - - chokidar@4.0.3: - resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} - engines: {node: '>= 14.16.0'} - - citty@0.1.6: - resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} - combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} - confbox@0.2.2: - resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} - - consola@3.4.2: - resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} - engines: {node: ^14.18.0 || >=16.10.0} - create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - cross-spawn@7.0.6: - resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} - engines: {node: '>= 8'} - - csstype@3.2.3: - resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} - - deepmerge-ts@7.1.5: - resolution: {integrity: sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==} - engines: {node: '>=16.0.0'} - - defu@6.1.4: - resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} - denque@2.1.0: - resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} - engines: {node: '>=0.10'} - - destr@2.0.5: - resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} - diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} - dotenv@16.6.1: - resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} - engines: {node: '>=12'} - dotenv@17.2.3: resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==} engines: {node: '>=12'} + drizzle-kit@0.31.8: + resolution: {integrity: sha512-O9EC/miwdnRDY10qRxM8P3Pg8hXe3LyU4ZipReKOgTwn4OqANmftj8XJz1UPUAS6NMHf0E2htjsbQujUTkncCg==} + hasBin: true + + drizzle-orm@0.45.1: + resolution: {integrity: sha512-Te0FOdKIistGNPMq2jscdqngBRfBpC8uMFVwqjf6gtTVJHIQ/dosgV/CLBU2N4ZJBsXL5savCba9b0YJskKdcA==} + peerDependencies: + '@aws-sdk/client-rds-data': '>=3' + '@cloudflare/workers-types': '>=4' + '@electric-sql/pglite': '>=0.2.0' + '@libsql/client': '>=0.10.0' + '@libsql/client-wasm': '>=0.10.0' + '@neondatabase/serverless': '>=0.10.0' + '@op-engineering/op-sqlite': '>=2' + '@opentelemetry/api': ^1.4.1 + '@planetscale/database': '>=1.13' + '@prisma/client': '*' + '@tidbcloud/serverless': '*' + '@types/better-sqlite3': '*' + '@types/pg': '*' + '@types/sql.js': '*' + '@upstash/redis': '>=1.34.7' + '@vercel/postgres': '>=0.8.0' + '@xata.io/client': '*' + better-sqlite3: '>=7' + bun-types: '*' + expo-sqlite: '>=14.0.0' + gel: '>=2' + knex: '*' + kysely: '*' + mysql2: '>=2' + pg: '>=8' + postgres: '>=3' + prisma: '*' + sql.js: '>=1' + sqlite3: '>=5' + peerDependenciesMeta: + '@aws-sdk/client-rds-data': + optional: true + '@cloudflare/workers-types': + optional: true + '@electric-sql/pglite': + optional: true + '@libsql/client': + optional: true + '@libsql/client-wasm': + optional: true + '@neondatabase/serverless': + optional: true + '@op-engineering/op-sqlite': + optional: true + '@opentelemetry/api': + optional: true + '@planetscale/database': + optional: true + '@prisma/client': + optional: true + '@tidbcloud/serverless': + optional: true + '@types/better-sqlite3': + optional: true + '@types/pg': + optional: true + '@types/sql.js': + optional: true + '@upstash/redis': + optional: true + '@vercel/postgres': + optional: true + '@xata.io/client': + optional: true + better-sqlite3: + optional: true + bun-types: + optional: true + expo-sqlite: + optional: true + gel: + optional: true + knex: + optional: true + kysely: + optional: true + mysql2: + optional: true + pg: + optional: true + postgres: + optional: true + prisma: + optional: true + sql.js: + optional: true + sqlite3: + optional: true + dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} - effect@3.18.4: - resolution: {integrity: sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA==} - - empathic@2.0.0: - resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==} - engines: {node: '>=14'} - es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} @@ -458,18 +701,26 @@ packages: resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} engines: {node: '>= 0.4'} + esbuild-register@3.6.0: + resolution: {integrity: sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==} + peerDependencies: + esbuild: '>=0.12 <1' + + esbuild@0.18.20: + resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} + engines: {node: '>=12'} + hasBin: true + + esbuild@0.25.12: + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} + engines: {node: '>=18'} + hasBin: true + esbuild@0.27.2: resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} engines: {node: '>=18'} hasBin: true - exsolve@1.0.8: - resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==} - - fast-check@3.23.2: - resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==} - engines: {node: '>=8.0.0'} - follow-redirects@1.15.11: resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} engines: {node: '>=4.0'} @@ -479,10 +730,6 @@ packages: debug: optional: true - foreground-child@3.3.1: - resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} - engines: {node: '>=14'} - form-data@4.0.5: resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} engines: {node: '>= 6'} @@ -500,16 +747,10 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - generate-function@2.3.1: - resolution: {integrity: sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==} - get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} - get-port-please@3.1.2: - resolution: {integrity: sha512-Gxc29eLs1fbn6LQ4jSU4vXjlwyZhF5HsGuMAa7gqBP4Rw4yxxltyDUuF5MBclFzDTXO+ACchGQoeela4DSfzdQ==} - get-proto@1.0.1: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} @@ -517,20 +758,10 @@ packages: get-tsconfig@4.13.0: resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} - giget@2.0.0: - resolution: {integrity: sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==} - hasBin: true - gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} - graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - - grammex@3.1.12: - resolution: {integrity: sha512-6ufJOsSA7LcQehIJNCO7HIBykfM7DXQual0Ny780/DEcJIpBlHRvcqEBWGPYd7hrXL2GJ3oJI1MIhaXjWmLQOQ==} - has-symbols@1.1.0: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} @@ -543,41 +774,6 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} - hono@4.10.6: - resolution: {integrity: sha512-BIdolzGpDO9MQ4nu3AUuDwHZZ+KViNm+EZ75Ae55eMXMqLVhDFqEMXxtUe9Qh8hjL+pIna/frs2j6Y2yD5Ua/g==} - engines: {node: '>=16.9.0'} - - http-status-codes@2.3.0: - resolution: {integrity: sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==} - - iconv-lite@0.7.1: - resolution: {integrity: sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==} - engines: {node: '>=0.10.0'} - - is-property@1.0.2: - resolution: {integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==} - - isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - - jiti@2.6.1: - resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} - hasBin: true - - lilconfig@2.1.0: - resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} - engines: {node: '>=10'} - - lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - - long@5.3.2: - resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} - - lru.min@1.1.3: - resolution: {integrity: sha512-Lkk/vx6ak3rYkRR0Nhu4lFUT2VDnQSxBe8Hbl7f36358p6ow8Bnvr8lrLt98H8J1aGxfhbX4Fs5tYg2+FTwr5Q==} - engines: {bun: '>=1.0.0', deno: '>=1.30.0', node: '>=8.0.0'} - make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} @@ -593,34 +789,8 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} - mysql2@3.15.3: - resolution: {integrity: sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg==} - engines: {node: '>= 8.0'} - - named-placeholders@1.1.6: - resolution: {integrity: sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==} - engines: {node: '>=8.0.0'} - - node-fetch-native@1.6.7: - resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} - - nypm@0.6.2: - resolution: {integrity: sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g==} - engines: {node: ^14.16.0 || >=16.10.0} - hasBin: true - - ohash@2.0.11: - resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} - - path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - - pathe@2.0.3: - resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - - perfect-debounce@1.0.0: - resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} pg-cloudflare@1.2.7: resolution: {integrity: sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==} @@ -656,9 +826,6 @@ packages: pgpass@1.0.5: resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} - pkg-types@2.3.0: - resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==} - playwright-core@1.57.0: resolution: {integrity: sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==} engines: {node: '>=18'} @@ -673,10 +840,6 @@ packages: resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} engines: {node: '>=4'} - postgres-array@3.0.4: - resolution: {integrity: sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ==} - engines: {node: '>=12'} - postgres-bytea@1.0.1: resolution: {integrity: sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==} engines: {node: '>=0.10.0'} @@ -689,100 +852,23 @@ packages: resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} engines: {node: '>=0.10.0'} - postgres@3.4.7: - resolution: {integrity: sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==} - engines: {node: '>=12'} - - prisma@7.2.0: - resolution: {integrity: sha512-jSdHWgWOgFF24+nRyyNRVBIgGDQEsMEF8KPHvhBBg3jWyR9fUAK0Nq9ThUmiGlNgq2FA7vSk/ZoCvefod+a8qg==} - engines: {node: ^20.19 || ^22.12 || >=24.0} - hasBin: true - peerDependencies: - better-sqlite3: '>=9.0.0' - typescript: '>=5.4.0' - peerDependenciesMeta: - better-sqlite3: - optional: true - typescript: - optional: true - - proper-lockfile@4.1.2: - resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} - proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - pure-rand@6.1.0: - resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} - - rc9@2.1.2: - resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==} - - react-dom@19.2.3: - resolution: {integrity: sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==} - peerDependencies: - react: ^19.2.3 - - react@19.2.3: - resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==} - engines: {node: '>=0.10.0'} - - readdirp@4.1.2: - resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} - engines: {node: '>= 14.18.0'} - - regexp-to-ast@0.5.0: - resolution: {integrity: sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==} - - remeda@2.21.3: - resolution: {integrity: sha512-XXrZdLA10oEOQhLLzEJEiFFSKi21REGAkHdImIb4rt/XXy8ORGXh5HCcpUOsElfPNDb+X6TA/+wkh+p2KffYmg==} - resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - retry@0.12.0: - resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} - engines: {node: '>= 4'} + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} - safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - - scheduler@0.27.0: - resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} - - seq-queue@0.0.5: - resolution: {integrity: sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==} - - shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} - - shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - - signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - - signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} split2@4.2.0: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} - sqlstring@2.3.3: - resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==} - engines: {node: '>= 0.6'} - - std-env@3.9.0: - resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} - - tinyexec@1.0.2: - resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} - engines: {node: '>=18'} - ts-node@10.9.2: resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} hasBin: true @@ -802,10 +888,6 @@ packages: engines: {node: '>=18.0.0'} hasBin: true - type-fest@4.41.0: - resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} - engines: {node: '>=16'} - typescript@5.9.3: resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} @@ -817,19 +899,6 @@ packages: v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - valibot@1.2.0: - resolution: {integrity: sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==} - peerDependencies: - typescript: '>=5' - peerDependenciesMeta: - typescript: - optional: true - - which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true - xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} @@ -838,122 +907,246 @@ packages: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} - zeptomatch@2.0.2: - resolution: {integrity: sha512-H33jtSKf8Ijtb5BW6wua3G5DhnFjbFML36eFu+VdOoVY4HD9e7ggjqdM6639B+L87rjnR6Y+XeRzBXZdy52B/g==} - snapshots: - '@chevrotain/cst-dts-gen@10.5.0': - dependencies: - '@chevrotain/gast': 10.5.0 - '@chevrotain/types': 10.5.0 - lodash: 4.17.21 - - '@chevrotain/gast@10.5.0': - dependencies: - '@chevrotain/types': 10.5.0 - lodash: 4.17.21 - - '@chevrotain/types@10.5.0': {} - - '@chevrotain/utils@10.5.0': {} - '@cspotcode/source-map-support@0.8.1': dependencies: '@jridgewell/trace-mapping': 0.3.9 - '@electric-sql/pglite-socket@0.0.6(@electric-sql/pglite@0.3.2)': - dependencies: - '@electric-sql/pglite': 0.3.2 + '@drizzle-team/brocli@0.10.2': {} - '@electric-sql/pglite-tools@0.2.7(@electric-sql/pglite@0.3.2)': + '@esbuild-kit/core-utils@3.3.2': dependencies: - '@electric-sql/pglite': 0.3.2 + esbuild: 0.18.20 + source-map-support: 0.5.21 - '@electric-sql/pglite@0.3.2': {} + '@esbuild-kit/esm-loader@2.6.5': + dependencies: + '@esbuild-kit/core-utils': 3.3.2 + get-tsconfig: 4.13.0 + + '@esbuild/aix-ppc64@0.25.12': + optional: true '@esbuild/aix-ppc64@0.27.2': optional: true + '@esbuild/android-arm64@0.18.20': + optional: true + + '@esbuild/android-arm64@0.25.12': + optional: true + '@esbuild/android-arm64@0.27.2': optional: true + '@esbuild/android-arm@0.18.20': + optional: true + + '@esbuild/android-arm@0.25.12': + optional: true + '@esbuild/android-arm@0.27.2': optional: true + '@esbuild/android-x64@0.18.20': + optional: true + + '@esbuild/android-x64@0.25.12': + optional: true + '@esbuild/android-x64@0.27.2': optional: true + '@esbuild/darwin-arm64@0.18.20': + optional: true + + '@esbuild/darwin-arm64@0.25.12': + optional: true + '@esbuild/darwin-arm64@0.27.2': optional: true + '@esbuild/darwin-x64@0.18.20': + optional: true + + '@esbuild/darwin-x64@0.25.12': + optional: true + '@esbuild/darwin-x64@0.27.2': optional: true + '@esbuild/freebsd-arm64@0.18.20': + optional: true + + '@esbuild/freebsd-arm64@0.25.12': + optional: true + '@esbuild/freebsd-arm64@0.27.2': optional: true + '@esbuild/freebsd-x64@0.18.20': + optional: true + + '@esbuild/freebsd-x64@0.25.12': + optional: true + '@esbuild/freebsd-x64@0.27.2': optional: true + '@esbuild/linux-arm64@0.18.20': + optional: true + + '@esbuild/linux-arm64@0.25.12': + optional: true + '@esbuild/linux-arm64@0.27.2': optional: true + '@esbuild/linux-arm@0.18.20': + optional: true + + '@esbuild/linux-arm@0.25.12': + optional: true + '@esbuild/linux-arm@0.27.2': optional: true + '@esbuild/linux-ia32@0.18.20': + optional: true + + '@esbuild/linux-ia32@0.25.12': + optional: true + '@esbuild/linux-ia32@0.27.2': optional: true + '@esbuild/linux-loong64@0.18.20': + optional: true + + '@esbuild/linux-loong64@0.25.12': + optional: true + '@esbuild/linux-loong64@0.27.2': optional: true + '@esbuild/linux-mips64el@0.18.20': + optional: true + + '@esbuild/linux-mips64el@0.25.12': + optional: true + '@esbuild/linux-mips64el@0.27.2': optional: true + '@esbuild/linux-ppc64@0.18.20': + optional: true + + '@esbuild/linux-ppc64@0.25.12': + optional: true + '@esbuild/linux-ppc64@0.27.2': optional: true + '@esbuild/linux-riscv64@0.18.20': + optional: true + + '@esbuild/linux-riscv64@0.25.12': + optional: true + '@esbuild/linux-riscv64@0.27.2': optional: true + '@esbuild/linux-s390x@0.18.20': + optional: true + + '@esbuild/linux-s390x@0.25.12': + optional: true + '@esbuild/linux-s390x@0.27.2': optional: true + '@esbuild/linux-x64@0.18.20': + optional: true + + '@esbuild/linux-x64@0.25.12': + optional: true + '@esbuild/linux-x64@0.27.2': optional: true + '@esbuild/netbsd-arm64@0.25.12': + optional: true + '@esbuild/netbsd-arm64@0.27.2': optional: true + '@esbuild/netbsd-x64@0.18.20': + optional: true + + '@esbuild/netbsd-x64@0.25.12': + optional: true + '@esbuild/netbsd-x64@0.27.2': optional: true + '@esbuild/openbsd-arm64@0.25.12': + optional: true + '@esbuild/openbsd-arm64@0.27.2': optional: true + '@esbuild/openbsd-x64@0.18.20': + optional: true + + '@esbuild/openbsd-x64@0.25.12': + optional: true + '@esbuild/openbsd-x64@0.27.2': optional: true + '@esbuild/openharmony-arm64@0.25.12': + optional: true + '@esbuild/openharmony-arm64@0.27.2': optional: true + '@esbuild/sunos-x64@0.18.20': + optional: true + + '@esbuild/sunos-x64@0.25.12': + optional: true + '@esbuild/sunos-x64@0.27.2': optional: true + '@esbuild/win32-arm64@0.18.20': + optional: true + + '@esbuild/win32-arm64@0.25.12': + optional: true + '@esbuild/win32-arm64@0.27.2': optional: true + '@esbuild/win32-ia32@0.18.20': + optional: true + + '@esbuild/win32-ia32@0.25.12': + optional: true + '@esbuild/win32-ia32@0.27.2': optional: true + '@esbuild/win32-x64@0.18.20': + optional: true + + '@esbuild/win32-x64@0.25.12': + optional: true + '@esbuild/win32-x64@0.27.2': optional: true - '@hono/node-server@1.19.6(hono@4.10.6)': - dependencies: - hono: 4.10.6 - '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/sourcemap-codec@1.5.5': {} @@ -963,100 +1156,6 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@mrleebo/prisma-ast@0.12.1': - dependencies: - chevrotain: 10.5.0 - lilconfig: 2.1.0 - - '@prisma/adapter-pg@7.2.0': - dependencies: - '@prisma/driver-adapter-utils': 7.2.0 - pg: 8.16.3 - postgres-array: 3.0.4 - transitivePeerDependencies: - - pg-native - - '@prisma/client-runtime-utils@7.2.0': {} - - '@prisma/client@7.2.0(prisma@7.2.0(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3))(typescript@5.9.3)': - dependencies: - '@prisma/client-runtime-utils': 7.2.0 - optionalDependencies: - prisma: 7.2.0(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3) - typescript: 5.9.3 - - '@prisma/config@7.2.0': - dependencies: - c12: 3.1.0 - deepmerge-ts: 7.1.5 - effect: 3.18.4 - empathic: 2.0.0 - transitivePeerDependencies: - - magicast - - '@prisma/debug@6.8.2': {} - - '@prisma/debug@7.2.0': {} - - '@prisma/dev@0.17.0(typescript@5.9.3)': - dependencies: - '@electric-sql/pglite': 0.3.2 - '@electric-sql/pglite-socket': 0.0.6(@electric-sql/pglite@0.3.2) - '@electric-sql/pglite-tools': 0.2.7(@electric-sql/pglite@0.3.2) - '@hono/node-server': 1.19.6(hono@4.10.6) - '@mrleebo/prisma-ast': 0.12.1 - '@prisma/get-platform': 6.8.2 - '@prisma/query-plan-executor': 6.18.0 - foreground-child: 3.3.1 - get-port-please: 3.1.2 - hono: 4.10.6 - http-status-codes: 2.3.0 - pathe: 2.0.3 - proper-lockfile: 4.1.2 - remeda: 2.21.3 - std-env: 3.9.0 - valibot: 1.2.0(typescript@5.9.3) - zeptomatch: 2.0.2 - transitivePeerDependencies: - - typescript - - '@prisma/driver-adapter-utils@7.2.0': - dependencies: - '@prisma/debug': 7.2.0 - - '@prisma/engines-version@7.2.0-4.0c8ef2ce45c83248ab3df073180d5eda9e8be7a3': {} - - '@prisma/engines@7.2.0': - dependencies: - '@prisma/debug': 7.2.0 - '@prisma/engines-version': 7.2.0-4.0c8ef2ce45c83248ab3df073180d5eda9e8be7a3 - '@prisma/fetch-engine': 7.2.0 - '@prisma/get-platform': 7.2.0 - - '@prisma/fetch-engine@7.2.0': - dependencies: - '@prisma/debug': 7.2.0 - '@prisma/engines-version': 7.2.0-4.0c8ef2ce45c83248ab3df073180d5eda9e8be7a3 - '@prisma/get-platform': 7.2.0 - - '@prisma/get-platform@6.8.2': - dependencies: - '@prisma/debug': 6.8.2 - - '@prisma/get-platform@7.2.0': - dependencies: - '@prisma/debug': 7.2.0 - - '@prisma/query-plan-executor@6.18.0': {} - - '@prisma/studio-core@0.9.0(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - '@types/react': 19.2.7 - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) - - '@standard-schema/spec@1.1.0': {} - '@tsconfig/node10@1.0.12': {} '@tsconfig/node12@1.0.11': {} @@ -1075,10 +1174,6 @@ snapshots: pg-protocol: 1.10.3 pg-types: 2.2.0 - '@types/react@19.2.7': - dependencies: - csstype: 3.2.3 - acorn-walk@8.3.4: dependencies: acorn: 8.15.0 @@ -1089,8 +1184,6 @@ snapshots: asynckit@0.4.0: {} - aws-ssl-profiles@1.1.2: {} - axios@1.13.2: dependencies: follow-redirects: 1.15.11 @@ -1099,90 +1192,49 @@ snapshots: transitivePeerDependencies: - debug - c12@3.1.0: - dependencies: - chokidar: 4.0.3 - confbox: 0.2.2 - defu: 6.1.4 - dotenv: 16.6.1 - exsolve: 1.0.8 - giget: 2.0.0 - jiti: 2.6.1 - ohash: 2.0.11 - pathe: 2.0.3 - perfect-debounce: 1.0.0 - pkg-types: 2.3.0 - rc9: 2.1.2 + buffer-from@1.1.2: {} call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 function-bind: 1.1.2 - chevrotain@10.5.0: - dependencies: - '@chevrotain/cst-dts-gen': 10.5.0 - '@chevrotain/gast': 10.5.0 - '@chevrotain/types': 10.5.0 - '@chevrotain/utils': 10.5.0 - lodash: 4.17.21 - regexp-to-ast: 0.5.0 - - chokidar@4.0.3: - dependencies: - readdirp: 4.1.2 - - citty@0.1.6: - dependencies: - consola: 3.4.2 - combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 - confbox@0.2.2: {} - - consola@3.4.2: {} - create-require@1.1.1: {} - cross-spawn@7.0.6: + debug@4.4.3: dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - - csstype@3.2.3: {} - - deepmerge-ts@7.1.5: {} - - defu@6.1.4: {} + ms: 2.1.3 delayed-stream@1.0.0: {} - denque@2.1.0: {} - - destr@2.0.5: {} - diff@4.0.2: {} - dotenv@16.6.1: {} - dotenv@17.2.3: {} + drizzle-kit@0.31.8: + dependencies: + '@drizzle-team/brocli': 0.10.2 + '@esbuild-kit/esm-loader': 2.6.5 + esbuild: 0.25.12 + esbuild-register: 3.6.0(esbuild@0.25.12) + transitivePeerDependencies: + - supports-color + + drizzle-orm@0.45.1(@types/pg@8.16.0)(pg@8.16.3): + optionalDependencies: + '@types/pg': 8.16.0 + pg: 8.16.3 + dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 es-errors: 1.3.0 gopd: 1.2.0 - effect@3.18.4: - dependencies: - '@standard-schema/spec': 1.1.0 - fast-check: 3.23.2 - - empathic@2.0.0: {} - es-define-property@1.0.1: {} es-errors@1.3.0: {} @@ -1198,6 +1250,67 @@ snapshots: has-tostringtag: 1.0.2 hasown: 2.0.2 + esbuild-register@3.6.0(esbuild@0.25.12): + dependencies: + debug: 4.4.3 + esbuild: 0.25.12 + transitivePeerDependencies: + - supports-color + + esbuild@0.18.20: + optionalDependencies: + '@esbuild/android-arm': 0.18.20 + '@esbuild/android-arm64': 0.18.20 + '@esbuild/android-x64': 0.18.20 + '@esbuild/darwin-arm64': 0.18.20 + '@esbuild/darwin-x64': 0.18.20 + '@esbuild/freebsd-arm64': 0.18.20 + '@esbuild/freebsd-x64': 0.18.20 + '@esbuild/linux-arm': 0.18.20 + '@esbuild/linux-arm64': 0.18.20 + '@esbuild/linux-ia32': 0.18.20 + '@esbuild/linux-loong64': 0.18.20 + '@esbuild/linux-mips64el': 0.18.20 + '@esbuild/linux-ppc64': 0.18.20 + '@esbuild/linux-riscv64': 0.18.20 + '@esbuild/linux-s390x': 0.18.20 + '@esbuild/linux-x64': 0.18.20 + '@esbuild/netbsd-x64': 0.18.20 + '@esbuild/openbsd-x64': 0.18.20 + '@esbuild/sunos-x64': 0.18.20 + '@esbuild/win32-arm64': 0.18.20 + '@esbuild/win32-ia32': 0.18.20 + '@esbuild/win32-x64': 0.18.20 + + esbuild@0.25.12: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.12 + '@esbuild/android-arm': 0.25.12 + '@esbuild/android-arm64': 0.25.12 + '@esbuild/android-x64': 0.25.12 + '@esbuild/darwin-arm64': 0.25.12 + '@esbuild/darwin-x64': 0.25.12 + '@esbuild/freebsd-arm64': 0.25.12 + '@esbuild/freebsd-x64': 0.25.12 + '@esbuild/linux-arm': 0.25.12 + '@esbuild/linux-arm64': 0.25.12 + '@esbuild/linux-ia32': 0.25.12 + '@esbuild/linux-loong64': 0.25.12 + '@esbuild/linux-mips64el': 0.25.12 + '@esbuild/linux-ppc64': 0.25.12 + '@esbuild/linux-riscv64': 0.25.12 + '@esbuild/linux-s390x': 0.25.12 + '@esbuild/linux-x64': 0.25.12 + '@esbuild/netbsd-arm64': 0.25.12 + '@esbuild/netbsd-x64': 0.25.12 + '@esbuild/openbsd-arm64': 0.25.12 + '@esbuild/openbsd-x64': 0.25.12 + '@esbuild/openharmony-arm64': 0.25.12 + '@esbuild/sunos-x64': 0.25.12 + '@esbuild/win32-arm64': 0.25.12 + '@esbuild/win32-ia32': 0.25.12 + '@esbuild/win32-x64': 0.25.12 + esbuild@0.27.2: optionalDependencies: '@esbuild/aix-ppc64': 0.27.2 @@ -1227,19 +1340,8 @@ snapshots: '@esbuild/win32-ia32': 0.27.2 '@esbuild/win32-x64': 0.27.2 - exsolve@1.0.8: {} - - fast-check@3.23.2: - dependencies: - pure-rand: 6.1.0 - follow-redirects@1.15.11: {} - foreground-child@3.3.1: - dependencies: - cross-spawn: 7.0.6 - signal-exit: 4.1.0 - form-data@4.0.5: dependencies: asynckit: 0.4.0 @@ -1256,10 +1358,6 @@ snapshots: function-bind@1.1.2: {} - generate-function@2.3.1: - dependencies: - is-property: 1.0.2 - get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -1273,8 +1371,6 @@ snapshots: hasown: 2.0.2 math-intrinsics: 1.1.0 - get-port-please@3.1.2: {} - get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 @@ -1284,21 +1380,8 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 - giget@2.0.0: - dependencies: - citty: 0.1.6 - consola: 3.4.2 - defu: 6.1.4 - node-fetch-native: 1.6.7 - nypm: 0.6.2 - pathe: 2.0.3 - gopd@1.2.0: {} - graceful-fs@4.2.11: {} - - grammex@3.1.12: {} - has-symbols@1.1.0: {} has-tostringtag@1.0.2: @@ -1309,28 +1392,6 @@ snapshots: dependencies: function-bind: 1.1.2 - hono@4.10.6: {} - - http-status-codes@2.3.0: {} - - iconv-lite@0.7.1: - dependencies: - safer-buffer: 2.1.2 - - is-property@1.0.2: {} - - isexe@2.0.0: {} - - jiti@2.6.1: {} - - lilconfig@2.1.0: {} - - lodash@4.17.21: {} - - long@5.3.2: {} - - lru.min@1.1.3: {} - make-error@1.3.6: {} math-intrinsics@1.1.0: {} @@ -1341,39 +1402,7 @@ snapshots: dependencies: mime-db: 1.52.0 - mysql2@3.15.3: - dependencies: - aws-ssl-profiles: 1.1.2 - denque: 2.1.0 - generate-function: 2.3.1 - iconv-lite: 0.7.1 - long: 5.3.2 - lru.min: 1.1.3 - named-placeholders: 1.1.6 - seq-queue: 0.0.5 - sqlstring: 2.3.3 - - named-placeholders@1.1.6: - dependencies: - lru.min: 1.1.3 - - node-fetch-native@1.6.7: {} - - nypm@0.6.2: - dependencies: - citty: 0.1.6 - consola: 3.4.2 - pathe: 2.0.3 - pkg-types: 2.3.0 - tinyexec: 1.0.2 - - ohash@2.0.11: {} - - path-key@3.1.1: {} - - pathe@2.0.3: {} - - perfect-debounce@1.0.0: {} + ms@2.1.3: {} pg-cloudflare@1.2.7: optional: true @@ -1410,12 +1439,6 @@ snapshots: dependencies: split2: 4.2.0 - pkg-types@2.3.0: - dependencies: - confbox: 0.2.2 - exsolve: 1.0.8 - pathe: 2.0.3 - playwright-core@1.57.0: {} playwright@1.57.0: @@ -1426,8 +1449,6 @@ snapshots: postgres-array@2.0.0: {} - postgres-array@3.0.4: {} - postgres-bytea@1.0.1: {} postgres-date@1.0.7: {} @@ -1436,82 +1457,19 @@ snapshots: dependencies: xtend: 4.0.2 - postgres@3.4.7: {} - - prisma@7.2.0(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3): - dependencies: - '@prisma/config': 7.2.0 - '@prisma/dev': 0.17.0(typescript@5.9.3) - '@prisma/engines': 7.2.0 - '@prisma/studio-core': 0.9.0(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - mysql2: 3.15.3 - postgres: 3.4.7 - optionalDependencies: - typescript: 5.9.3 - transitivePeerDependencies: - - '@types/react' - - magicast - - react - - react-dom - - proper-lockfile@4.1.2: - dependencies: - graceful-fs: 4.2.11 - retry: 0.12.0 - signal-exit: 3.0.7 - proxy-from-env@1.1.0: {} - pure-rand@6.1.0: {} - - rc9@2.1.2: - dependencies: - defu: 6.1.4 - destr: 2.0.5 - - react-dom@19.2.3(react@19.2.3): - dependencies: - react: 19.2.3 - scheduler: 0.27.0 - - react@19.2.3: {} - - readdirp@4.1.2: {} - - regexp-to-ast@0.5.0: {} - - remeda@2.21.3: - dependencies: - type-fest: 4.41.0 - resolve-pkg-maps@1.0.0: {} - retry@0.12.0: {} - - safer-buffer@2.1.2: {} - - scheduler@0.27.0: {} - - seq-queue@0.0.5: {} - - shebang-command@2.0.0: + source-map-support@0.5.21: dependencies: - shebang-regex: 3.0.0 + buffer-from: 1.1.2 + source-map: 0.6.1 - shebang-regex@3.0.0: {} - - signal-exit@3.0.7: {} - - signal-exit@4.1.0: {} + source-map@0.6.1: {} split2@4.2.0: {} - sqlstring@2.3.3: {} - - std-env@3.9.0: {} - - tinyexec@1.0.2: {} - ts-node@10.9.2(@types/node@25.0.3)(typescript@5.9.3): dependencies: '@cspotcode/source-map-support': 0.8.1 @@ -1537,26 +1495,12 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - type-fest@4.41.0: {} - typescript@5.9.3: {} undici-types@7.16.0: {} v8-compile-cache-lib@3.0.1: {} - valibot@1.2.0(typescript@5.9.3): - optionalDependencies: - typescript: 5.9.3 - - which@2.0.2: - dependencies: - isexe: 2.0.0 - xtend@4.0.2: {} yn@3.1.1: {} - - zeptomatch@2.0.2: - dependencies: - grammex: 3.1.12 diff --git a/prisma.config.ts b/prisma.config.ts deleted file mode 100644 index 96c9cc1..0000000 --- a/prisma.config.ts +++ /dev/null @@ -1,12 +0,0 @@ -import 'dotenv/config' -import { defineConfig } from 'prisma/config' - -export default defineConfig({ - schema: 'src/database/prisma/schema.prisma', - migrations: { - path: 'src/database/prisma/migrations', - }, - datasource: { - url: process.env.DATABASE_URL, - }, -}) \ No newline at end of file diff --git a/src/config/database.ts b/src/config/database.ts index d0b3ab4..bfcbba3 100644 --- a/src/config/database.ts +++ b/src/config/database.ts @@ -1,15 +1,12 @@ -import "dotenv/config"; -import { PrismaPg } from '@prisma/adapter-pg'; -import { PrismaClient } from '../../generated/prisma/client.js'; +import 'dotenv/config'; +import { db } from '../database/client.js'; +import { stores } from '../db/schema.js'; -const connectionString = `${process.env.DATABASE_URL}`; - -const adapter = new PrismaPg({ connectionString }); -export const prisma = new PrismaClient({ adapter }); +export { db }; export async function connectDatabase() { try { - await prisma.$connect(); + await db.select().from(stores).limit(1); console.log('✅ Подключение к базе данных установлено'); } catch (error) { console.error('❌ Ошибка подключения к базе данных:', error); @@ -18,7 +15,6 @@ export async function connectDatabase() { } export async function disconnectDatabase() { - await prisma.$disconnect(); + await db.$client.end(); console.log('✅ Отключение от базы данных'); } - diff --git a/src/database/client.ts b/src/database/client.ts index f347055..4fc9d28 100644 --- a/src/database/client.ts +++ b/src/database/client.ts @@ -1,11 +1,12 @@ -import "dotenv/config"; -import { PrismaPg } from '@prisma/adapter-pg'; -import { PrismaClient } from '../../generated/prisma/client.js'; +import 'dotenv/config'; +import { drizzle } from 'drizzle-orm/node-postgres'; +import { Pool } from 'pg'; +import * as schema from '../db/schema.js'; -const connectionString = `${process.env.DATABASE_URL}`; +const connectionString = process.env.DATABASE_URL; -const adapter = new PrismaPg({ connectionString }); -export const prisma = new PrismaClient({ adapter }); +const pool = new Pool({ connectionString }); -export default prisma; +export const db = drizzle(pool, { schema }); +export default db; diff --git a/src/database/prisma/schema.prisma b/src/database/prisma/schema.prisma deleted file mode 100644 index be5fc7c..0000000 --- a/src/database/prisma/schema.prisma +++ /dev/null @@ -1,104 +0,0 @@ -// This is your Prisma schema file, -// learn more about it in the docs: https://pris.ly/d/prisma-schema - -generator client { - provider = "prisma-client" - output = "../../../generated/prisma" -} - -datasource db { - provider = "postgresql" -} - -model Store { - id Int @id @default(autoincrement()) - name String - type String // "web" | "app" - code String? // storeCode для API (например "992301") - url String? - region String? - address String? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - products Product[] - sessions ScrapingSession[] - - @@index([code]) -} - -model Category { - id Int @id @default(autoincrement()) - externalId Int? // ID из внешнего API - name String - parentId Int? - parent Category? @relation("CategoryHierarchy", fields: [parentId], references: [id]) - children Category[] @relation("CategoryHierarchy") - description String? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - products Product[] - - @@index([externalId]) - @@index([parentId]) -} - -model Product { - id Int @id @default(autoincrement()) - externalId String // ID из API (например "1000300796") - storeId Int - categoryId Int? - name String - description String? - url String? - imageUrl String? - currentPrice Decimal @db.Decimal(10, 2) - unit String? // единица измерения - weight String? // вес/объем - brand String? - - // Промо-информация - oldPrice Decimal? @db.Decimal(10, 2) // старая цена при акции - discountPercent Decimal? @db.Decimal(5, 2) // процент скидки - promotionEndDate DateTime? // дата окончания акции - - // Рейтинги - rating Decimal? @db.Decimal(3, 2) // рейтинг товара - scoresCount Int? // количество оценок - commentsCount Int? // количество комментариев - - // Остаток и бейджи - quantity Int? // остаток на складе - badges String? // массив бейджей в формате JSON - - // Детальная информация - isDetailsFetched Boolean @default(false) // были ли получены детали через detail endpoint - - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - store Store @relation(fields: [storeId], references: [id]) - category Category? @relation(fields: [categoryId], references: [id]) - - @@unique([externalId, storeId]) - @@index([storeId]) - @@index([categoryId]) - @@index([externalId]) -} - -model ScrapingSession { - id Int @id @default(autoincrement()) - storeId Int - sourceType String // "api" | "web" | "app" - status String // "pending" | "running" | "completed" | "failed" - startedAt DateTime @default(now()) - finishedAt DateTime? - error String? - - store Store @relation(fields: [storeId], references: [id]) - - @@index([storeId]) - @@index([status]) - @@index([startedAt]) -} diff --git a/src/db/schema.ts b/src/db/schema.ts new file mode 100644 index 0000000..6d74db0 --- /dev/null +++ b/src/db/schema.ts @@ -0,0 +1,73 @@ +import { pgTable, integer, varchar, text, decimal, timestamp, index, unique, boolean } from 'drizzle-orm/pg-core'; + +export const stores = pgTable('stores', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + name: varchar({ length: 255 }).notNull(), + type: varchar({ length: 50 }).notNull(), + code: varchar({ length: 50 }), + url: text(), + region: varchar({ length: 255 }), + address: text(), + createdAt: timestamp().defaultNow().notNull(), + updatedAt: timestamp().defaultNow(), +}, (table) => [ + index('stores_code_idx').on(table.code), +]); + +export const categories = pgTable('categories', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + externalId: integer(), + name: varchar({ length: 255 }).notNull(), + parentId: integer(), + description: text(), + createdAt: timestamp().defaultNow().notNull(), + updatedAt: timestamp().defaultNow(), +}, (table) => [ + index('categories_externalId_idx').on(table.externalId), + index('categories_parentId_idx').on(table.parentId), +]); + +export const products = pgTable('products', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + externalId: varchar({ length: 50 }).notNull(), + storeId: integer().notNull().references(() => stores.id), + categoryId: integer().references(() => categories.id), + name: varchar({ length: 500 }).notNull(), + description: text(), + url: text(), + imageUrl: text(), + currentPrice: decimal({ precision: 10, scale: 2 }).notNull(), + unit: varchar({ length: 50 }), + weight: varchar({ length: 100 }), + brand: varchar({ length: 255 }), + oldPrice: decimal({ precision: 10, scale: 2 }), + discountPercent: decimal({ precision: 5, scale: 2 }), + promotionEndDate: timestamp(), + rating: decimal({ precision: 3, scale: 2 }), + scoresCount: integer(), + commentsCount: integer(), + quantity: integer(), + badges: text(), + isDetailsFetched: boolean().default(false).notNull(), + createdAt: timestamp().defaultNow().notNull(), + updatedAt: timestamp().defaultNow(), +}, (table) => [ + unique('products_externalId_storeId_unique').on(table.externalId, table.storeId), + index('products_storeId_idx').on(table.storeId), + index('products_categoryId_idx').on(table.categoryId), + index('products_externalId_idx').on(table.externalId), +]); + +export const scrapingSessions = pgTable('scraping_sessions', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + storeId: integer().notNull().references(() => stores.id), + sourceType: varchar({ length: 50 }).notNull(), + status: varchar({ length: 50 }).notNull(), + startedAt: timestamp().defaultNow().notNull(), + finishedAt: timestamp(), + error: text(), +}, (table) => [ + index('scraping_sessions_storeId_idx').on(table.storeId), + index('scraping_sessions_status_idx').on(table.status), + index('scraping_sessions_startedAt_idx').on(table.startedAt), +]); diff --git a/src/scrapers/api/magnit/MagnitApiScraper.ts b/src/scrapers/api/magnit/MagnitApiScraper.ts index 6ccd2ac..5018d2f 100644 --- a/src/scrapers/api/magnit/MagnitApiScraper.ts +++ b/src/scrapers/api/magnit/MagnitApiScraper.ts @@ -13,7 +13,6 @@ import { } from './types.js'; import { ProductService } from '../../../services/product/ProductService.js'; import { ProductParser } from '../../../services/parser/ProductParser.js'; -import { PrismaClient } from '../../../../generated/prisma/client.js'; import { withRetryAndReinit } from '../../../utils/retry.js'; export interface MagnitScraperConfig { @@ -510,12 +509,12 @@ export class MagnitApiScraper { */ async saveToDatabase( products: ProductItem[], - prisma: PrismaClient + drizzleDb: any ): Promise { try { Logger.info(`Начало сохранения ${products.length} товаров в БД...`); - const productService = new ProductService(prisma); + const productService = new ProductService(drizzleDb); // Получаем или создаем магазин const store = await productService.getOrCreateStore( @@ -558,13 +557,13 @@ export class MagnitApiScraper { * Более эффективно для больших каталогов */ async saveToDatabaseStreaming( - prisma: PrismaClient, + drizzleDb: any, options: { batchSize?: number; // default: 50 maxProducts?: number; } = {} ): Promise { - const productService = new ProductService(prisma); + const productService = new ProductService(drizzleDb); let totalSaved = 0; // Получаем магазин один раз в начале diff --git a/src/scripts/analyze-category-nulls.ts b/src/scripts/analyze-category-nulls.ts index dc0ea7a..a35d385 100644 --- a/src/scripts/analyze-category-nulls.ts +++ b/src/scripts/analyze-category-nulls.ts @@ -1,93 +1,46 @@ import 'dotenv/config'; -import { connectDatabase, disconnectDatabase, prisma } from '../config/database.js'; +import { connectDatabase, disconnectDatabase, db } from '../config/database.js'; +import { products, categories } from '../db/schema.js'; +import { isNull, and, not } from 'drizzle-orm'; import { Logger } from '../utils/logger.js'; async function main() { try { await connectDatabase(); - // Check total products and null categoryId count - const totalProducts = await prisma.product.count(); - const nullCategoryCount = await prisma.product.count({ - where: { categoryId: null } - }); - const withCategoryCount = await prisma.product.count({ - where: { categoryId: { not: null } } - }); + const allProducts = await db.select().from(products); + const totalProducts = allProducts.length; + const nullCategoryCount = allProducts.filter(p => p.categoryId === null).length; + const withCategoryCount = totalProducts - nullCategoryCount; Logger.info('\n📊 СТАТИСТИКА ПО КАТЕГОРИЯМ:'); Logger.info(`Всего товаров: ${totalProducts}`); Logger.info(`Товаров без категории (null): ${nullCategoryCount} (${((nullCategoryCount / totalProducts) * 100).toFixed(2)}%)`); Logger.info(`Товаров с категорией: ${withCategoryCount} (${((withCategoryCount / totalProducts) * 100).toFixed(2)}%)`); - // Check total categories - const totalCategories = await prisma.category.count(); - Logger.info(`\nВсего категорий в БД: ${totalCategories}`); + const allCategories = await db.select().from(categories); + Logger.info(`\nВсего категорий в БД: ${allCategories.length}`); - // Sample categories - if (totalCategories > 0) { - const sampleCategories = await prisma.category.findMany({ - take: 5, - select: { - id: true, - externalId: true, - name: true, - _count: { - select: { products: true } - } - } - }); + if (allCategories.length > 0) { + const sampleCategories = allCategories.slice(0, 5); Logger.info('\n📁 Примеры категорий:'); - sampleCategories.forEach(cat => { - Logger.info(` - [${cat.externalId}] ${cat.name} (товаров: ${cat._count.products})`); + sampleCategories.forEach((cat: any) => { + const productsCount = allProducts.filter(p => p.categoryId === cat.id).length; + Logger.info(` - [${cat.externalId}] ${cat.name} (товаров: ${productsCount})`); }); } - // Sample products without categories - const productsWithoutCategory = await prisma.product.findMany({ - where: { categoryId: null }, - take: 5, - select: { - id: true, - externalId: true, - name: true, - currentPrice: true - } - }); - - Logger.info('\n❌ Примеры товаров БЕЗ категории:'); - productsWithoutCategory.forEach(p => { - Logger.info(` - [${p.externalId}] ${p.name} (₽${p.currentPrice})`); - }); - - // Sample products with categories - const productsWithCategory = await prisma.product.findMany({ - where: { categoryId: { not: null } }, - take: 5, - select: { - id: true, - externalId: true, - name: true, - currentPrice: true, - category: { - select: { - externalId: true, - name: true - } - } - } - }); - - if (productsWithCategory.length > 0) { - Logger.info('\n✅ Примеры товаров С категорией:'); - productsWithCategory.forEach(p => { - Logger.info(` - [${p.externalId}] ${p.name} → [${p.category?.externalId}] ${p.category?.name}`); + const productsWithoutCategory = allProducts.filter(p => p.categoryId === null).slice(0, 5); + if (productsWithoutCategory.length > 0) { + Logger.info('\n📦 Примеры товаров без категории:'); + productsWithoutCategory.forEach((p: any) => { + Logger.info(` - [${p.externalId}] ${p.name}`); }); } } catch (error) { - Logger.error('❌ Ошибка при анализе:', error); + Logger.error('❌ Ошибка:', error); process.exit(1); } finally { await disconnectDatabase(); diff --git a/src/scripts/check-product-details.ts b/src/scripts/check-product-details.ts index 7724e23..4cf32fd 100644 --- a/src/scripts/check-product-details.ts +++ b/src/scripts/check-product-details.ts @@ -1,30 +1,17 @@ import 'dotenv/config'; -import { connectDatabase, disconnectDatabase, prisma } from '../config/database.js'; +import { connectDatabase, disconnectDatabase, db } from '../config/database.js'; +import { products } from '../db/schema.js'; import { Logger } from '../utils/logger.js'; async function main() { try { await connectDatabase(); - // Get a sample product with all fields - const product = await prisma.product.findFirst({ - select: { - id: true, - externalId: true, - name: true, - description: true, - currentPrice: true, - unit: true, - weight: true, - brand: true, - categoryId: true, - badges: true, - } - }); + const result = await db.select().from(products).limit(1); - if (product) { + if (result.length > 0) { Logger.info('=== ДЕТАЛИ ТОВАРА ИЗ БД ==='); - Logger.info(JSON.stringify(product, null, 2)); + Logger.info(JSON.stringify(result[0], null, 2)); } } catch (error) { diff --git a/src/scripts/enrich-product-details.ts b/src/scripts/enrich-product-details.ts index c6e2777..de220a7 100644 --- a/src/scripts/enrich-product-details.ts +++ b/src/scripts/enrich-product-details.ts @@ -1,5 +1,5 @@ import 'dotenv/config'; -import { prisma } from '../config/database.js'; +import { db } from '../config/database.js'; import { MagnitApiScraper } from '../scrapers/api/magnit/MagnitApiScraper.js'; import { ProductService } from '../services/product/ProductService.js'; import { ProductParser } from '../services/parser/ProductParser.js'; @@ -33,7 +33,7 @@ async function main() { } // Инициализация - const productService = new ProductService(prisma); + const productService = new ProductService(db); const scraper = new MagnitApiScraper({ storeCode, rateLimitDelay: 300, diff --git a/src/scripts/scrape-magnit-products.ts b/src/scripts/scrape-magnit-products.ts index d2f1058..fa9be2b 100644 --- a/src/scripts/scrape-magnit-products.ts +++ b/src/scripts/scrape-magnit-products.ts @@ -1,6 +1,6 @@ import 'dotenv/config'; import { MagnitApiScraper } from '../scrapers/api/magnit/MagnitApiScraper.js'; -import { connectDatabase, disconnectDatabase, prisma } from '../config/database.js'; +import { connectDatabase, disconnectDatabase, db } from '../config/database.js'; import { Logger } from '../utils/logger.js'; async function main() { @@ -64,7 +64,7 @@ async function main() { // STREAMING режим (рекомендуется для больших каталогов) Logger.info('📡 Использование потокового режима скрапинга'); - saved = await scraper.saveToDatabaseStreaming(prisma, { + saved = await scraper.saveToDatabaseStreaming(db, { batchSize: 50, maxProducts, }); @@ -77,7 +77,7 @@ async function main() { Logger.info(`📦 Получено товаров: ${products.length}`); if (products.length > 0) { - saved = await scraper.saveToDatabase(products, prisma); + saved = await scraper.saveToDatabase(products, db); } else { Logger.warn('⚠️ Товары не найдены'); } diff --git a/src/services/product/ProductService.ts b/src/services/product/ProductService.ts index 501a22c..4b56f70 100644 --- a/src/services/product/ProductService.ts +++ b/src/services/product/ProductService.ts @@ -1,4 +1,6 @@ -import { PrismaClient, Product, Store, Category } from '../../../generated/prisma/client.js'; +import { db } from '../../database/client.js'; +import { products, stores, categories } from '../../db/schema.js'; +import { eq, and, asc } from 'drizzle-orm'; import { Logger } from '../../utils/logger.js'; import { DatabaseError } from '../../utils/errors.js'; @@ -25,64 +27,124 @@ export interface CreateProductData { isDetailsFetched?: boolean; } -export class ProductService { - constructor(private prisma: PrismaClient) {} +export interface Product { + id: number; + externalId: string; + storeId: number; + categoryId: number | null; + name: string; + description: string | null; + url: string | null; + imageUrl: string | null; + currentPrice: string; + unit: string | null; + weight: string | null; + brand: string | null; + oldPrice: string | null; + discountPercent: string | null; + promotionEndDate: Date | null; + rating: string | null; + scoresCount: number | null; + commentsCount: number | null; + quantity: number | null; + badges: string | null; + isDetailsFetched: boolean; + createdAt: Date; + updatedAt: Date | null; +} + +export interface Store { + id: number; + name: string; + type: string; + code: string | null; + url: string | null; + region: string | null; + address: string | null; + createdAt: Date; + updatedAt: Date | null; +} + +export interface Category { + id: number; + externalId: number | null; + name: string; + parentId: number | null; + description: string | null; + createdAt: Date; + updatedAt: Date | null; +} + +export class ProductService { + constructor(private drizzleDb: typeof db) {} + + private get db() { + return this.drizzleDb; + } - /** - * Сохранение одного товара - */ async saveProduct(data: CreateProductData): Promise { try { - // Проверяем, существует ли товар - const existing = await this.prisma.product.findUnique({ - where: { - externalId_storeId: { - externalId: data.externalId, - storeId: data.storeId, - }, - }, - }); + const existing = await this.db + .select() + .from(products) + .where( + and( + eq(products.externalId, data.externalId), + eq(products.storeId, data.storeId) + ) + ) + .limit(1); - if (existing) { - // Обновляем существующий товар + if (existing.length > 0) { Logger.debug(`Обновление товара: ${data.externalId}`); - // Если isDetailsFetched не передан явно, сохраняем текущее значение const updateData: any = { name: data.name, description: data.description, url: data.url, imageUrl: data.imageUrl, - currentPrice: data.currentPrice, + currentPrice: data.currentPrice.toString(), unit: data.unit, weight: data.weight, brand: data.brand, - oldPrice: data.oldPrice, - discountPercent: data.discountPercent, + oldPrice: data.oldPrice?.toString(), + discountPercent: data.discountPercent?.toString(), promotionEndDate: data.promotionEndDate, - rating: data.rating, + rating: data.rating?.toString(), scoresCount: data.scoresCount, commentsCount: data.commentsCount, quantity: data.quantity, badges: data.badges, categoryId: data.categoryId, + updatedAt: new Date(), }; - // Обновляем isDetailsFetched только если передано явно if (data.isDetailsFetched !== undefined) { updateData.isDetailsFetched = data.isDetailsFetched; } - return await this.prisma.product.update({ - where: { id: existing.id }, - data: updateData, - }); + const result = await this.db + .update(products) + .set(updateData) + .where(eq(products.id, existing[0].id)) + .returning(); + + return result[0] as Product; } else { - // Создаем новый товар Logger.debug(`Создание нового товара: ${data.externalId}`); - return await this.prisma.product.create({ - data, - }); + const result = await this.db + .insert(products) + .values({ + ...data, + currentPrice: data.currentPrice.toString(), + oldPrice: data.oldPrice?.toString(), + discountPercent: data.discountPercent?.toString(), + rating: data.rating?.toString(), + updatedAt: new Date(), + }) + .returning(); + + return result[0] as Product; } } catch (error) { Logger.error('Ошибка сохранения товара:', error); @@ -93,22 +155,18 @@ export class ProductService { } } - /** - * Сохранение нескольких товаров батчами - */ - async saveProducts(products: CreateProductData[]): Promise { + async saveProducts(productsData: CreateProductData[]): Promise { try { - Logger.info(`Сохранение ${products.length} товаров...`); + Logger.info(`Сохранение ${productsData.length} товаров...`); let saved = 0; - // Обрабатываем батчами по 50 товаров const batchSize = 50; - for (let i = 0; i < products.length; i += batchSize) { - const batch = products.slice(i, i + batchSize); + for (let i = 0; i < productsData.length; i += batchSize) { + const batch = productsData.slice(i, i + batchSize); const promises = batch.map(product => this.saveProduct(product)); await Promise.all(promises); saved += batch.length; - Logger.info(`Сохранено товаров: ${saved}/${products.length}`); + Logger.info(`Сохранено товаров: ${saved}/${productsData.length}`); } Logger.info(`✅ Всего сохранено товаров: ${saved}`); @@ -119,22 +177,23 @@ export class ProductService { } } - /** - * Поиск товара по externalId и storeId - */ async findByExternalId( externalId: string, storeId: number ): Promise { try { - return await this.prisma.product.findUnique({ - where: { - externalId_storeId: { - externalId, - storeId, - }, - }, - }); + const result = await this.db + .select() + .from(products) + .where( + and( + eq(products.externalId, externalId), + eq(products.storeId, storeId) + ) + ) + .limit(1); + + return result.length > 0 ? (result[0] as Product) : null; } catch (error) { Logger.error('Ошибка поиска товара:', error); throw new DatabaseError( @@ -144,30 +203,33 @@ export class ProductService { } } - /** - * Получение или создание магазина - */ async getOrCreateStore( code: string, name: string = 'Магнит' ): Promise { try { - let store = await this.prisma.store.findFirst({ - where: { code }, - }); + const existing = await this.db + .select() + .from(stores) + .where(eq(stores.code, code)) + .limit(1); - if (!store) { - Logger.info(`Создание нового магазина: ${code}`); - store = await this.prisma.store.create({ - data: { - name, - type: 'web', - code, - }, - }); + if (existing.length > 0) { + return existing[0] as Store; } - return store; + Logger.info(`Создание нового магазина: ${code}`); + const result = await this.db + .insert(stores) + .values({ + name, + type: 'web', + code, + updatedAt: new Date(), + }) + .returning(); + + return result[0] as Store; } catch (error) { Logger.error('Ошибка получения/создания магазина:', error); throw new DatabaseError( @@ -177,35 +239,41 @@ export class ProductService { } } - /** - * Получение или создание категории - */ async getOrCreateCategory( externalId: number, name: string ): Promise { try { - let category = await this.prisma.category.findFirst({ - where: { externalId }, - }); + const existing = await this.db + .select() + .from(categories) + .where(eq(categories.externalId, externalId)) + .limit(1); - if (!category) { - Logger.info(`Создание новой категории: ${name} (${externalId})`); - category = await this.prisma.category.create({ - data: { - externalId, - name, - }, - }); - } else if (category.name !== name) { - // Обновляем название категории, если изменилось - category = await this.prisma.category.update({ - where: { id: category.id }, - data: { name }, - }); + if (existing.length > 0) { + const category = existing[0] as Category; + if (category.name !== name) { + const result = await this.db + .update(categories) + .set({ name, updatedAt: new Date() }) + .where(eq(categories.id, category.id)) + .returning(); + return result[0] as Category; + } + return category; } - return category; + Logger.info(`Создание новой категории: ${name} (${externalId})`); + const result = await this.db + .insert(categories) + .values({ + externalId, + name, + updatedAt: new Date(), + }) + .returning(); + + return result[0] as Category; } catch (error) { Logger.error('Ошибка получения/создания категории:', error); throw new DatabaseError( @@ -215,30 +283,33 @@ export class ProductService { } } - /** - * Получение товаров, для которых не были получены детали - */ async getProductsNeedingDetails(storeCode: string, limit?: number): Promise { try { - // Сначала находим store по code - const store = await this.prisma.store.findFirst({ - where: { code: storeCode }, - }); + const storeResult = await this.db + .select() + .from(stores) + .where(eq(stores.code, storeCode)) + .limit(1); - if (!store) { + if (storeResult.length === 0) { throw new DatabaseError(`Магазин с кодом ${storeCode} не найден`); } - return await this.prisma.product.findMany({ - where: { - storeId: store.id, - isDetailsFetched: false, - }, - take: limit, - orderBy: { - id: 'asc', - }, - }); + const store = storeResult[0]; + + const result = await this.db + .select() + .from(products) + .where( + and( + eq(products.storeId, store.id), + eq(products.isDetailsFetched, false) + ) + ) + .orderBy(asc(products.id)) + .limit(limit || 1000); + + return result as Product[]; } catch (error) { Logger.error('Ошибка получения товаров для обогащения:', error); throw new DatabaseError( @@ -248,9 +319,6 @@ export class ProductService { } } - /** - * Обновление деталей товара (бренд, описание, вес, единица измерения, рейтинг) - */ async updateProductDetails( externalId: string, storeId: number, @@ -267,18 +335,23 @@ export class ProductService { } ): Promise { try { - return await this.prisma.product.update({ - where: { - externalId_storeId: { - externalId, - storeId, - }, - }, - data: { + const result = await this.db + .update(products) + .set({ ...details, + rating: details.rating?.toString(), isDetailsFetched: true, - }, - }); + updatedAt: new Date(), + }) + .where( + and( + eq(products.externalId, externalId), + eq(products.storeId, storeId) + ) + ) + .returning(); + + return result[0] as Product; } catch (error) { Logger.error('Ошибка обновления деталей товара:', error); throw new DatabaseError( @@ -288,22 +361,23 @@ export class ProductService { } } - /** - * Отметить товар как обработанный (даже если детали не были получены) - */ async markAsDetailsFetched(externalId: string, storeId: number): Promise { try { - return await this.prisma.product.update({ - where: { - externalId_storeId: { - externalId, - storeId, - }, - }, - data: { + const result = await this.db + .update(products) + .set({ isDetailsFetched: true, - }, - }); + updatedAt: new Date(), + }) + .where( + and( + eq(products.externalId, externalId), + eq(products.storeId, storeId) + ) + ) + .returning(); + + return result[0] as Product; } catch (error) { Logger.error('Ошибка отметки товара как обработанного:', error); throw new DatabaseError( @@ -313,4 +387,3 @@ export class ProductService { } } } -