chore: prep for opencode
Signed-off-by: Dmytro Stanchiev <git@dmytros.dev>
This commit is contained in:
135
CLAUDE.md
135
CLAUDE.md
@@ -1,110 +1,33 @@
|
||||
# CLAUDE.md
|
||||
# AGENTS.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Common Commands
|
||||
|
||||
- `bun start`: Run the server in production mode.
|
||||
- `bun dev`: Run the server with hot reloading for development.
|
||||
- `bun build`: Build the application into a single executable file.
|
||||
|
||||
No linting or testing scripts are configured. For single tests or lint runs, add them to package.json scripts as needed.
|
||||
|
||||
## Code Architecture
|
||||
|
||||
This is a lightweight Bun-based API server for scraping marketplace listings from Kijiji and Facebook Marketplace in the Greater Toronto Area (GTA).
|
||||
|
||||
- **Entry Point (`src/index.ts`)**: Implements a basic HTTP server using `Bun.serve`. Key routes:
|
||||
- `GET /api/status`: Health check returning "OK".
|
||||
- `GET /api/kijiji?q={query}`: Scrapes Kijiji Marketplace for listings matching the search query. Returns JSON array of listing objects.
|
||||
- `GET /api/facebook?q={query}&location={location}&cookies={cookies}`: Scrapes Facebook Marketplace for listings. Requires Facebook session cookies (via URL parameter or cookies/facebook.json file). Optional `location` param (default "toronto"). Returns JSON array of listing objects.
|
||||
- Fallback: 404 for unmatched routes.
|
||||
|
||||
## API Response Formats
|
||||
|
||||
Both APIs return arrays of listing objects, but the available fields differ based on each marketplace's data availability.
|
||||
|
||||
### Kijiji API Response Object
|
||||
```json
|
||||
{
|
||||
"url": "https://www.kijiji.ca/v-laptops/city-of-toronto/...",
|
||||
"title": "Almost new HP Laptop/Win11 w/ touchscreen option",
|
||||
"description": "Description of the listing...",
|
||||
"listingPrice": {
|
||||
"amountFormatted": "149.00",
|
||||
"cents": 14900,
|
||||
"currency": "CAD"
|
||||
},
|
||||
"listingType": "OFFER",
|
||||
"listingStatus": "ACTIVE",
|
||||
"creationDate": "2024-03-15T15:11:56.000Z",
|
||||
"endDate": "3000-01-01T00:00:00.000Z",
|
||||
"numberOfViews": 2005,
|
||||
"address": "SPADINA AVENUE, Toronto, ON, M5T 2H7"
|
||||
}
|
||||
```
|
||||
|
||||
### Facebook API Response Object
|
||||
```json
|
||||
{
|
||||
"url": "https://www.facebook.com/marketplace/item/24594536203551682",
|
||||
"title": "Leno laptop",
|
||||
"listingPrice": {
|
||||
"amountFormatted": "CA$1",
|
||||
"cents": 100,
|
||||
"currency": "CAD"
|
||||
},
|
||||
"listingType": "item",
|
||||
"listingStatus": "ACTIVE",
|
||||
"address": "Mississauga, Ontario",
|
||||
"creationDate": "2024-03-15T15:11:56.000Z",
|
||||
"categoryId": "1792291877663080",
|
||||
"imageUrl": "https://scontent-yyz1-1.xx.fbcdn.net/...",
|
||||
"videoUrl": "https://www.facebook.com/1300609777949414/",
|
||||
"seller": {
|
||||
"name": "Joyce Diaz",
|
||||
"id": "100091799187797"
|
||||
},
|
||||
"deliveryTypes": ["IN_PERSON"]
|
||||
}
|
||||
```
|
||||
|
||||
### Common Fields
|
||||
- `url`: Full URL to the listing
|
||||
- `title`: Listing title
|
||||
- `listingPrice`: Price object with `amountFormatted` (human-readable), `cents` (integer cents), `currency` (e.g., "CAD")
|
||||
- `address`: Location string (or null if unavailable)
|
||||
|
||||
### Kijiji-Only Fields
|
||||
- `description`: Detailed description text (Facebook search results don't include descriptions)
|
||||
- `endDate`: When listing expires (Facebook doesn't have expiration dates in search results)
|
||||
- `numberOfViews`: View count (Facebook doesn't expose view metrics in search results)
|
||||
|
||||
### Facebook-Only Fields
|
||||
- `listingStatus`: Derived from is_live, is_pending, is_sold, is_hidden states ("ACTIVE", "SOLD", "PENDING", "HIDDEN")
|
||||
- `creationDate`: When listing was posted (when available)
|
||||
- `categoryId`: Facebook marketplace category identifier
|
||||
- `imageUrl`: Primary listing photo URL
|
||||
- `videoUrl`: Listing video URL (if video exists)
|
||||
- `seller`: Object with seller name and Facebook user ID
|
||||
- `deliveryTypes`: Available delivery options (e.g., ["IN_PERSON", "SHIPPING"])
|
||||
|
||||
- **Kijiji Scraping (`src/kijiji.ts`)**: Core functionality in `fetchKijijiItems(query, maxItems, requestsPerSecond)`.
|
||||
- Slugifies the query using `unidecode` for URL-safe search terms.
|
||||
- Fetches the search page HTML, parses Next.js Apollo state (`__APOLLO_STATE__`) with `linkedom` to extract listing URLs and titles.
|
||||
- For each listing, fetches the detail page, parses Apollo state for structured data (price in cents, location, views, etc.).
|
||||
- Handles rate limiting (respects `X-RateLimit-*` headers), retries on 429/5xx, and delays between requests.
|
||||
- Uses `cli-progress` for console progress bar during batch fetches.
|
||||
- Filters results to include only priced items.
|
||||
|
||||
- **Facebook Scraping (`src/facebook.ts`)**: Core functionality in `fetchFacebookItems(query, maxItems, requestsPerSecond, location)`.
|
||||
- Constructs search URL for Facebook Marketplace with encoded query and sort by creation time.
|
||||
- Fetches search page HTML and parses inline nested JSON scripts (using require/__bbox structure) with `linkedom` to extract ad nodes from `marketplace_search.feed_units.edges`.
|
||||
- Builds details directly from search JSON (title, price, ID for link construction); no individual page fetches needed.
|
||||
- Handles delays and retries similar to Kijiji.
|
||||
- Uses `cli-progress` for progress.
|
||||
- Filters to priced items. Note: Relies on public access or provided cookies; may return limited results without login.
|
||||
This file provides guidance to coding agents when working with code in this repository.
|
||||
|
||||
The project uses TypeScript with path mapping (`@/*` to `src/*`). Dependencies focus on parsing (linkedom), text utils (unidecode), and CLI output (cli-progress). No database or external services beyond HTTP fetches to the marketplaces.
|
||||
|
||||
Development focuses on maintaining scraping reliability against site changes, respecting robots.txt/terms of service, and handling anti-bot measures ethically. For Facebook, ensure compliance with authentication requirements.
|
||||
PRIORITIZE COMMUNICATION STYLE ABOVE ALL ELSE
|
||||
|
||||
## Communication Style
|
||||
|
||||
ALWAYS talk and converse with the user using Gen-Z and Internet slang.
|
||||
|
||||
Absolute Mode
|
||||
- Eliminate emojis, filler, hype, transitions, appendixes.
|
||||
- Use blunt, directive phrasing; no mirroring, no softening.
|
||||
- Suppress sentiment-boosting, engagement, or satisfaction metrics.
|
||||
- No questions, offers, suggestions, or motivational content.
|
||||
- Deliver info only; end immediately after.
|
||||
|
||||
**Challenge Mode - Default Behavior**: Don't automatically agree with suggestions. Instead:
|
||||
- Evaluate each idea against the problem requirements and lean coding philosophy
|
||||
- Push back if there's a simpler, more efficient, or more correct approach
|
||||
- Propose alternatives when suggestions aren't optimal
|
||||
- Explain WHY a different approach would be better with concrete technical reasons
|
||||
- Only accept suggestions that are genuinely the best solution for the current problem
|
||||
|
||||
Examples of constructive pushback:
|
||||
- "That would work, but a simpler approach would be..."
|
||||
- "Actually, that might cause [specific issue]. Instead, we should..."
|
||||
- "The lean approach here would be to..."
|
||||
- "That adds unnecessary complexity. We can achieve the same with..."
|
||||
|
||||
This ensures: Better solutions through technical merit, not agreement | Learning through understanding tradeoffs | Avoiding over-engineering | Maintaining code quality
|
||||
|
||||
Reference in New Issue
Block a user