@a3api/node 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +160 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# @a3api/node
|
|
2
|
+
|
|
3
|
+
Official Node.js client for the [Arcadia Age API (A3)](https://www.a3api.io). Handles request construction, typed responses, automatic retries, and structured error handling.
|
|
4
|
+
|
|
5
|
+
- Full TypeScript types for requests and responses
|
|
6
|
+
- Automatic retry with exponential backoff (429, 5xx, network errors)
|
|
7
|
+
- Typed error classes for each failure mode
|
|
8
|
+
- ESM + CJS + type declarations
|
|
9
|
+
- Zero dependencies (uses native `fetch`)
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @a3api/node
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Requires Node.js 18 or later.
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { A3Client } from '@a3api/node';
|
|
23
|
+
|
|
24
|
+
const client = new A3Client({ apiKey: process.env.A3_API_KEY! });
|
|
25
|
+
|
|
26
|
+
const result = await client.assessAge({
|
|
27
|
+
os_signal: 'not-available',
|
|
28
|
+
user_country_code: 'US',
|
|
29
|
+
behavioral_metrics: {
|
|
30
|
+
avg_touch_precision: 0.72,
|
|
31
|
+
scroll_velocity: 1200,
|
|
32
|
+
form_completion_time_ms: 8500,
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
console.log(result.verdict); // "PROVISIONAL"
|
|
37
|
+
console.log(result.assessed_age_bracket); // "18-plus"
|
|
38
|
+
console.log(result.confidence_score); // 0.7
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Constructor Options
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
const client = new A3Client({
|
|
45
|
+
apiKey: 'your_api_key', // Required
|
|
46
|
+
baseUrl: 'https://...', // Default: https://api.a3api.io
|
|
47
|
+
timeout: 30_000, // Default: 30000 (ms)
|
|
48
|
+
maxRetries: 2, // Default: 2
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
| Option | Type | Default | Description |
|
|
53
|
+
|--------|------|---------|-------------|
|
|
54
|
+
| `apiKey` | `string` | *required* | Your A3 API key |
|
|
55
|
+
| `baseUrl` | `string` | `https://api.a3api.io` | API base URL |
|
|
56
|
+
| `timeout` | `number` | `30000` | Request timeout in milliseconds |
|
|
57
|
+
| `maxRetries` | `number` | `2` | Max retries for transient failures |
|
|
58
|
+
|
|
59
|
+
## `client.assessAge(request)`
|
|
60
|
+
|
|
61
|
+
Sends signals to `POST /v1/assurance/assess-age` and returns the assessment.
|
|
62
|
+
|
|
63
|
+
### Request
|
|
64
|
+
|
|
65
|
+
| Field | Type | Required | Description |
|
|
66
|
+
|-------|------|----------|-------------|
|
|
67
|
+
| `os_signal` | `OsSignal` | Yes | `'under-13'` \| `'13-15'` \| `'16-17'` \| `'18-plus'` \| `'not-available'` |
|
|
68
|
+
| `user_country_code` | `string` | Yes | ISO 3166-1 alpha-2 code (e.g., `'US'`) |
|
|
69
|
+
| `behavioral_metrics` | `BehavioralMetrics` | No | Touch precision, scroll velocity, form timing, etc. |
|
|
70
|
+
| `device_context` | `DeviceContext` | No | OS version, device model, accessibility settings |
|
|
71
|
+
| `contextual_signals` | `ContextualSignals` | No | IP type, timezone offset, referrer category |
|
|
72
|
+
| `account_longevity` | `AccountLongevity` | No | `{ account_age_days: number }` |
|
|
73
|
+
| `input_complexity` | `InputComplexity` | No | Autocorrect rate, word complexity score |
|
|
74
|
+
|
|
75
|
+
### Response
|
|
76
|
+
|
|
77
|
+
| Field | Type | Description |
|
|
78
|
+
|-------|------|-------------|
|
|
79
|
+
| `verdict` | `Verdict` | `'CONSISTENT'` \| `'OVERRIDE'` \| `'REVIEW'` \| `'PROVISIONAL'` |
|
|
80
|
+
| `assessed_age_bracket` | `AgeBracket` | `'under-13'` \| `'13-15'` \| `'16-17'` \| `'18-plus'` \| `'undetermined'` |
|
|
81
|
+
| `confidence_score` | `number` | 0–1 certainty in the assessed bracket (Pro/Scale plans only) |
|
|
82
|
+
| `os_signal_age_bracket` | `AgeBracket` | OS signal echoed back |
|
|
83
|
+
| `signal_overridden` | `boolean` | `true` when verdict is `OVERRIDE` |
|
|
84
|
+
| `evidence_tags` | `string[]` | Audit trail of which signals influenced the verdict (Pro/Scale plans only) |
|
|
85
|
+
| `verification_token` | `string` | HMAC-SHA256 signed cryptographic receipt — store in your logs |
|
|
86
|
+
|
|
87
|
+
## Error Handling
|
|
88
|
+
|
|
89
|
+
The client throws typed errors you can catch individually:
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
import {
|
|
93
|
+
A3Client,
|
|
94
|
+
A3ValidationError,
|
|
95
|
+
A3AuthenticationError,
|
|
96
|
+
A3RateLimitError,
|
|
97
|
+
A3ConnectionError,
|
|
98
|
+
} from '@a3api/node';
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
const result = await client.assessAge(request);
|
|
102
|
+
} catch (err) {
|
|
103
|
+
if (err instanceof A3ValidationError) {
|
|
104
|
+
// 400 — bad request body
|
|
105
|
+
console.error(err.validationErrors); // string[]
|
|
106
|
+
} else if (err instanceof A3AuthenticationError) {
|
|
107
|
+
// 401 — invalid API key
|
|
108
|
+
} else if (err instanceof A3RateLimitError) {
|
|
109
|
+
// 429 — rate limited (auto-retried, this means retries exhausted)
|
|
110
|
+
console.error(err.retryAfter); // ms until retry, if provided
|
|
111
|
+
} else if (err instanceof A3ConnectionError) {
|
|
112
|
+
// Network failure or timeout (auto-retried, retries exhausted)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
| Error Class | Status | Auto-Retried | Description |
|
|
118
|
+
|-------------|--------|:------------:|-------------|
|
|
119
|
+
| `A3ValidationError` | 400 | No | Request body failed validation |
|
|
120
|
+
| `A3AuthenticationError` | 401 | No | Missing or invalid API key |
|
|
121
|
+
| `A3RateLimitError` | 429 | Yes | Rate limit or quota exceeded |
|
|
122
|
+
| `A3ApiError` | 5xx | Yes | Server error |
|
|
123
|
+
| `A3ConnectionError` | — | Yes | Network failure or timeout |
|
|
124
|
+
|
|
125
|
+
## Usage with Express
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
import express from 'express';
|
|
129
|
+
import { A3Client } from '@a3api/node';
|
|
130
|
+
|
|
131
|
+
const app = express();
|
|
132
|
+
app.use(express.json());
|
|
133
|
+
|
|
134
|
+
const a3 = new A3Client({ apiKey: process.env.A3_API_KEY! });
|
|
135
|
+
|
|
136
|
+
app.post('/api/assess-age', async (req, res) => {
|
|
137
|
+
const result = await a3.assessAge({
|
|
138
|
+
os_signal: 'not-available',
|
|
139
|
+
user_country_code: 'US',
|
|
140
|
+
...req.body, // signals from @a3api/signals browser SDK
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
res.json({
|
|
144
|
+
verdict: result.verdict,
|
|
145
|
+
bracket: result.assessed_age_bracket,
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Links
|
|
151
|
+
|
|
152
|
+
- [Documentation](https://www.a3api.io/docs)
|
|
153
|
+
- [Web Integration Guide](https://www.a3api.io/docs/integration/web-integration)
|
|
154
|
+
- [API Reference](https://www.a3api.io/docs/integration/assess-age)
|
|
155
|
+
- [Quickstart Project](https://github.com/a3api/a3-quickstart)
|
|
156
|
+
- [Get an API Key](https://portal.a3api.io)
|
|
157
|
+
|
|
158
|
+
## License
|
|
159
|
+
|
|
160
|
+
MIT
|
package/dist/index.cjs
CHANGED
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/retry.ts","../src/version.ts","../src/client.ts"],"sourcesContent":["export { A3Client } from './client.js';\r\n\r\nexport type {\r\n A3ClientOptions,\r\n A3ErrorBody,\r\n AssessAgeRequest,\r\n AssessAgeResponse,\r\n OsSignal,\r\n Verdict,\r\n AgeBracket,\r\n ParentalConsentStatus,\r\n ConsentSource,\r\n FaceEstimationProvider,\r\n IpType,\r\n ReferrerCategory,\r\n BehavioralMetrics,\r\n DeviceContext,\r\n ContextualSignals,\r\n AccountLongevity,\r\n InputComplexity,\r\n FaceEstimationResult,\r\n} from './types.js';\r\n\r\nexport {\r\n A3ApiError,\r\n A3AuthenticationError,\r\n A3RateLimitError,\r\n A3ValidationError,\r\n A3ConnectionError,\r\n} from './errors.js';\r\n\r\nexport { SDK_VERSION } from './version.js';\r\n","import type { A3ErrorBody } from './types.js';\r\n\r\nexport class A3ApiError extends Error {\r\n readonly statusCode: number;\r\n readonly body: A3ErrorBody | undefined;\r\n\r\n constructor(message: string, statusCode: number, body?: A3ErrorBody) {\r\n super(message);\r\n this.name = 'A3ApiError';\r\n this.statusCode = statusCode;\r\n this.body = body;\r\n }\r\n}\r\n\r\nexport class A3AuthenticationError extends A3ApiError {\r\n constructor(body?: A3ErrorBody) {\r\n super(\r\n body?.message\r\n ? (Array.isArray(body.message) ? body.message.join(', ') : body.message)\r\n : 'Unauthorized',\r\n 401,\r\n body,\r\n );\r\n this.name = 'A3AuthenticationError';\r\n }\r\n}\r\n\r\nexport class A3RateLimitError extends A3ApiError {\r\n readonly retryAfter: number | undefined;\r\n\r\n constructor(retryAfter?: number, body?: A3ErrorBody) {\r\n super('Rate limit exceeded', 429, body);\r\n this.name = 'A3RateLimitError';\r\n this.retryAfter = retryAfter;\r\n }\r\n}\r\n\r\nexport class A3ValidationError extends A3ApiError {\r\n readonly validationErrors: string[];\r\n\r\n constructor(body?: A3ErrorBody) {\r\n const errors = body?.message\r\n ? (Array.isArray(body.message) ? body.message : [body.message])\r\n : [];\r\n super(\r\n errors.length > 0\r\n ? `Validation failed: ${errors.join(', ')}`\r\n : 'Validation failed',\r\n 400,\r\n body,\r\n );\r\n this.name = 'A3ValidationError';\r\n this.validationErrors = errors;\r\n }\r\n}\r\n\r\nexport class A3ConnectionError extends Error {\r\n readonly cause: unknown;\r\n\r\n constructor(message: string, cause?: unknown) {\r\n super(message);\r\n this.name = 'A3ConnectionError';\r\n this.cause = cause;\r\n }\r\n}\r\n","const RETRYABLE_STATUS_CODES = new Set([429, 500, 502, 503, 504]);\r\n\r\nexport function isRetryableStatus(status: number): boolean {\r\n return RETRYABLE_STATUS_CODES.has(status);\r\n}\r\n\r\nexport function parseRetryAfter(header: string | null): number | undefined {\r\n if (header == null || header === '') return undefined;\r\n const seconds = Number(header);\r\n if (!Number.isNaN(seconds) && seconds >= 0) {\r\n return seconds * 1000;\r\n }\r\n const date = Date.parse(header);\r\n if (!Number.isNaN(date)) {\r\n const ms = date - Date.now();\r\n return ms > 0 ? ms : 0;\r\n }\r\n return undefined;\r\n}\r\n\r\nexport function calculateDelay(\r\n attempt: number,\r\n baseDelayMs: number = 500,\r\n): number {\r\n const exponential = baseDelayMs * 2 ** attempt;\r\n return Math.random() * exponential;\r\n}\r\n\r\nexport async function withRetry<T>(\r\n fn: () => Promise<T>,\r\n opts: {\r\n maxRetries: number;\r\n shouldRetry: (error: unknown) => { retry: boolean; retryAfterMs?: number };\r\n },\r\n): Promise<T> {\r\n let lastError: unknown;\r\n\r\n for (let attempt = 0; attempt <= opts.maxRetries; attempt++) {\r\n try {\r\n return await fn();\r\n } catch (error) {\r\n lastError = error;\r\n\r\n if (attempt >= opts.maxRetries) break;\r\n\r\n const { retry, retryAfterMs } = opts.shouldRetry(error);\r\n if (!retry) break;\r\n\r\n const delay = retryAfterMs ?? calculateDelay(attempt);\r\n await new Promise((resolve) => setTimeout(resolve, delay));\r\n }\r\n }\r\n\r\n throw lastError;\r\n}\r\n","export const SDK_VERSION = '0.1.1';\r\n","import type {\r\n A3ClientOptions,\r\n A3ErrorBody,\r\n AssessAgeRequest,\r\n AssessAgeResponse,\r\n} from './types.js';\r\nimport {\r\n A3ApiError,\r\n A3AuthenticationError,\r\n A3ConnectionError,\r\n A3RateLimitError,\r\n A3ValidationError,\r\n} from './errors.js';\r\nimport { isRetryableStatus, parseRetryAfter, withRetry } from './retry.js';\r\nimport { SDK_VERSION } from './version.js';\r\n\r\nconst DEFAULT_BASE_URL = 'https://api.a3api.io';\r\nconst DEFAULT_TIMEOUT = 30_000;\r\nconst DEFAULT_MAX_RETRIES = 2;\r\n\r\nexport class A3Client {\r\n private readonly apiKey: string;\r\n private readonly baseUrl: string;\r\n private readonly timeout: number;\r\n private readonly maxRetries: number;\r\n\r\n constructor(options: A3ClientOptions) {\r\n if (!options.apiKey) {\r\n throw new Error('apiKey is required');\r\n }\r\n this.apiKey = options.apiKey;\r\n this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, '');\r\n this.timeout = options.timeout ?? DEFAULT_TIMEOUT;\r\n this.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;\r\n }\r\n\r\n async assessAge(request: AssessAgeRequest): Promise<AssessAgeResponse> {\r\n return withRetry(() => this._assessAge(request), {\r\n maxRetries: this.maxRetries,\r\n shouldRetry: (error) => {\r\n if (error instanceof A3RateLimitError) {\r\n return { retry: true, retryAfterMs: error.retryAfter };\r\n }\r\n if (error instanceof A3ApiError && isRetryableStatus(error.statusCode)) {\r\n return { retry: true };\r\n }\r\n if (error instanceof A3ConnectionError) {\r\n return { retry: true };\r\n }\r\n return { retry: false };\r\n },\r\n });\r\n }\r\n\r\n private async _assessAge(\r\n request: AssessAgeRequest,\r\n ): Promise<AssessAgeResponse> {\r\n const url = `${this.baseUrl}/v1/assurance/assess-age`;\r\n\r\n let response: Response;\r\n try {\r\n response = await fetch(url, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n Authorization: `Bearer ${this.apiKey}`,\r\n 'User-Agent': `a3api-node/${SDK_VERSION}`,\r\n },\r\n body: JSON.stringify(request),\r\n signal: AbortSignal.timeout(this.timeout),\r\n });\r\n } catch (error) {\r\n if (error instanceof DOMException && error.name === 'TimeoutError') {\r\n throw new A3ConnectionError(\r\n `Request timed out after ${this.timeout}ms`,\r\n error,\r\n );\r\n }\r\n throw new A3ConnectionError(\r\n error instanceof Error ? error.message : 'Network request failed',\r\n error,\r\n );\r\n }\r\n\r\n if (response.ok) {\r\n return (await response.json()) as AssessAgeResponse;\r\n }\r\n\r\n let body: A3ErrorBody | undefined;\r\n try {\r\n body = (await response.json()) as A3ErrorBody;\r\n } catch {\r\n // body may not be JSON\r\n }\r\n\r\n switch (response.status) {\r\n case 400:\r\n throw new A3ValidationError(body);\r\n case 401:\r\n throw new A3AuthenticationError(body);\r\n case 429: {\r\n const retryAfter = parseRetryAfter(\r\n response.headers.get('retry-after'),\r\n );\r\n throw new A3RateLimitError(retryAfter, body);\r\n }\r\n default:\r\n throw new A3ApiError(\r\n body?.error ?? `HTTP ${response.status}`,\r\n response.status,\r\n body,\r\n );\r\n }\r\n }\r\n}\r\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/retry.ts","../src/version.ts","../src/client.ts"],"sourcesContent":["export { A3Client } from './client.js';\r\n\r\nexport type {\r\n A3ClientOptions,\r\n A3ErrorBody,\r\n AssessAgeRequest,\r\n AssessAgeResponse,\r\n OsSignal,\r\n Verdict,\r\n AgeBracket,\r\n ParentalConsentStatus,\r\n ConsentSource,\r\n FaceEstimationProvider,\r\n IpType,\r\n ReferrerCategory,\r\n BehavioralMetrics,\r\n DeviceContext,\r\n ContextualSignals,\r\n AccountLongevity,\r\n InputComplexity,\r\n FaceEstimationResult,\r\n} from './types.js';\r\n\r\nexport {\r\n A3ApiError,\r\n A3AuthenticationError,\r\n A3RateLimitError,\r\n A3ValidationError,\r\n A3ConnectionError,\r\n} from './errors.js';\r\n\r\nexport { SDK_VERSION } from './version.js';\r\n","import type { A3ErrorBody } from './types.js';\r\n\r\nexport class A3ApiError extends Error {\r\n readonly statusCode: number;\r\n readonly body: A3ErrorBody | undefined;\r\n\r\n constructor(message: string, statusCode: number, body?: A3ErrorBody) {\r\n super(message);\r\n this.name = 'A3ApiError';\r\n this.statusCode = statusCode;\r\n this.body = body;\r\n }\r\n}\r\n\r\nexport class A3AuthenticationError extends A3ApiError {\r\n constructor(body?: A3ErrorBody) {\r\n super(\r\n body?.message\r\n ? (Array.isArray(body.message) ? body.message.join(', ') : body.message)\r\n : 'Unauthorized',\r\n 401,\r\n body,\r\n );\r\n this.name = 'A3AuthenticationError';\r\n }\r\n}\r\n\r\nexport class A3RateLimitError extends A3ApiError {\r\n readonly retryAfter: number | undefined;\r\n\r\n constructor(retryAfter?: number, body?: A3ErrorBody) {\r\n super('Rate limit exceeded', 429, body);\r\n this.name = 'A3RateLimitError';\r\n this.retryAfter = retryAfter;\r\n }\r\n}\r\n\r\nexport class A3ValidationError extends A3ApiError {\r\n readonly validationErrors: string[];\r\n\r\n constructor(body?: A3ErrorBody) {\r\n const errors = body?.message\r\n ? (Array.isArray(body.message) ? body.message : [body.message])\r\n : [];\r\n super(\r\n errors.length > 0\r\n ? `Validation failed: ${errors.join(', ')}`\r\n : 'Validation failed',\r\n 400,\r\n body,\r\n );\r\n this.name = 'A3ValidationError';\r\n this.validationErrors = errors;\r\n }\r\n}\r\n\r\nexport class A3ConnectionError extends Error {\r\n readonly cause: unknown;\r\n\r\n constructor(message: string, cause?: unknown) {\r\n super(message);\r\n this.name = 'A3ConnectionError';\r\n this.cause = cause;\r\n }\r\n}\r\n","const RETRYABLE_STATUS_CODES = new Set([429, 500, 502, 503, 504]);\r\n\r\nexport function isRetryableStatus(status: number): boolean {\r\n return RETRYABLE_STATUS_CODES.has(status);\r\n}\r\n\r\nexport function parseRetryAfter(header: string | null): number | undefined {\r\n if (header == null || header === '') return undefined;\r\n const seconds = Number(header);\r\n if (!Number.isNaN(seconds) && seconds >= 0) {\r\n return seconds * 1000;\r\n }\r\n const date = Date.parse(header);\r\n if (!Number.isNaN(date)) {\r\n const ms = date - Date.now();\r\n return ms > 0 ? ms : 0;\r\n }\r\n return undefined;\r\n}\r\n\r\nexport function calculateDelay(\r\n attempt: number,\r\n baseDelayMs: number = 500,\r\n): number {\r\n const exponential = baseDelayMs * 2 ** attempt;\r\n return Math.random() * exponential;\r\n}\r\n\r\nexport async function withRetry<T>(\r\n fn: () => Promise<T>,\r\n opts: {\r\n maxRetries: number;\r\n shouldRetry: (error: unknown) => { retry: boolean; retryAfterMs?: number };\r\n },\r\n): Promise<T> {\r\n let lastError: unknown;\r\n\r\n for (let attempt = 0; attempt <= opts.maxRetries; attempt++) {\r\n try {\r\n return await fn();\r\n } catch (error) {\r\n lastError = error;\r\n\r\n if (attempt >= opts.maxRetries) break;\r\n\r\n const { retry, retryAfterMs } = opts.shouldRetry(error);\r\n if (!retry) break;\r\n\r\n const delay = retryAfterMs ?? calculateDelay(attempt);\r\n await new Promise((resolve) => setTimeout(resolve, delay));\r\n }\r\n }\r\n\r\n throw lastError;\r\n}\r\n","export const SDK_VERSION = '0.1.2';\r\n","import type {\r\n A3ClientOptions,\r\n A3ErrorBody,\r\n AssessAgeRequest,\r\n AssessAgeResponse,\r\n} from './types.js';\r\nimport {\r\n A3ApiError,\r\n A3AuthenticationError,\r\n A3ConnectionError,\r\n A3RateLimitError,\r\n A3ValidationError,\r\n} from './errors.js';\r\nimport { isRetryableStatus, parseRetryAfter, withRetry } from './retry.js';\r\nimport { SDK_VERSION } from './version.js';\r\n\r\nconst DEFAULT_BASE_URL = 'https://api.a3api.io';\r\nconst DEFAULT_TIMEOUT = 30_000;\r\nconst DEFAULT_MAX_RETRIES = 2;\r\n\r\nexport class A3Client {\r\n private readonly apiKey: string;\r\n private readonly baseUrl: string;\r\n private readonly timeout: number;\r\n private readonly maxRetries: number;\r\n\r\n constructor(options: A3ClientOptions) {\r\n if (!options.apiKey) {\r\n throw new Error('apiKey is required');\r\n }\r\n this.apiKey = options.apiKey;\r\n this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, '');\r\n this.timeout = options.timeout ?? DEFAULT_TIMEOUT;\r\n this.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;\r\n }\r\n\r\n async assessAge(request: AssessAgeRequest): Promise<AssessAgeResponse> {\r\n return withRetry(() => this._assessAge(request), {\r\n maxRetries: this.maxRetries,\r\n shouldRetry: (error) => {\r\n if (error instanceof A3RateLimitError) {\r\n return { retry: true, retryAfterMs: error.retryAfter };\r\n }\r\n if (error instanceof A3ApiError && isRetryableStatus(error.statusCode)) {\r\n return { retry: true };\r\n }\r\n if (error instanceof A3ConnectionError) {\r\n return { retry: true };\r\n }\r\n return { retry: false };\r\n },\r\n });\r\n }\r\n\r\n private async _assessAge(\r\n request: AssessAgeRequest,\r\n ): Promise<AssessAgeResponse> {\r\n const url = `${this.baseUrl}/v1/assurance/assess-age`;\r\n\r\n let response: Response;\r\n try {\r\n response = await fetch(url, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n Authorization: `Bearer ${this.apiKey}`,\r\n 'User-Agent': `a3api-node/${SDK_VERSION}`,\r\n },\r\n body: JSON.stringify(request),\r\n signal: AbortSignal.timeout(this.timeout),\r\n });\r\n } catch (error) {\r\n if (error instanceof DOMException && error.name === 'TimeoutError') {\r\n throw new A3ConnectionError(\r\n `Request timed out after ${this.timeout}ms`,\r\n error,\r\n );\r\n }\r\n throw new A3ConnectionError(\r\n error instanceof Error ? error.message : 'Network request failed',\r\n error,\r\n );\r\n }\r\n\r\n if (response.ok) {\r\n return (await response.json()) as AssessAgeResponse;\r\n }\r\n\r\n let body: A3ErrorBody | undefined;\r\n try {\r\n body = (await response.json()) as A3ErrorBody;\r\n } catch {\r\n // body may not be JSON\r\n }\r\n\r\n switch (response.status) {\r\n case 400:\r\n throw new A3ValidationError(body);\r\n case 401:\r\n throw new A3AuthenticationError(body);\r\n case 429: {\r\n const retryAfter = parseRetryAfter(\r\n response.headers.get('retry-after'),\r\n );\r\n throw new A3RateLimitError(retryAfter, body);\r\n }\r\n default:\r\n throw new A3ApiError(\r\n body?.error ?? `HTTP ${response.status}`,\r\n response.status,\r\n body,\r\n );\r\n }\r\n }\r\n}\r\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -106,6 +106,6 @@ declare class A3ConnectionError extends Error {
|
|
|
106
106
|
constructor(message: string, cause?: unknown);
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
declare const SDK_VERSION = "0.1.
|
|
109
|
+
declare const SDK_VERSION = "0.1.2";
|
|
110
110
|
|
|
111
111
|
export { A3ApiError, A3AuthenticationError, A3Client, type A3ClientOptions, A3ConnectionError, type A3ErrorBody, A3RateLimitError, A3ValidationError, type AccountLongevity, type AgeBracket, type AssessAgeRequest, type AssessAgeResponse, type BehavioralMetrics, type ConsentSource, type ContextualSignals, type DeviceContext, type FaceEstimationProvider, type FaceEstimationResult, type InputComplexity, type IpType, type OsSignal, type ParentalConsentStatus, type ReferrerCategory, SDK_VERSION, type Verdict };
|
package/dist/index.d.ts
CHANGED
|
@@ -106,6 +106,6 @@ declare class A3ConnectionError extends Error {
|
|
|
106
106
|
constructor(message: string, cause?: unknown);
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
declare const SDK_VERSION = "0.1.
|
|
109
|
+
declare const SDK_VERSION = "0.1.2";
|
|
110
110
|
|
|
111
111
|
export { A3ApiError, A3AuthenticationError, A3Client, type A3ClientOptions, A3ConnectionError, type A3ErrorBody, A3RateLimitError, A3ValidationError, type AccountLongevity, type AgeBracket, type AssessAgeRequest, type AssessAgeResponse, type BehavioralMetrics, type ConsentSource, type ContextualSignals, type DeviceContext, type FaceEstimationProvider, type FaceEstimationResult, type InputComplexity, type IpType, type OsSignal, type ParentalConsentStatus, type ReferrerCategory, SDK_VERSION, type Verdict };
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/retry.ts","../src/version.ts","../src/client.ts"],"sourcesContent":["import type { A3ErrorBody } from './types.js';\r\n\r\nexport class A3ApiError extends Error {\r\n readonly statusCode: number;\r\n readonly body: A3ErrorBody | undefined;\r\n\r\n constructor(message: string, statusCode: number, body?: A3ErrorBody) {\r\n super(message);\r\n this.name = 'A3ApiError';\r\n this.statusCode = statusCode;\r\n this.body = body;\r\n }\r\n}\r\n\r\nexport class A3AuthenticationError extends A3ApiError {\r\n constructor(body?: A3ErrorBody) {\r\n super(\r\n body?.message\r\n ? (Array.isArray(body.message) ? body.message.join(', ') : body.message)\r\n : 'Unauthorized',\r\n 401,\r\n body,\r\n );\r\n this.name = 'A3AuthenticationError';\r\n }\r\n}\r\n\r\nexport class A3RateLimitError extends A3ApiError {\r\n readonly retryAfter: number | undefined;\r\n\r\n constructor(retryAfter?: number, body?: A3ErrorBody) {\r\n super('Rate limit exceeded', 429, body);\r\n this.name = 'A3RateLimitError';\r\n this.retryAfter = retryAfter;\r\n }\r\n}\r\n\r\nexport class A3ValidationError extends A3ApiError {\r\n readonly validationErrors: string[];\r\n\r\n constructor(body?: A3ErrorBody) {\r\n const errors = body?.message\r\n ? (Array.isArray(body.message) ? body.message : [body.message])\r\n : [];\r\n super(\r\n errors.length > 0\r\n ? `Validation failed: ${errors.join(', ')}`\r\n : 'Validation failed',\r\n 400,\r\n body,\r\n );\r\n this.name = 'A3ValidationError';\r\n this.validationErrors = errors;\r\n }\r\n}\r\n\r\nexport class A3ConnectionError extends Error {\r\n readonly cause: unknown;\r\n\r\n constructor(message: string, cause?: unknown) {\r\n super(message);\r\n this.name = 'A3ConnectionError';\r\n this.cause = cause;\r\n }\r\n}\r\n","const RETRYABLE_STATUS_CODES = new Set([429, 500, 502, 503, 504]);\r\n\r\nexport function isRetryableStatus(status: number): boolean {\r\n return RETRYABLE_STATUS_CODES.has(status);\r\n}\r\n\r\nexport function parseRetryAfter(header: string | null): number | undefined {\r\n if (header == null || header === '') return undefined;\r\n const seconds = Number(header);\r\n if (!Number.isNaN(seconds) && seconds >= 0) {\r\n return seconds * 1000;\r\n }\r\n const date = Date.parse(header);\r\n if (!Number.isNaN(date)) {\r\n const ms = date - Date.now();\r\n return ms > 0 ? ms : 0;\r\n }\r\n return undefined;\r\n}\r\n\r\nexport function calculateDelay(\r\n attempt: number,\r\n baseDelayMs: number = 500,\r\n): number {\r\n const exponential = baseDelayMs * 2 ** attempt;\r\n return Math.random() * exponential;\r\n}\r\n\r\nexport async function withRetry<T>(\r\n fn: () => Promise<T>,\r\n opts: {\r\n maxRetries: number;\r\n shouldRetry: (error: unknown) => { retry: boolean; retryAfterMs?: number };\r\n },\r\n): Promise<T> {\r\n let lastError: unknown;\r\n\r\n for (let attempt = 0; attempt <= opts.maxRetries; attempt++) {\r\n try {\r\n return await fn();\r\n } catch (error) {\r\n lastError = error;\r\n\r\n if (attempt >= opts.maxRetries) break;\r\n\r\n const { retry, retryAfterMs } = opts.shouldRetry(error);\r\n if (!retry) break;\r\n\r\n const delay = retryAfterMs ?? calculateDelay(attempt);\r\n await new Promise((resolve) => setTimeout(resolve, delay));\r\n }\r\n }\r\n\r\n throw lastError;\r\n}\r\n","export const SDK_VERSION = '0.1.1';\r\n","import type {\r\n A3ClientOptions,\r\n A3ErrorBody,\r\n AssessAgeRequest,\r\n AssessAgeResponse,\r\n} from './types.js';\r\nimport {\r\n A3ApiError,\r\n A3AuthenticationError,\r\n A3ConnectionError,\r\n A3RateLimitError,\r\n A3ValidationError,\r\n} from './errors.js';\r\nimport { isRetryableStatus, parseRetryAfter, withRetry } from './retry.js';\r\nimport { SDK_VERSION } from './version.js';\r\n\r\nconst DEFAULT_BASE_URL = 'https://api.a3api.io';\r\nconst DEFAULT_TIMEOUT = 30_000;\r\nconst DEFAULT_MAX_RETRIES = 2;\r\n\r\nexport class A3Client {\r\n private readonly apiKey: string;\r\n private readonly baseUrl: string;\r\n private readonly timeout: number;\r\n private readonly maxRetries: number;\r\n\r\n constructor(options: A3ClientOptions) {\r\n if (!options.apiKey) {\r\n throw new Error('apiKey is required');\r\n }\r\n this.apiKey = options.apiKey;\r\n this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, '');\r\n this.timeout = options.timeout ?? DEFAULT_TIMEOUT;\r\n this.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;\r\n }\r\n\r\n async assessAge(request: AssessAgeRequest): Promise<AssessAgeResponse> {\r\n return withRetry(() => this._assessAge(request), {\r\n maxRetries: this.maxRetries,\r\n shouldRetry: (error) => {\r\n if (error instanceof A3RateLimitError) {\r\n return { retry: true, retryAfterMs: error.retryAfter };\r\n }\r\n if (error instanceof A3ApiError && isRetryableStatus(error.statusCode)) {\r\n return { retry: true };\r\n }\r\n if (error instanceof A3ConnectionError) {\r\n return { retry: true };\r\n }\r\n return { retry: false };\r\n },\r\n });\r\n }\r\n\r\n private async _assessAge(\r\n request: AssessAgeRequest,\r\n ): Promise<AssessAgeResponse> {\r\n const url = `${this.baseUrl}/v1/assurance/assess-age`;\r\n\r\n let response: Response;\r\n try {\r\n response = await fetch(url, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n Authorization: `Bearer ${this.apiKey}`,\r\n 'User-Agent': `a3api-node/${SDK_VERSION}`,\r\n },\r\n body: JSON.stringify(request),\r\n signal: AbortSignal.timeout(this.timeout),\r\n });\r\n } catch (error) {\r\n if (error instanceof DOMException && error.name === 'TimeoutError') {\r\n throw new A3ConnectionError(\r\n `Request timed out after ${this.timeout}ms`,\r\n error,\r\n );\r\n }\r\n throw new A3ConnectionError(\r\n error instanceof Error ? error.message : 'Network request failed',\r\n error,\r\n );\r\n }\r\n\r\n if (response.ok) {\r\n return (await response.json()) as AssessAgeResponse;\r\n }\r\n\r\n let body: A3ErrorBody | undefined;\r\n try {\r\n body = (await response.json()) as A3ErrorBody;\r\n } catch {\r\n // body may not be JSON\r\n }\r\n\r\n switch (response.status) {\r\n case 400:\r\n throw new A3ValidationError(body);\r\n case 401:\r\n throw new A3AuthenticationError(body);\r\n case 429: {\r\n const retryAfter = parseRetryAfter(\r\n response.headers.get('retry-after'),\r\n );\r\n throw new A3RateLimitError(retryAfter, body);\r\n }\r\n default:\r\n throw new A3ApiError(\r\n body?.error ?? `HTTP ${response.status}`,\r\n response.status,\r\n body,\r\n );\r\n }\r\n }\r\n}\r\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/retry.ts","../src/version.ts","../src/client.ts"],"sourcesContent":["import type { A3ErrorBody } from './types.js';\r\n\r\nexport class A3ApiError extends Error {\r\n readonly statusCode: number;\r\n readonly body: A3ErrorBody | undefined;\r\n\r\n constructor(message: string, statusCode: number, body?: A3ErrorBody) {\r\n super(message);\r\n this.name = 'A3ApiError';\r\n this.statusCode = statusCode;\r\n this.body = body;\r\n }\r\n}\r\n\r\nexport class A3AuthenticationError extends A3ApiError {\r\n constructor(body?: A3ErrorBody) {\r\n super(\r\n body?.message\r\n ? (Array.isArray(body.message) ? body.message.join(', ') : body.message)\r\n : 'Unauthorized',\r\n 401,\r\n body,\r\n );\r\n this.name = 'A3AuthenticationError';\r\n }\r\n}\r\n\r\nexport class A3RateLimitError extends A3ApiError {\r\n readonly retryAfter: number | undefined;\r\n\r\n constructor(retryAfter?: number, body?: A3ErrorBody) {\r\n super('Rate limit exceeded', 429, body);\r\n this.name = 'A3RateLimitError';\r\n this.retryAfter = retryAfter;\r\n }\r\n}\r\n\r\nexport class A3ValidationError extends A3ApiError {\r\n readonly validationErrors: string[];\r\n\r\n constructor(body?: A3ErrorBody) {\r\n const errors = body?.message\r\n ? (Array.isArray(body.message) ? body.message : [body.message])\r\n : [];\r\n super(\r\n errors.length > 0\r\n ? `Validation failed: ${errors.join(', ')}`\r\n : 'Validation failed',\r\n 400,\r\n body,\r\n );\r\n this.name = 'A3ValidationError';\r\n this.validationErrors = errors;\r\n }\r\n}\r\n\r\nexport class A3ConnectionError extends Error {\r\n readonly cause: unknown;\r\n\r\n constructor(message: string, cause?: unknown) {\r\n super(message);\r\n this.name = 'A3ConnectionError';\r\n this.cause = cause;\r\n }\r\n}\r\n","const RETRYABLE_STATUS_CODES = new Set([429, 500, 502, 503, 504]);\r\n\r\nexport function isRetryableStatus(status: number): boolean {\r\n return RETRYABLE_STATUS_CODES.has(status);\r\n}\r\n\r\nexport function parseRetryAfter(header: string | null): number | undefined {\r\n if (header == null || header === '') return undefined;\r\n const seconds = Number(header);\r\n if (!Number.isNaN(seconds) && seconds >= 0) {\r\n return seconds * 1000;\r\n }\r\n const date = Date.parse(header);\r\n if (!Number.isNaN(date)) {\r\n const ms = date - Date.now();\r\n return ms > 0 ? ms : 0;\r\n }\r\n return undefined;\r\n}\r\n\r\nexport function calculateDelay(\r\n attempt: number,\r\n baseDelayMs: number = 500,\r\n): number {\r\n const exponential = baseDelayMs * 2 ** attempt;\r\n return Math.random() * exponential;\r\n}\r\n\r\nexport async function withRetry<T>(\r\n fn: () => Promise<T>,\r\n opts: {\r\n maxRetries: number;\r\n shouldRetry: (error: unknown) => { retry: boolean; retryAfterMs?: number };\r\n },\r\n): Promise<T> {\r\n let lastError: unknown;\r\n\r\n for (let attempt = 0; attempt <= opts.maxRetries; attempt++) {\r\n try {\r\n return await fn();\r\n } catch (error) {\r\n lastError = error;\r\n\r\n if (attempt >= opts.maxRetries) break;\r\n\r\n const { retry, retryAfterMs } = opts.shouldRetry(error);\r\n if (!retry) break;\r\n\r\n const delay = retryAfterMs ?? calculateDelay(attempt);\r\n await new Promise((resolve) => setTimeout(resolve, delay));\r\n }\r\n }\r\n\r\n throw lastError;\r\n}\r\n","export const SDK_VERSION = '0.1.2';\r\n","import type {\r\n A3ClientOptions,\r\n A3ErrorBody,\r\n AssessAgeRequest,\r\n AssessAgeResponse,\r\n} from './types.js';\r\nimport {\r\n A3ApiError,\r\n A3AuthenticationError,\r\n A3ConnectionError,\r\n A3RateLimitError,\r\n A3ValidationError,\r\n} from './errors.js';\r\nimport { isRetryableStatus, parseRetryAfter, withRetry } from './retry.js';\r\nimport { SDK_VERSION } from './version.js';\r\n\r\nconst DEFAULT_BASE_URL = 'https://api.a3api.io';\r\nconst DEFAULT_TIMEOUT = 30_000;\r\nconst DEFAULT_MAX_RETRIES = 2;\r\n\r\nexport class A3Client {\r\n private readonly apiKey: string;\r\n private readonly baseUrl: string;\r\n private readonly timeout: number;\r\n private readonly maxRetries: number;\r\n\r\n constructor(options: A3ClientOptions) {\r\n if (!options.apiKey) {\r\n throw new Error('apiKey is required');\r\n }\r\n this.apiKey = options.apiKey;\r\n this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, '');\r\n this.timeout = options.timeout ?? DEFAULT_TIMEOUT;\r\n this.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;\r\n }\r\n\r\n async assessAge(request: AssessAgeRequest): Promise<AssessAgeResponse> {\r\n return withRetry(() => this._assessAge(request), {\r\n maxRetries: this.maxRetries,\r\n shouldRetry: (error) => {\r\n if (error instanceof A3RateLimitError) {\r\n return { retry: true, retryAfterMs: error.retryAfter };\r\n }\r\n if (error instanceof A3ApiError && isRetryableStatus(error.statusCode)) {\r\n return { retry: true };\r\n }\r\n if (error instanceof A3ConnectionError) {\r\n return { retry: true };\r\n }\r\n return { retry: false };\r\n },\r\n });\r\n }\r\n\r\n private async _assessAge(\r\n request: AssessAgeRequest,\r\n ): Promise<AssessAgeResponse> {\r\n const url = `${this.baseUrl}/v1/assurance/assess-age`;\r\n\r\n let response: Response;\r\n try {\r\n response = await fetch(url, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n Authorization: `Bearer ${this.apiKey}`,\r\n 'User-Agent': `a3api-node/${SDK_VERSION}`,\r\n },\r\n body: JSON.stringify(request),\r\n signal: AbortSignal.timeout(this.timeout),\r\n });\r\n } catch (error) {\r\n if (error instanceof DOMException && error.name === 'TimeoutError') {\r\n throw new A3ConnectionError(\r\n `Request timed out after ${this.timeout}ms`,\r\n error,\r\n );\r\n }\r\n throw new A3ConnectionError(\r\n error instanceof Error ? error.message : 'Network request failed',\r\n error,\r\n );\r\n }\r\n\r\n if (response.ok) {\r\n return (await response.json()) as AssessAgeResponse;\r\n }\r\n\r\n let body: A3ErrorBody | undefined;\r\n try {\r\n body = (await response.json()) as A3ErrorBody;\r\n } catch {\r\n // body may not be JSON\r\n }\r\n\r\n switch (response.status) {\r\n case 400:\r\n throw new A3ValidationError(body);\r\n case 401:\r\n throw new A3AuthenticationError(body);\r\n case 429: {\r\n const retryAfter = parseRetryAfter(\r\n response.headers.get('retry-after'),\r\n );\r\n throw new A3RateLimitError(retryAfter, body);\r\n }\r\n default:\r\n throw new A3ApiError(\r\n body?.error ?? `HTTP ${response.status}`,\r\n response.status,\r\n body,\r\n );\r\n }\r\n }\r\n}\r\n"],"mappings":"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","names":[]}
|