Skip to content

Commit 16e476e

Browse files
oschwaldclaude
andcommitted
Add CLAUDE.md for Claude Code guidance
This file provides documentation for Claude Code when working with this repository, including architecture, testing conventions, and development workflow. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 04e2e60 commit 16e476e

File tree

1 file changed

+398
-0
lines changed

1 file changed

+398
-0
lines changed

CLAUDE.md

Lines changed: 398 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,398 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
**minfraud-api-node** is MaxMind's official Node.js/TypeScript client library for the minFraud web services:
8+
- **minFraud Score**: Returns a risk score (0.01-99) indicating fraud likelihood
9+
- **minFraud Insights**: Score + additional data about IP, email, device, and shipping/billing addresses
10+
- **minFraud Factors**: Insights + risk score reasons and subscores (deprecated)
11+
- **Report Transactions API**: Report transaction outcomes to improve fraud detection
12+
13+
The library is server-side only and provides strongly-typed request/response models for fraud detection.
14+
15+
**Key Technologies:**
16+
- TypeScript with strict type checking
17+
- Node.js 18+ (targets active LTS versions)
18+
- Uses Node.js built-in `fetch` for HTTP requests
19+
- Jest for testing
20+
- ESLint + Prettier for code quality
21+
- TypeDoc for API documentation
22+
- Depends on @maxmind/geoip2-node for IP geolocation data
23+
24+
## Code Architecture
25+
26+
### Package Structure
27+
28+
```
29+
src/
30+
├── request/ # Transaction input models (Device, Email, Billing, etc.)
31+
│ ├── transaction.ts # Main Transaction class that aggregates all inputs
32+
│ └── *.ts # Individual request component classes
33+
├── response/
34+
│ ├── models/ # Response models (Score, Insights, Factors)
35+
│ ├── records.ts # TypeScript interfaces for response data records
36+
│ └── web-records.ts # Raw API response types (snake_case)
37+
├── webServiceClient.ts # HTTP client for minFraud web services
38+
├── constants.ts # Enums for API values (EventType, Processor, etc.)
39+
├── utils.ts # Utility functions (camelCase/snake_case conversion)
40+
├── errors.ts # Custom error classes
41+
└── types.ts # Type definitions
42+
```
43+
44+
### Key Design Patterns
45+
46+
#### 1. **Request/Response Transformation**
47+
48+
Requests use camelCase and are converted to snake_case for the API:
49+
```typescript
50+
// User provides: new Device({ ipAddress: '1.2.3.4' })
51+
// Sent to API: { ip_address: '1.2.3.4' }
52+
```
53+
54+
Responses use snake_case and are converted to camelCase in model constructors:
55+
```typescript
56+
// API returns: { risk_score: 50, funds_remaining: 10.50 }
57+
// Model exposes: response.riskScore, response.fundsRemaining
58+
```
59+
60+
The `camelizeResponse()` utility in `utils.ts` handles deep conversion for response data.
61+
The `snakecaseKeys()` utility converts request objects to snake_case.
62+
63+
#### 2. **Model Inheritance Hierarchy**
64+
65+
Response models follow clear inheritance:
66+
- `Score` → base model with risk score, disposition, warnings
67+
- `Insights` extends `Score` → adds billing/shipping addresses, device, email, credit card data
68+
- `Factors` extends `Insights` → adds risk score reasons and subscores (deprecated)
69+
70+
#### 3. **Transaction Builder Pattern**
71+
72+
Transactions are composed of optional components:
73+
```typescript
74+
const transaction = new minFraud.Transaction({
75+
device: new minFraud.Device({ ipAddress: '1.2.3.4' }),
76+
email: new minFraud.Email({ address: '[email protected]' }),
77+
billing: new minFraud.Billing({ /* ... */ }),
78+
// ... other components
79+
});
80+
```
81+
82+
Each component validates its inputs in the constructor and throws `ArgumentError` for invalid data.
83+
84+
#### 4. **Web Service Client Pattern**
85+
86+
The `WebServiceClient` provides direct methods for each endpoint:
87+
```typescript
88+
const client = new WebServiceClient(accountID, licenseKey, timeout, host);
89+
const response = await client.score(transaction);
90+
const response = await client.insights(transaction);
91+
const response = await client.factors(transaction);
92+
await client.reportTransaction(report);
93+
```
94+
95+
#### 5. **Type Definitions Pattern**
96+
97+
**IMPORTANT:** When defining enum-like fields with a fixed set of values:
98+
99+
1. **Define union types in `web-records.ts`** for the snake_case API response:
100+
```typescript
101+
export type CreditCardType = 'charge' | 'credit' | 'debit';
102+
export type EmailDomainClassification = 'business' | 'education' | 'government' | 'isp_email';
103+
```
104+
105+
2. **Import and reuse these types in `records.ts`** for the camelCase public API:
106+
```typescript
107+
import {
108+
CreditCardType,
109+
DispositionAction,
110+
DispositionReason,
111+
EmailDomainClassification,
112+
EmailDomainVisitStatus,
113+
} from './web-records';
114+
115+
export interface CreditCardRecord {
116+
readonly type: CreditCardType; // Use the imported type, NOT string
117+
}
118+
```
119+
120+
**Do NOT use plain `string` type when a union type exists in web-records.ts.** This pattern:
121+
- Provides better type safety for library consumers
122+
- Maintains a single source of truth for enum values
123+
- Follows the established pattern for `CreditCardType`, `DispositionAction`, `DispositionReason`, etc.
124+
125+
**Exception:** Use plain `string` only when the API documentation explicitly states "additional values may be added in the future" AND you want to allow any string value (e.g., `phone.numberType`).
126+
127+
#### 6. **GeoIP2 Integration**
128+
129+
The Insights and Factors responses include IP geolocation data from the @maxmind/geoip2-node library:
130+
```typescript
131+
// In Insights constructor:
132+
const insights = new GeoInsights(response.ip_address) as records.IpAddress;
133+
// Augment with minFraud-specific fields
134+
insights.country.isHighRisk = response.ip_address.country.is_high_risk;
135+
insights.risk = response.ip_address.risk;
136+
```
137+
138+
## Testing Conventions
139+
140+
### Running Tests
141+
142+
```bash
143+
# Install dependencies
144+
npm install
145+
146+
# Run all tests
147+
npm test
148+
149+
# Run tests in watch mode
150+
npm run test:watch
151+
152+
# Run specific test file
153+
npx jest src/webServiceClient.spec.ts
154+
```
155+
156+
### Linting and Building
157+
158+
```bash
159+
# Lint code (ESLint + TypeScript)
160+
npm run lint
161+
162+
# Format code (Prettier)
163+
npm run prettier:ts
164+
npm run prettier:json
165+
166+
# Build TypeScript
167+
npm run build
168+
169+
# Build and deploy documentation
170+
npm run build:docs
171+
npm run deploy:docs
172+
```
173+
174+
### Test Structure
175+
176+
Tests use `.spec.ts` files co-located with source:
177+
- `src/webServiceClient.spec.ts` - Web service client tests
178+
- `src/request/*.spec.ts` - Request component tests
179+
- `src/response/models/*.spec.ts` - Response model tests
180+
- `src/utils.spec.ts` - Utility function tests
181+
- `e2e/` - End-to-end integration tests (JS and TS)
182+
183+
### Test Patterns
184+
185+
Tests typically use `nock` to mock HTTP responses:
186+
```typescript
187+
import nock from 'nock';
188+
189+
nock('https://minfraud.maxmind.com')
190+
.post('/minfraud/v2.0/score')
191+
.reply(200, { risk_score: 50, id: '...', /* ... */ });
192+
193+
const response = await client.score(transaction);
194+
expect(response.riskScore).toBe(50);
195+
```
196+
197+
When adding new fields to models:
198+
1. Update test fixtures/mocks to include the new field
199+
2. Add assertions to verify the field is properly mapped
200+
3. Test both presence and absence (undefined handling)
201+
4. Verify camelCase conversion from snake_case source
202+
203+
## Working with This Codebase
204+
205+
### Adding New Fields to Request Components
206+
207+
1. **Add the property** to the component class with validation:
208+
```typescript
209+
/**
210+
* Description of the field.
211+
*/
212+
public fieldName?: Type;
213+
```
214+
215+
2. **Update the constructor** if validation is needed:
216+
```typescript
217+
if (props.fieldName && !isValid(props.fieldName)) {
218+
throw new ArgumentError('Invalid fieldName');
219+
}
220+
this.fieldName = props.fieldName;
221+
```
222+
223+
3. **Add to the interface** (e.g., `DeviceProps`, `EmailProps`) for type safety
224+
225+
4. **Add tests** that verify validation and serialization
226+
227+
5. **Update CHANGELOG.md** with the change
228+
229+
### Adding New Fields to Response Models
230+
231+
1. **Add the property** with proper type annotation:
232+
```typescript
233+
/**
234+
* Description of the field, including availability (which endpoints).
235+
*/
236+
public readonly fieldName?: Type;
237+
```
238+
239+
2. **Update the constructor** to map from the response:
240+
```typescript
241+
// For simple fields:
242+
this.fieldName = response.field_name;
243+
244+
// For nested objects that need camelization:
245+
this.fieldName = this.maybeGet<records.FieldType>(response, 'field_name');
246+
```
247+
248+
3. **Update corresponding record interfaces** in `records.ts` or `web-records.ts`
249+
250+
4. **Add tests** that verify the field mapping and type
251+
252+
5. **Update CHANGELOG.md** with the change
253+
254+
### Adding New Constants/Enums
255+
256+
When the API adds new enum values (e.g., new payment processors, event types):
257+
258+
1. **Update `src/constants.ts`** with the new value:
259+
```typescript
260+
export enum Processor {
261+
Stripe = 'stripe',
262+
NewProcessor = 'new_processor',
263+
// ...
264+
}
265+
```
266+
267+
2. **Add tests** if the enum has validation logic
268+
269+
3. **Update CHANGELOG.md** with the change
270+
271+
### CHANGELOG.md Format
272+
273+
Always update `CHANGELOG.md` for user-facing changes.
274+
275+
**Important**: Do not add a date to changelog entries until release time.
276+
277+
- If the next version doesn't exist, create it as `X.Y.Z (unreleased)` or just `X.Y.Z` (the header format varies)
278+
- If it already exists without a date, add your changes there
279+
- The release date will be added when the version is actually released
280+
281+
```markdown
282+
8.2.0
283+
------------------
284+
285+
* Added `NewProcessor` to the `Processor` enum.
286+
* Added the output `/email/first_seen`. This is available as the
287+
`firstSeen` property on `response.email`.
288+
```
289+
290+
## Common Pitfalls and Solutions
291+
292+
### Problem: Incorrect snake_case to camelCase Mapping
293+
294+
API responses use snake_case but must be exposed as camelCase.
295+
296+
**Solution**: Use `camelizeResponse()` for nested objects:
297+
```typescript
298+
this.email = this.maybeGet<records.Email>(response, 'email');
299+
300+
private maybeGet<T>(response: Response, prop: keyof Response): T | undefined {
301+
return response[prop] ? (camelizeResponse(response[prop]) as T) : undefined;
302+
}
303+
```
304+
305+
### Problem: Request Validation Not Working
306+
307+
Request components should validate inputs in constructors.
308+
309+
**Solution**: Import validators from the `validator` package:
310+
```typescript
311+
import validator from 'validator';
312+
313+
if (!validator.isEmail(props.address)) {
314+
throw new ArgumentError('Invalid email address');
315+
}
316+
```
317+
318+
### Problem: Missing Error Handling
319+
320+
The client can throw various error types.
321+
322+
**Solution**: Check for error codes in catch blocks:
323+
```typescript
324+
try {
325+
const response = await client.score(transaction);
326+
} catch (error) {
327+
// error has shape: { code: string, error: string, url?: string }
328+
if (error.code === 'INSUFFICIENT_FUNDS') {
329+
// Handle insufficient funds
330+
}
331+
}
332+
```
333+
334+
### Problem: GeoIP2 Type Conflicts
335+
336+
The IP address data comes from @maxmind/geoip2-node and needs augmentation.
337+
338+
**Solution**: Cast the GeoInsights object and add minFraud-specific fields:
339+
```typescript
340+
const insights = new GeoInsights(response.ip_address) as records.IpAddress;
341+
insights.risk = response.ip_address.risk;
342+
```
343+
344+
## Code Style Requirements
345+
346+
- **TypeScript strict mode** - All files use strict type checking
347+
- **ESLint** - Configured with TypeScript ESLint rules (see `eslint.config.mjs`)
348+
- **Prettier** - Consistent formatting enforced
349+
- **Readonly response fields** - Response model properties are `readonly`
350+
- **Optional chaining** - Use `?.` for optional nested properties
351+
- **TypeDoc comments** - Document public APIs with JSDoc-style comments
352+
353+
## Development Workflow
354+
355+
### Setup
356+
```bash
357+
npm install
358+
```
359+
360+
### Before Committing
361+
```bash
362+
# Tidy code (auto-fix issues)
363+
precious tidy -g
364+
365+
# Lint code (check for issues)
366+
precious lint -g
367+
368+
# Run tests
369+
npm test
370+
371+
# Build
372+
npm run build
373+
```
374+
375+
Note: Precious is already set up and handles code formatting and linting. Use `precious tidy -g` to automatically fix issues, and `precious lint -g` to check for remaining problems.
376+
377+
### Version Requirements
378+
- **Node.js 18+** required (targets active LTS: 18, 20, 22)
379+
- Uses Node.js built-in `fetch` (no external HTTP libraries)
380+
- TypeScript 5.x
381+
382+
## Cross-Language Consistency
383+
384+
This library is part of MaxMind's multi-language client library ecosystem. When adding features:
385+
386+
- **Field names** should match other client libraries (PHP, Python, etc.) after camelCase conversion
387+
- **Model structure** should parallel other implementations where possible
388+
- **Error handling** patterns should be consistent
389+
- **Documentation style** should follow established patterns
390+
391+
Refer to other minFraud client implementations for guidance on new features.
392+
393+
## Additional Resources
394+
395+
- [API Documentation](https://maxmind.github.io/minfraud-api-node/)
396+
- [minFraud Web Services Docs](https://dev.maxmind.com/minfraud/)
397+
- [minFraud API Documentation](https://dev.maxmind.com/minfraud/api-documentation/)
398+
- GitHub Issues: https://github.com/maxmind/minfraud-api-node/issues

0 commit comments

Comments
 (0)