# AGENTS.md Guide for coding agents working in this repository. ## Repo reality This repo is an ESM TypeScript scraper focused on Magnit supermarket data. The active stack is Playwright, Axios, PostgreSQL, and Drizzle ORM. Use `package.json` and `src/` as source of truth. Treat `README.md` carefully. It still documents useful local setup like `docker-compose up -d`, but it also contains stale Prisma-era commands and descriptions that no longer match the code. Do not treat README Prisma commands as the current workflow. ## Package manager and commands Use `pnpm`. ```bash pnpm install pnpm exec playwright install chromium pnpm type-check pnpm build pnpm dev pnpm enrich pnpm test-db pnpm db:generate pnpm db:migrate pnpm db:push pnpm db:studio ``` Command mapping: * `pnpm type-check`, `tsc --noEmit` * `pnpm build`, compile TypeScript to `dist/` * `pnpm dev`, run `src/scripts/scrape-magnit-products.ts` * `pnpm enrich`, run `src/scripts/enrich-product-details.ts` * `pnpm test-db`, run `src/scripts/test-db-connection.ts` * `pnpm db:*`, Drizzle migration and studio commands There is no lint command, no automated test framework, and no single-test execution command today. ## Local DB setup and verification reality The README still documents `docker-compose up -d` for local PostgreSQL, and that is still the documented bootstrap step. Current source uses Drizzle, not Prisma, so do not describe `pnpm prisma:*` commands as current. Use the commands that actually exist for validation: * `pnpm type-check` * `pnpm build` * `pnpm test-db`, when DB setup or connectivity is involved * `pnpm dev`, when scraper behavior is involved * `pnpm enrich`, when enrichment flow is involved There is no Jest, Vitest, Mocha, or similar runner configured, no per-file test command, and no single-test pattern to follow. ## Direct script entry points Main scripts are `src/scripts/scrape-magnit-products.ts`, `src/scripts/enrich-product-details.ts`, and `src/scripts/test-db-connection.ts`. Direct execution example: ```bash tsx src/scripts/scrape-magnit-products.ts MAGNIT_STORE_CODE=992301 tsx src/scripts/scrape-magnit-products.ts ``` ## TypeScript and module conventions The project is ESM TypeScript. `package.json` sets `"type": "module"`. `tsconfig.json` uses `strict: true`, `module: "ESNext"`, and `moduleResolution: "bundler"`. Observed conventions: * Local imports use the `.js` extension * External imports are usually first, then local imports, but the repo is mixed * Match nearby file style instead of rewriting unrelated imports * Public APIs and service methods often have explicit types * Pragmatic `any` and casts are present around ORM and script boundaries * Do not invent a fake rule that every type must be explicit everywhere ## Naming and file naming Follow the naming already in the repo. * Classes, interfaces, and major service types use PascalCase * Functions, variables, and object properties use camelCase * Constants often use UPPER_SNAKE_CASE * Class and service files are usually PascalCase, for example `MagnitApiScraper.ts` * Utility, config, database, and schema files are usually lowercase, for example `logger.ts`, `errors.ts`, `client.ts`, `schema.ts` * Script files are kebab-case, for example `scrape-magnit-products.ts` ## Logging, comments, and errors Prefer `Logger` from `src/utils/logger.ts` for application code. It provides `info`, `error`, `warn`, and `debug`, with `debug` gated by `DEBUG === 'true'`. * `Logger` is preferred in scraper and service code * Plain `console.log` and `console.error` still appear in infra and test-style scripts * `src/config/database.ts` and `src/scripts/test-db-connection.ts` are current examples * Comments and log messages are often Russian, so keep language and tone consistent with nearby code Active error classes live in `src/utils/errors.ts`. * `DatabaseError` is actively used in `ProductService` * `APIError` is actively used in `MagnitApiScraper` * `ScraperError` exists, but it is not a common active pattern today Common style is to catch low-level failures, log the original error, and re-throw a domain-specific error when that pattern already exists nearby. ## Database and schema patterns The active DB layer is Drizzle. Source-backed facts: * DB client is `src/database/client.ts` * It exports `db = drizzle(pool, { schema })` * `src/config/database.ts` re-exports `db` and provides connect and disconnect helpers * Schema lives in `src/db/schema.ts` * Products have a composite unique constraint on `(externalId, storeId)` * Product price columns are decimal values * Parser code converts inbound kopecks to rubles before persistence Common query style uses Drizzle builders such as: * `.select().from(...).where(...)` * `.insert().values(...).returning()` * `.update().set(...).where(...).returning()` Batch size `50` is a common working number in product saving and scraper pagination defaults. ## Service and scraper flow Keep the existing responsibilities intact. * `MagnitApiScraper` owns Playwright session setup, cookie and device-id capture, API requests, retries, reinit behavior, pagination, and streaming batches * `ProductParser` transforms API payloads into `CreateProductData` and enrichment fields * `ProductService` owns store, category, and product persistence Main persistence flow: 1. `MagnitApiScraper` fetches product batches 2. `ProductParser` maps API items into DB-ready structures 3. `ProductService` creates or updates stores, categories, and products Prefer extending that flow over bypassing it. Avoid dropping raw Drizzle writes into scraper fetch logic when `ProductService` already owns the path. ## Scripts and direct DB access Production-style scraper flow leans on services. Some debug and utility scripts are more direct. * `src/scripts/scrape-magnit-products.ts` uses `connectDatabase`, `disconnectDatabase`, and passes `db` into scraper save methods * `src/scripts/enrich-product-details.ts` instantiates `ProductService` directly and coordinates enrichment batches * Debug or infra-style code may query `db` directly when narrowly focused on setup or inspection Match the local pattern instead of forcing extra abstraction into one-off scripts. ## Environment variables Common variables confirmed by source and docs: ```bash DATABASE_URL=postgresql://user:password@localhost:5432/supermarket MAGNIT_STORE_CODE=992301 DEBUG=true ``` Other runtime variables used by scripts include `MAGNIT_USE_STREAMING`, `MAGNIT_MAX_PRODUCTS`, `MAGNIT_STORE_TYPE`, `MAGNIT_CATALOG_TYPE`, `MAGNIT_HEADLESS`, `MAGNIT_PAGE_SIZE`, `MAGNIT_RATE_LIMIT_DELAY`, `MAGNIT_MAX_ITERATIONS`, `MAGNIT_RETRY_ATTEMPTS`, and `FETCH_OBJECT_INFO`. ## Cursor Requestly rule The repo includes a Cursor rule at `.cursor/rules/requestly-test-rules.mdc` for `.requestly-supermarket/**/*.json`. Preserve these points: * Use `rq.test()` for tests * Use `rq.expect()` for assertions * Read response data from `rq.response.body` and parse it as JSON before working with fields * Prices in these Requestly payloads are in kopecks, for example `24999` means `249.99` rubles ## Practical guidance Read nearby files before standardizing style. Prefer source over README when they disagree, document Drizzle not Prisma, and do not claim linting, automated tests, or single-test support that does not exist. When changing scraper persistence, check `MagnitApiScraper`, `ProductParser`, and `ProductService` together.