@a3api/node 0.1.0

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/dist/index.cjs ADDED
@@ -0,0 +1,225 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ A3ApiError: () => A3ApiError,
24
+ A3AuthenticationError: () => A3AuthenticationError,
25
+ A3Client: () => A3Client,
26
+ A3ConnectionError: () => A3ConnectionError,
27
+ A3RateLimitError: () => A3RateLimitError,
28
+ A3ValidationError: () => A3ValidationError,
29
+ SDK_VERSION: () => SDK_VERSION
30
+ });
31
+ module.exports = __toCommonJS(index_exports);
32
+
33
+ // src/errors.ts
34
+ var A3ApiError = class extends Error {
35
+ statusCode;
36
+ body;
37
+ constructor(message, statusCode, body) {
38
+ super(message);
39
+ this.name = "A3ApiError";
40
+ this.statusCode = statusCode;
41
+ this.body = body;
42
+ }
43
+ };
44
+ var A3AuthenticationError = class extends A3ApiError {
45
+ constructor(body) {
46
+ super(
47
+ body?.message ? Array.isArray(body.message) ? body.message.join(", ") : body.message : "Unauthorized",
48
+ 401,
49
+ body
50
+ );
51
+ this.name = "A3AuthenticationError";
52
+ }
53
+ };
54
+ var A3RateLimitError = class extends A3ApiError {
55
+ retryAfter;
56
+ constructor(retryAfter, body) {
57
+ super("Rate limit exceeded", 429, body);
58
+ this.name = "A3RateLimitError";
59
+ this.retryAfter = retryAfter;
60
+ }
61
+ };
62
+ var A3ValidationError = class extends A3ApiError {
63
+ validationErrors;
64
+ constructor(body) {
65
+ const errors = body?.message ? Array.isArray(body.message) ? body.message : [body.message] : [];
66
+ super(
67
+ errors.length > 0 ? `Validation failed: ${errors.join(", ")}` : "Validation failed",
68
+ 400,
69
+ body
70
+ );
71
+ this.name = "A3ValidationError";
72
+ this.validationErrors = errors;
73
+ }
74
+ };
75
+ var A3ConnectionError = class extends Error {
76
+ cause;
77
+ constructor(message, cause) {
78
+ super(message);
79
+ this.name = "A3ConnectionError";
80
+ this.cause = cause;
81
+ }
82
+ };
83
+
84
+ // src/retry.ts
85
+ var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
86
+ function isRetryableStatus(status) {
87
+ return RETRYABLE_STATUS_CODES.has(status);
88
+ }
89
+ function parseRetryAfter(header) {
90
+ if (header == null || header === "") return void 0;
91
+ const seconds = Number(header);
92
+ if (!Number.isNaN(seconds) && seconds >= 0) {
93
+ return seconds * 1e3;
94
+ }
95
+ const date = Date.parse(header);
96
+ if (!Number.isNaN(date)) {
97
+ const ms = date - Date.now();
98
+ return ms > 0 ? ms : 0;
99
+ }
100
+ return void 0;
101
+ }
102
+ function calculateDelay(attempt, baseDelayMs = 500) {
103
+ const exponential = baseDelayMs * 2 ** attempt;
104
+ return Math.random() * exponential;
105
+ }
106
+ async function withRetry(fn, opts) {
107
+ let lastError;
108
+ for (let attempt = 0; attempt <= opts.maxRetries; attempt++) {
109
+ try {
110
+ return await fn();
111
+ } catch (error) {
112
+ lastError = error;
113
+ if (attempt >= opts.maxRetries) break;
114
+ const { retry, retryAfterMs } = opts.shouldRetry(error);
115
+ if (!retry) break;
116
+ const delay = retryAfterMs ?? calculateDelay(attempt);
117
+ await new Promise((resolve) => setTimeout(resolve, delay));
118
+ }
119
+ }
120
+ throw lastError;
121
+ }
122
+
123
+ // src/version.ts
124
+ var SDK_VERSION = "0.1.0";
125
+
126
+ // src/client.ts
127
+ var DEFAULT_BASE_URL = "https://api.a3api.io";
128
+ var DEFAULT_TIMEOUT = 3e4;
129
+ var DEFAULT_MAX_RETRIES = 2;
130
+ var A3Client = class {
131
+ apiKey;
132
+ baseUrl;
133
+ timeout;
134
+ maxRetries;
135
+ constructor(options) {
136
+ if (!options.apiKey) {
137
+ throw new Error("apiKey is required");
138
+ }
139
+ this.apiKey = options.apiKey;
140
+ this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
141
+ this.timeout = options.timeout ?? DEFAULT_TIMEOUT;
142
+ this.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
143
+ }
144
+ async assessAge(request) {
145
+ return withRetry(() => this._assessAge(request), {
146
+ maxRetries: this.maxRetries,
147
+ shouldRetry: (error) => {
148
+ if (error instanceof A3RateLimitError) {
149
+ return { retry: true, retryAfterMs: error.retryAfter };
150
+ }
151
+ if (error instanceof A3ApiError && isRetryableStatus(error.statusCode)) {
152
+ return { retry: true };
153
+ }
154
+ if (error instanceof A3ConnectionError) {
155
+ return { retry: true };
156
+ }
157
+ return { retry: false };
158
+ }
159
+ });
160
+ }
161
+ async _assessAge(request) {
162
+ const url = `${this.baseUrl}/v1/assurance/assess-age`;
163
+ let response;
164
+ try {
165
+ response = await fetch(url, {
166
+ method: "POST",
167
+ headers: {
168
+ "Content-Type": "application/json",
169
+ "x-api-key": this.apiKey,
170
+ "User-Agent": `a3api-node/${SDK_VERSION}`
171
+ },
172
+ body: JSON.stringify(request),
173
+ signal: AbortSignal.timeout(this.timeout)
174
+ });
175
+ } catch (error) {
176
+ if (error instanceof DOMException && error.name === "TimeoutError") {
177
+ throw new A3ConnectionError(
178
+ `Request timed out after ${this.timeout}ms`,
179
+ error
180
+ );
181
+ }
182
+ throw new A3ConnectionError(
183
+ error instanceof Error ? error.message : "Network request failed",
184
+ error
185
+ );
186
+ }
187
+ if (response.ok) {
188
+ return await response.json();
189
+ }
190
+ let body;
191
+ try {
192
+ body = await response.json();
193
+ } catch {
194
+ }
195
+ switch (response.status) {
196
+ case 400:
197
+ throw new A3ValidationError(body);
198
+ case 401:
199
+ throw new A3AuthenticationError(body);
200
+ case 429: {
201
+ const retryAfter = parseRetryAfter(
202
+ response.headers.get("retry-after")
203
+ );
204
+ throw new A3RateLimitError(retryAfter, body);
205
+ }
206
+ default:
207
+ throw new A3ApiError(
208
+ body?.error ?? `HTTP ${response.status}`,
209
+ response.status,
210
+ body
211
+ );
212
+ }
213
+ }
214
+ };
215
+ // Annotate the CommonJS export names for ESM import in node:
216
+ 0 && (module.exports = {
217
+ A3ApiError,
218
+ A3AuthenticationError,
219
+ A3Client,
220
+ A3ConnectionError,
221
+ A3RateLimitError,
222
+ A3ValidationError,
223
+ SDK_VERSION
224
+ });
225
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +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.0';\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 'x-api-key': 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":[]}
@@ -0,0 +1,111 @@
1
+ type OsSignal = 'under-13' | '13-15' | '16-17' | '18-plus' | 'not-available';
2
+ type ParentalConsentStatus = 'pending' | 'approved' | 'denied' | 'revoked';
3
+ type ConsentSource = 'os-system' | 'third-party-wallet' | 'in-app';
4
+ type FaceEstimationProvider = 'yoti' | 'privado' | 'facetec';
5
+ type IpType = 'residential' | 'education' | 'datacenter' | 'corporate';
6
+ type ReferrerCategory = 'direct' | 'social_minor' | 'social_general' | 'parental_control' | 'search_engine' | 'unknown';
7
+ interface FaceEstimationResult {
8
+ estimation_provider: FaceEstimationProvider;
9
+ estimated_age_lower: number;
10
+ estimated_age_upper: number;
11
+ confidence: number;
12
+ }
13
+ interface BehavioralMetrics {
14
+ avg_touch_precision: number;
15
+ scroll_velocity: number;
16
+ form_completion_time_ms: number;
17
+ is_autofill_detected?: boolean;
18
+ touch_pressure_variance?: number;
19
+ multi_touch_frequency?: number;
20
+ face_estimation_result?: FaceEstimationResult;
21
+ }
22
+ interface DeviceContext {
23
+ os_version: string;
24
+ device_model?: string;
25
+ is_high_contrast_enabled: boolean;
26
+ screen_scale_factor: number;
27
+ }
28
+ interface ContextualSignals {
29
+ ip_type?: IpType;
30
+ timezone_offset_delta_minutes?: number;
31
+ referrer_category?: ReferrerCategory;
32
+ }
33
+ interface AccountLongevity {
34
+ account_age_days: number;
35
+ }
36
+ interface InputComplexity {
37
+ keyboard_autocorrect_rate: number;
38
+ average_word_complexity_score: number;
39
+ }
40
+ interface AssessAgeRequest {
41
+ os_signal: OsSignal;
42
+ user_country_code: string;
43
+ behavioral_metrics?: BehavioralMetrics;
44
+ device_context?: DeviceContext;
45
+ contextual_signals?: ContextualSignals;
46
+ account_longevity?: AccountLongevity;
47
+ input_complexity?: InputComplexity;
48
+ parental_consent_status?: ParentalConsentStatus;
49
+ consent_source?: ConsentSource;
50
+ }
51
+ type Verdict = 'CONSISTENT' | 'OVERRIDE' | 'REVIEW' | 'PROVISIONAL';
52
+ type AgeBracket = 'under-13' | '13-15' | '16-17' | '18-plus' | 'undetermined';
53
+ interface AssessAgeResponse {
54
+ confidence_score: number;
55
+ verdict: Verdict;
56
+ os_signal_age_bracket: AgeBracket;
57
+ assessed_age_bracket: AgeBracket;
58
+ signal_overridden: boolean;
59
+ internal_evidence_only: boolean;
60
+ evidence_tags: string[];
61
+ user_country_code: string;
62
+ verification_token: string;
63
+ parental_consent_status?: ParentalConsentStatus;
64
+ consent_source?: ConsentSource;
65
+ }
66
+ interface A3ClientOptions {
67
+ apiKey: string;
68
+ baseUrl?: string;
69
+ timeout?: number;
70
+ maxRetries?: number;
71
+ }
72
+ interface A3ErrorBody {
73
+ statusCode: number;
74
+ message: string | string[];
75
+ error: string;
76
+ }
77
+
78
+ declare class A3Client {
79
+ private readonly apiKey;
80
+ private readonly baseUrl;
81
+ private readonly timeout;
82
+ private readonly maxRetries;
83
+ constructor(options: A3ClientOptions);
84
+ assessAge(request: AssessAgeRequest): Promise<AssessAgeResponse>;
85
+ private _assessAge;
86
+ }
87
+
88
+ declare class A3ApiError extends Error {
89
+ readonly statusCode: number;
90
+ readonly body: A3ErrorBody | undefined;
91
+ constructor(message: string, statusCode: number, body?: A3ErrorBody);
92
+ }
93
+ declare class A3AuthenticationError extends A3ApiError {
94
+ constructor(body?: A3ErrorBody);
95
+ }
96
+ declare class A3RateLimitError extends A3ApiError {
97
+ readonly retryAfter: number | undefined;
98
+ constructor(retryAfter?: number, body?: A3ErrorBody);
99
+ }
100
+ declare class A3ValidationError extends A3ApiError {
101
+ readonly validationErrors: string[];
102
+ constructor(body?: A3ErrorBody);
103
+ }
104
+ declare class A3ConnectionError extends Error {
105
+ readonly cause: unknown;
106
+ constructor(message: string, cause?: unknown);
107
+ }
108
+
109
+ declare const SDK_VERSION = "0.1.0";
110
+
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 };
@@ -0,0 +1,111 @@
1
+ type OsSignal = 'under-13' | '13-15' | '16-17' | '18-plus' | 'not-available';
2
+ type ParentalConsentStatus = 'pending' | 'approved' | 'denied' | 'revoked';
3
+ type ConsentSource = 'os-system' | 'third-party-wallet' | 'in-app';
4
+ type FaceEstimationProvider = 'yoti' | 'privado' | 'facetec';
5
+ type IpType = 'residential' | 'education' | 'datacenter' | 'corporate';
6
+ type ReferrerCategory = 'direct' | 'social_minor' | 'social_general' | 'parental_control' | 'search_engine' | 'unknown';
7
+ interface FaceEstimationResult {
8
+ estimation_provider: FaceEstimationProvider;
9
+ estimated_age_lower: number;
10
+ estimated_age_upper: number;
11
+ confidence: number;
12
+ }
13
+ interface BehavioralMetrics {
14
+ avg_touch_precision: number;
15
+ scroll_velocity: number;
16
+ form_completion_time_ms: number;
17
+ is_autofill_detected?: boolean;
18
+ touch_pressure_variance?: number;
19
+ multi_touch_frequency?: number;
20
+ face_estimation_result?: FaceEstimationResult;
21
+ }
22
+ interface DeviceContext {
23
+ os_version: string;
24
+ device_model?: string;
25
+ is_high_contrast_enabled: boolean;
26
+ screen_scale_factor: number;
27
+ }
28
+ interface ContextualSignals {
29
+ ip_type?: IpType;
30
+ timezone_offset_delta_minutes?: number;
31
+ referrer_category?: ReferrerCategory;
32
+ }
33
+ interface AccountLongevity {
34
+ account_age_days: number;
35
+ }
36
+ interface InputComplexity {
37
+ keyboard_autocorrect_rate: number;
38
+ average_word_complexity_score: number;
39
+ }
40
+ interface AssessAgeRequest {
41
+ os_signal: OsSignal;
42
+ user_country_code: string;
43
+ behavioral_metrics?: BehavioralMetrics;
44
+ device_context?: DeviceContext;
45
+ contextual_signals?: ContextualSignals;
46
+ account_longevity?: AccountLongevity;
47
+ input_complexity?: InputComplexity;
48
+ parental_consent_status?: ParentalConsentStatus;
49
+ consent_source?: ConsentSource;
50
+ }
51
+ type Verdict = 'CONSISTENT' | 'OVERRIDE' | 'REVIEW' | 'PROVISIONAL';
52
+ type AgeBracket = 'under-13' | '13-15' | '16-17' | '18-plus' | 'undetermined';
53
+ interface AssessAgeResponse {
54
+ confidence_score: number;
55
+ verdict: Verdict;
56
+ os_signal_age_bracket: AgeBracket;
57
+ assessed_age_bracket: AgeBracket;
58
+ signal_overridden: boolean;
59
+ internal_evidence_only: boolean;
60
+ evidence_tags: string[];
61
+ user_country_code: string;
62
+ verification_token: string;
63
+ parental_consent_status?: ParentalConsentStatus;
64
+ consent_source?: ConsentSource;
65
+ }
66
+ interface A3ClientOptions {
67
+ apiKey: string;
68
+ baseUrl?: string;
69
+ timeout?: number;
70
+ maxRetries?: number;
71
+ }
72
+ interface A3ErrorBody {
73
+ statusCode: number;
74
+ message: string | string[];
75
+ error: string;
76
+ }
77
+
78
+ declare class A3Client {
79
+ private readonly apiKey;
80
+ private readonly baseUrl;
81
+ private readonly timeout;
82
+ private readonly maxRetries;
83
+ constructor(options: A3ClientOptions);
84
+ assessAge(request: AssessAgeRequest): Promise<AssessAgeResponse>;
85
+ private _assessAge;
86
+ }
87
+
88
+ declare class A3ApiError extends Error {
89
+ readonly statusCode: number;
90
+ readonly body: A3ErrorBody | undefined;
91
+ constructor(message: string, statusCode: number, body?: A3ErrorBody);
92
+ }
93
+ declare class A3AuthenticationError extends A3ApiError {
94
+ constructor(body?: A3ErrorBody);
95
+ }
96
+ declare class A3RateLimitError extends A3ApiError {
97
+ readonly retryAfter: number | undefined;
98
+ constructor(retryAfter?: number, body?: A3ErrorBody);
99
+ }
100
+ declare class A3ValidationError extends A3ApiError {
101
+ readonly validationErrors: string[];
102
+ constructor(body?: A3ErrorBody);
103
+ }
104
+ declare class A3ConnectionError extends Error {
105
+ readonly cause: unknown;
106
+ constructor(message: string, cause?: unknown);
107
+ }
108
+
109
+ declare const SDK_VERSION = "0.1.0";
110
+
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 ADDED
@@ -0,0 +1,192 @@
1
+ // src/errors.ts
2
+ var A3ApiError = class extends Error {
3
+ statusCode;
4
+ body;
5
+ constructor(message, statusCode, body) {
6
+ super(message);
7
+ this.name = "A3ApiError";
8
+ this.statusCode = statusCode;
9
+ this.body = body;
10
+ }
11
+ };
12
+ var A3AuthenticationError = class extends A3ApiError {
13
+ constructor(body) {
14
+ super(
15
+ body?.message ? Array.isArray(body.message) ? body.message.join(", ") : body.message : "Unauthorized",
16
+ 401,
17
+ body
18
+ );
19
+ this.name = "A3AuthenticationError";
20
+ }
21
+ };
22
+ var A3RateLimitError = class extends A3ApiError {
23
+ retryAfter;
24
+ constructor(retryAfter, body) {
25
+ super("Rate limit exceeded", 429, body);
26
+ this.name = "A3RateLimitError";
27
+ this.retryAfter = retryAfter;
28
+ }
29
+ };
30
+ var A3ValidationError = class extends A3ApiError {
31
+ validationErrors;
32
+ constructor(body) {
33
+ const errors = body?.message ? Array.isArray(body.message) ? body.message : [body.message] : [];
34
+ super(
35
+ errors.length > 0 ? `Validation failed: ${errors.join(", ")}` : "Validation failed",
36
+ 400,
37
+ body
38
+ );
39
+ this.name = "A3ValidationError";
40
+ this.validationErrors = errors;
41
+ }
42
+ };
43
+ var A3ConnectionError = class extends Error {
44
+ cause;
45
+ constructor(message, cause) {
46
+ super(message);
47
+ this.name = "A3ConnectionError";
48
+ this.cause = cause;
49
+ }
50
+ };
51
+
52
+ // src/retry.ts
53
+ var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
54
+ function isRetryableStatus(status) {
55
+ return RETRYABLE_STATUS_CODES.has(status);
56
+ }
57
+ function parseRetryAfter(header) {
58
+ if (header == null || header === "") return void 0;
59
+ const seconds = Number(header);
60
+ if (!Number.isNaN(seconds) && seconds >= 0) {
61
+ return seconds * 1e3;
62
+ }
63
+ const date = Date.parse(header);
64
+ if (!Number.isNaN(date)) {
65
+ const ms = date - Date.now();
66
+ return ms > 0 ? ms : 0;
67
+ }
68
+ return void 0;
69
+ }
70
+ function calculateDelay(attempt, baseDelayMs = 500) {
71
+ const exponential = baseDelayMs * 2 ** attempt;
72
+ return Math.random() * exponential;
73
+ }
74
+ async function withRetry(fn, opts) {
75
+ let lastError;
76
+ for (let attempt = 0; attempt <= opts.maxRetries; attempt++) {
77
+ try {
78
+ return await fn();
79
+ } catch (error) {
80
+ lastError = error;
81
+ if (attempt >= opts.maxRetries) break;
82
+ const { retry, retryAfterMs } = opts.shouldRetry(error);
83
+ if (!retry) break;
84
+ const delay = retryAfterMs ?? calculateDelay(attempt);
85
+ await new Promise((resolve) => setTimeout(resolve, delay));
86
+ }
87
+ }
88
+ throw lastError;
89
+ }
90
+
91
+ // src/version.ts
92
+ var SDK_VERSION = "0.1.0";
93
+
94
+ // src/client.ts
95
+ var DEFAULT_BASE_URL = "https://api.a3api.io";
96
+ var DEFAULT_TIMEOUT = 3e4;
97
+ var DEFAULT_MAX_RETRIES = 2;
98
+ var A3Client = class {
99
+ apiKey;
100
+ baseUrl;
101
+ timeout;
102
+ maxRetries;
103
+ constructor(options) {
104
+ if (!options.apiKey) {
105
+ throw new Error("apiKey is required");
106
+ }
107
+ this.apiKey = options.apiKey;
108
+ this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
109
+ this.timeout = options.timeout ?? DEFAULT_TIMEOUT;
110
+ this.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
111
+ }
112
+ async assessAge(request) {
113
+ return withRetry(() => this._assessAge(request), {
114
+ maxRetries: this.maxRetries,
115
+ shouldRetry: (error) => {
116
+ if (error instanceof A3RateLimitError) {
117
+ return { retry: true, retryAfterMs: error.retryAfter };
118
+ }
119
+ if (error instanceof A3ApiError && isRetryableStatus(error.statusCode)) {
120
+ return { retry: true };
121
+ }
122
+ if (error instanceof A3ConnectionError) {
123
+ return { retry: true };
124
+ }
125
+ return { retry: false };
126
+ }
127
+ });
128
+ }
129
+ async _assessAge(request) {
130
+ const url = `${this.baseUrl}/v1/assurance/assess-age`;
131
+ let response;
132
+ try {
133
+ response = await fetch(url, {
134
+ method: "POST",
135
+ headers: {
136
+ "Content-Type": "application/json",
137
+ "x-api-key": this.apiKey,
138
+ "User-Agent": `a3api-node/${SDK_VERSION}`
139
+ },
140
+ body: JSON.stringify(request),
141
+ signal: AbortSignal.timeout(this.timeout)
142
+ });
143
+ } catch (error) {
144
+ if (error instanceof DOMException && error.name === "TimeoutError") {
145
+ throw new A3ConnectionError(
146
+ `Request timed out after ${this.timeout}ms`,
147
+ error
148
+ );
149
+ }
150
+ throw new A3ConnectionError(
151
+ error instanceof Error ? error.message : "Network request failed",
152
+ error
153
+ );
154
+ }
155
+ if (response.ok) {
156
+ return await response.json();
157
+ }
158
+ let body;
159
+ try {
160
+ body = await response.json();
161
+ } catch {
162
+ }
163
+ switch (response.status) {
164
+ case 400:
165
+ throw new A3ValidationError(body);
166
+ case 401:
167
+ throw new A3AuthenticationError(body);
168
+ case 429: {
169
+ const retryAfter = parseRetryAfter(
170
+ response.headers.get("retry-after")
171
+ );
172
+ throw new A3RateLimitError(retryAfter, body);
173
+ }
174
+ default:
175
+ throw new A3ApiError(
176
+ body?.error ?? `HTTP ${response.status}`,
177
+ response.status,
178
+ body
179
+ );
180
+ }
181
+ }
182
+ };
183
+ export {
184
+ A3ApiError,
185
+ A3AuthenticationError,
186
+ A3Client,
187
+ A3ConnectionError,
188
+ A3RateLimitError,
189
+ A3ValidationError,
190
+ SDK_VERSION
191
+ };
192
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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.0';\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 'x-api-key': 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/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@a3api/node",
3
+ "version": "0.1.0",
4
+ "description": "Official Node.js client for the A3 Age Assurance API",
5
+ "private": false,
6
+ "type": "module",
7
+ "main": "./dist/index.cjs",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "require": "./dist/index.cjs"
15
+ }
16
+ },
17
+ "sideEffects": false,
18
+ "files": [
19
+ "dist",
20
+ "README.md"
21
+ ],
22
+ "engines": {
23
+ "node": ">=18"
24
+ },
25
+ "scripts": {
26
+ "build": "tsup",
27
+ "test": "vitest run",
28
+ "test:watch": "vitest"
29
+ },
30
+ "devDependencies": {
31
+ "tsup": "^8.0.0",
32
+ "typescript": "^5.7.2",
33
+ "vitest": "^2.0.0"
34
+ },
35
+ "keywords": [
36
+ "a3",
37
+ "age-assurance",
38
+ "age-verification",
39
+ "ab-1043",
40
+ "coppa"
41
+ ],
42
+ "license": "MIT"
43
+ }