@a3api/node 0.1.0 → 0.1.1

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 CHANGED
@@ -121,7 +121,7 @@ async function withRetry(fn, opts) {
121
121
  }
122
122
 
123
123
  // src/version.ts
124
- var SDK_VERSION = "0.1.0";
124
+ var SDK_VERSION = "0.1.1";
125
125
 
126
126
  // src/client.ts
127
127
  var DEFAULT_BASE_URL = "https://api.a3api.io";
@@ -166,7 +166,7 @@ var A3Client = class {
166
166
  method: "POST",
167
167
  headers: {
168
168
  "Content-Type": "application/json",
169
- "x-api-key": this.apiKey,
169
+ Authorization: `Bearer ${this.apiKey}`,
170
170
  "User-Agent": `a3api-node/${SDK_VERSION}`
171
171
  },
172
172
  body: JSON.stringify(request),
@@ -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.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":[]}
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":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACEO,IAAM,aAAN,cAAyB,MAAM;AAAA,EAC3B;AAAA,EACA;AAAA,EAET,YAAY,SAAiB,YAAoB,MAAoB;AACnE,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,aAAa;AAClB,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,wBAAN,cAAoC,WAAW;AAAA,EACpD,YAAY,MAAoB;AAC9B;AAAA,MACE,MAAM,UACD,MAAM,QAAQ,KAAK,OAAO,IAAI,KAAK,QAAQ,KAAK,IAAI,IAAI,KAAK,UAC9D;AAAA,MACJ;AAAA,MACA;AAAA,IACF;AACA,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,mBAAN,cAA+B,WAAW;AAAA,EACtC;AAAA,EAET,YAAY,YAAqB,MAAoB;AACnD,UAAM,uBAAuB,KAAK,IAAI;AACtC,SAAK,OAAO;AACZ,SAAK,aAAa;AAAA,EACpB;AACF;AAEO,IAAM,oBAAN,cAAgC,WAAW;AAAA,EACvC;AAAA,EAET,YAAY,MAAoB;AAC9B,UAAM,SAAS,MAAM,UAChB,MAAM,QAAQ,KAAK,OAAO,IAAI,KAAK,UAAU,CAAC,KAAK,OAAO,IAC3D,CAAC;AACL;AAAA,MACE,OAAO,SAAS,IACZ,sBAAsB,OAAO,KAAK,IAAI,CAAC,KACvC;AAAA,MACJ;AAAA,MACA;AAAA,IACF;AACA,SAAK,OAAO;AACZ,SAAK,mBAAmB;AAAA,EAC1B;AACF;AAEO,IAAM,oBAAN,cAAgC,MAAM;AAAA,EAClC;AAAA,EAET,YAAY,SAAiB,OAAiB;AAC5C,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,QAAQ;AAAA,EACf;AACF;;;AChEA,IAAM,yBAAyB,oBAAI,IAAI,CAAC,KAAK,KAAK,KAAK,KAAK,GAAG,CAAC;AAEzD,SAAS,kBAAkB,QAAyB;AACzD,SAAO,uBAAuB,IAAI,MAAM;AAC1C;AAEO,SAAS,gBAAgB,QAA2C;AACzE,MAAI,UAAU,QAAQ,WAAW,GAAI,QAAO;AAC5C,QAAM,UAAU,OAAO,MAAM;AAC7B,MAAI,CAAC,OAAO,MAAM,OAAO,KAAK,WAAW,GAAG;AAC1C,WAAO,UAAU;AAAA,EACnB;AACA,QAAM,OAAO,KAAK,MAAM,MAAM;AAC9B,MAAI,CAAC,OAAO,MAAM,IAAI,GAAG;AACvB,UAAM,KAAK,OAAO,KAAK,IAAI;AAC3B,WAAO,KAAK,IAAI,KAAK;AAAA,EACvB;AACA,SAAO;AACT;AAEO,SAAS,eACd,SACA,cAAsB,KACd;AACR,QAAM,cAAc,cAAc,KAAK;AACvC,SAAO,KAAK,OAAO,IAAI;AACzB;AAEA,eAAsB,UACpB,IACA,MAIY;AACZ,MAAI;AAEJ,WAAS,UAAU,GAAG,WAAW,KAAK,YAAY,WAAW;AAC3D,QAAI;AACF,aAAO,MAAM,GAAG;AAAA,IAClB,SAAS,OAAO;AACd,kBAAY;AAEZ,UAAI,WAAW,KAAK,WAAY;AAEhC,YAAM,EAAE,OAAO,aAAa,IAAI,KAAK,YAAY,KAAK;AACtD,UAAI,CAAC,MAAO;AAEZ,YAAM,QAAQ,gBAAgB,eAAe,OAAO;AACpD,YAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,KAAK,CAAC;AAAA,IAC3D;AAAA,EACF;AAEA,QAAM;AACR;;;ACtDO,IAAM,cAAc;;;ACgB3B,IAAM,mBAAmB;AACzB,IAAM,kBAAkB;AACxB,IAAM,sBAAsB;AAErB,IAAM,WAAN,MAAe;AAAA,EACH;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,SAA0B;AACpC,QAAI,CAAC,QAAQ,QAAQ;AACnB,YAAM,IAAI,MAAM,oBAAoB;AAAA,IACtC;AACA,SAAK,SAAS,QAAQ;AACtB,SAAK,WAAW,QAAQ,WAAW,kBAAkB,QAAQ,QAAQ,EAAE;AACvE,SAAK,UAAU,QAAQ,WAAW;AAClC,SAAK,aAAa,QAAQ,cAAc;AAAA,EAC1C;AAAA,EAEA,MAAM,UAAU,SAAuD;AACrE,WAAO,UAAU,MAAM,KAAK,WAAW,OAAO,GAAG;AAAA,MAC/C,YAAY,KAAK;AAAA,MACjB,aAAa,CAAC,UAAU;AACtB,YAAI,iBAAiB,kBAAkB;AACrC,iBAAO,EAAE,OAAO,MAAM,cAAc,MAAM,WAAW;AAAA,QACvD;AACA,YAAI,iBAAiB,cAAc,kBAAkB,MAAM,UAAU,GAAG;AACtE,iBAAO,EAAE,OAAO,KAAK;AAAA,QACvB;AACA,YAAI,iBAAiB,mBAAmB;AACtC,iBAAO,EAAE,OAAO,KAAK;AAAA,QACvB;AACA,eAAO,EAAE,OAAO,MAAM;AAAA,MACxB;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,WACZ,SAC4B;AAC5B,UAAM,MAAM,GAAG,KAAK,OAAO;AAE3B,QAAI;AACJ,QAAI;AACF,iBAAW,MAAM,MAAM,KAAK;AAAA,QAC1B,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,eAAe,UAAU,KAAK,MAAM;AAAA,UACpC,cAAc,cAAc,WAAW;AAAA,QACzC;AAAA,QACA,MAAM,KAAK,UAAU,OAAO;AAAA,QAC5B,QAAQ,YAAY,QAAQ,KAAK,OAAO;AAAA,MAC1C,CAAC;AAAA,IACH,SAAS,OAAO;AACd,UAAI,iBAAiB,gBAAgB,MAAM,SAAS,gBAAgB;AAClE,cAAM,IAAI;AAAA,UACR,2BAA2B,KAAK,OAAO;AAAA,UACvC;AAAA,QACF;AAAA,MACF;AACA,YAAM,IAAI;AAAA,QACR,iBAAiB,QAAQ,MAAM,UAAU;AAAA,QACzC;AAAA,MACF;AAAA,IACF;AAEA,QAAI,SAAS,IAAI;AACf,aAAQ,MAAM,SAAS,KAAK;AAAA,IAC9B;AAEA,QAAI;AACJ,QAAI;AACF,aAAQ,MAAM,SAAS,KAAK;AAAA,IAC9B,QAAQ;AAAA,IAER;AAEA,YAAQ,SAAS,QAAQ;AAAA,MACvB,KAAK;AACH,cAAM,IAAI,kBAAkB,IAAI;AAAA,MAClC,KAAK;AACH,cAAM,IAAI,sBAAsB,IAAI;AAAA,MACtC,KAAK,KAAK;AACR,cAAM,aAAa;AAAA,UACjB,SAAS,QAAQ,IAAI,aAAa;AAAA,QACpC;AACA,cAAM,IAAI,iBAAiB,YAAY,IAAI;AAAA,MAC7C;AAAA,MACA;AACE,cAAM,IAAI;AAAA,UACR,MAAM,SAAS,QAAQ,SAAS,MAAM;AAAA,UACtC,SAAS;AAAA,UACT;AAAA,QACF;AAAA,IACJ;AAAA,EACF;AACF;","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.0";
109
+ declare const SDK_VERSION = "0.1.1";
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.0";
109
+ declare const SDK_VERSION = "0.1.1";
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
@@ -89,7 +89,7 @@ async function withRetry(fn, opts) {
89
89
  }
90
90
 
91
91
  // src/version.ts
92
- var SDK_VERSION = "0.1.0";
92
+ var SDK_VERSION = "0.1.1";
93
93
 
94
94
  // src/client.ts
95
95
  var DEFAULT_BASE_URL = "https://api.a3api.io";
@@ -134,7 +134,7 @@ var A3Client = class {
134
134
  method: "POST",
135
135
  headers: {
136
136
  "Content-Type": "application/json",
137
- "x-api-key": this.apiKey,
137
+ Authorization: `Bearer ${this.apiKey}`,
138
138
  "User-Agent": `a3api-node/${SDK_VERSION}`
139
139
  },
140
140
  body: JSON.stringify(request),
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.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":[]}
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":";AAEO,IAAM,aAAN,cAAyB,MAAM;AAAA,EAC3B;AAAA,EACA;AAAA,EAET,YAAY,SAAiB,YAAoB,MAAoB;AACnE,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,aAAa;AAClB,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,wBAAN,cAAoC,WAAW;AAAA,EACpD,YAAY,MAAoB;AAC9B;AAAA,MACE,MAAM,UACD,MAAM,QAAQ,KAAK,OAAO,IAAI,KAAK,QAAQ,KAAK,IAAI,IAAI,KAAK,UAC9D;AAAA,MACJ;AAAA,MACA;AAAA,IACF;AACA,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,mBAAN,cAA+B,WAAW;AAAA,EACtC;AAAA,EAET,YAAY,YAAqB,MAAoB;AACnD,UAAM,uBAAuB,KAAK,IAAI;AACtC,SAAK,OAAO;AACZ,SAAK,aAAa;AAAA,EACpB;AACF;AAEO,IAAM,oBAAN,cAAgC,WAAW;AAAA,EACvC;AAAA,EAET,YAAY,MAAoB;AAC9B,UAAM,SAAS,MAAM,UAChB,MAAM,QAAQ,KAAK,OAAO,IAAI,KAAK,UAAU,CAAC,KAAK,OAAO,IAC3D,CAAC;AACL;AAAA,MACE,OAAO,SAAS,IACZ,sBAAsB,OAAO,KAAK,IAAI,CAAC,KACvC;AAAA,MACJ;AAAA,MACA;AAAA,IACF;AACA,SAAK,OAAO;AACZ,SAAK,mBAAmB;AAAA,EAC1B;AACF;AAEO,IAAM,oBAAN,cAAgC,MAAM;AAAA,EAClC;AAAA,EAET,YAAY,SAAiB,OAAiB;AAC5C,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,QAAQ;AAAA,EACf;AACF;;;AChEA,IAAM,yBAAyB,oBAAI,IAAI,CAAC,KAAK,KAAK,KAAK,KAAK,GAAG,CAAC;AAEzD,SAAS,kBAAkB,QAAyB;AACzD,SAAO,uBAAuB,IAAI,MAAM;AAC1C;AAEO,SAAS,gBAAgB,QAA2C;AACzE,MAAI,UAAU,QAAQ,WAAW,GAAI,QAAO;AAC5C,QAAM,UAAU,OAAO,MAAM;AAC7B,MAAI,CAAC,OAAO,MAAM,OAAO,KAAK,WAAW,GAAG;AAC1C,WAAO,UAAU;AAAA,EACnB;AACA,QAAM,OAAO,KAAK,MAAM,MAAM;AAC9B,MAAI,CAAC,OAAO,MAAM,IAAI,GAAG;AACvB,UAAM,KAAK,OAAO,KAAK,IAAI;AAC3B,WAAO,KAAK,IAAI,KAAK;AAAA,EACvB;AACA,SAAO;AACT;AAEO,SAAS,eACd,SACA,cAAsB,KACd;AACR,QAAM,cAAc,cAAc,KAAK;AACvC,SAAO,KAAK,OAAO,IAAI;AACzB;AAEA,eAAsB,UACpB,IACA,MAIY;AACZ,MAAI;AAEJ,WAAS,UAAU,GAAG,WAAW,KAAK,YAAY,WAAW;AAC3D,QAAI;AACF,aAAO,MAAM,GAAG;AAAA,IAClB,SAAS,OAAO;AACd,kBAAY;AAEZ,UAAI,WAAW,KAAK,WAAY;AAEhC,YAAM,EAAE,OAAO,aAAa,IAAI,KAAK,YAAY,KAAK;AACtD,UAAI,CAAC,MAAO;AAEZ,YAAM,QAAQ,gBAAgB,eAAe,OAAO;AACpD,YAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,KAAK,CAAC;AAAA,IAC3D;AAAA,EACF;AAEA,QAAM;AACR;;;ACtDO,IAAM,cAAc;;;ACgB3B,IAAM,mBAAmB;AACzB,IAAM,kBAAkB;AACxB,IAAM,sBAAsB;AAErB,IAAM,WAAN,MAAe;AAAA,EACH;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,SAA0B;AACpC,QAAI,CAAC,QAAQ,QAAQ;AACnB,YAAM,IAAI,MAAM,oBAAoB;AAAA,IACtC;AACA,SAAK,SAAS,QAAQ;AACtB,SAAK,WAAW,QAAQ,WAAW,kBAAkB,QAAQ,QAAQ,EAAE;AACvE,SAAK,UAAU,QAAQ,WAAW;AAClC,SAAK,aAAa,QAAQ,cAAc;AAAA,EAC1C;AAAA,EAEA,MAAM,UAAU,SAAuD;AACrE,WAAO,UAAU,MAAM,KAAK,WAAW,OAAO,GAAG;AAAA,MAC/C,YAAY,KAAK;AAAA,MACjB,aAAa,CAAC,UAAU;AACtB,YAAI,iBAAiB,kBAAkB;AACrC,iBAAO,EAAE,OAAO,MAAM,cAAc,MAAM,WAAW;AAAA,QACvD;AACA,YAAI,iBAAiB,cAAc,kBAAkB,MAAM,UAAU,GAAG;AACtE,iBAAO,EAAE,OAAO,KAAK;AAAA,QACvB;AACA,YAAI,iBAAiB,mBAAmB;AACtC,iBAAO,EAAE,OAAO,KAAK;AAAA,QACvB;AACA,eAAO,EAAE,OAAO,MAAM;AAAA,MACxB;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,WACZ,SAC4B;AAC5B,UAAM,MAAM,GAAG,KAAK,OAAO;AAE3B,QAAI;AACJ,QAAI;AACF,iBAAW,MAAM,MAAM,KAAK;AAAA,QAC1B,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,eAAe,UAAU,KAAK,MAAM;AAAA,UACpC,cAAc,cAAc,WAAW;AAAA,QACzC;AAAA,QACA,MAAM,KAAK,UAAU,OAAO;AAAA,QAC5B,QAAQ,YAAY,QAAQ,KAAK,OAAO;AAAA,MAC1C,CAAC;AAAA,IACH,SAAS,OAAO;AACd,UAAI,iBAAiB,gBAAgB,MAAM,SAAS,gBAAgB;AAClE,cAAM,IAAI;AAAA,UACR,2BAA2B,KAAK,OAAO;AAAA,UACvC;AAAA,QACF;AAAA,MACF;AACA,YAAM,IAAI;AAAA,QACR,iBAAiB,QAAQ,MAAM,UAAU;AAAA,QACzC;AAAA,MACF;AAAA,IACF;AAEA,QAAI,SAAS,IAAI;AACf,aAAQ,MAAM,SAAS,KAAK;AAAA,IAC9B;AAEA,QAAI;AACJ,QAAI;AACF,aAAQ,MAAM,SAAS,KAAK;AAAA,IAC9B,QAAQ;AAAA,IAER;AAEA,YAAQ,SAAS,QAAQ;AAAA,MACvB,KAAK;AACH,cAAM,IAAI,kBAAkB,IAAI;AAAA,MAClC,KAAK;AACH,cAAM,IAAI,sBAAsB,IAAI;AAAA,MACtC,KAAK,KAAK;AACR,cAAM,aAAa;AAAA,UACjB,SAAS,QAAQ,IAAI,aAAa;AAAA,QACpC;AACA,cAAM,IAAI,iBAAiB,YAAY,IAAI;AAAA,MAC7C;AAAA,MACA;AACE,cAAM,IAAI;AAAA,UACR,MAAM,SAAS,QAAQ,SAAS,MAAM;AAAA,UACtC,SAAS;AAAA,UACT;AAAA,QACF;AAAA,IACJ;AAAA,EACF;AACF;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@a3api/node",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Official Node.js client for the A3 Age Assurance API",
5
5
  "private": false,
6
6
  "type": "module",