@authenta/core 1.0.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/README.md ADDED
@@ -0,0 +1,324 @@
1
+ # @authenta/core
2
+
3
+ Pure TypeScript API client for the [Authenta](https://authenta.ai) eKYC platform. Works in **Node.js** and **React Native** — no native modules or UI dependencies.
4
+
5
+ Use this package directly if you want headless control over uploads, polling, and result retrieval. For a ready-made camera capture UI, use [`@authenta/react-native`](https://www.npmjs.com/package/@authenta/react-native).
6
+
7
+ ---
8
+
9
+ ## Table of Contents
10
+
11
+ - [Installation](#installation)
12
+ - [Quick Start](#quick-start)
13
+ - [AuthentaClient](#authentaclient)
14
+ - [Configuration](#configuration)
15
+ - [faceIntelligence()](#faceintelligence)
16
+ - [RunOptions](#runoptions)
17
+ - [Low-level API](#low-level-api)
18
+ - [Models](#models)
19
+ - [Error Handling](#error-handling)
20
+ - [TypeScript Types](#typescript-types)
21
+ - [Contributing](#contributing)
22
+
23
+ ---
24
+
25
+ ## Installation
26
+
27
+ ```bash
28
+ npm install @authenta/core
29
+ ```
30
+
31
+ No peer dependencies. Works out of the box in Node.js >= 16 and React Native >= 0.72.
32
+
33
+ ---
34
+
35
+ ## Quick Start
36
+
37
+ ```ts
38
+ import { AuthentaClient } from '@authenta/core';
39
+
40
+ const client = new AuthentaClient({
41
+ clientId: 'YOUR_CLIENT_ID',
42
+ clientSecret: 'YOUR_CLIENT_SECRET',
43
+ });
44
+
45
+ const result = await client.faceIntelligence('file:///path/to/selfie.jpg', 'FI-1', {
46
+ livenessCheck: true,
47
+ });
48
+
49
+ console.log(result.result?.isLiveness); // true | false
50
+ console.log(result.status); // "PROCESSED"
51
+ ```
52
+
53
+ ---
54
+
55
+ ## AuthentaClient
56
+
57
+ ### Configuration
58
+
59
+ ```ts
60
+ const client = new AuthentaClient({
61
+ clientId: 'YOUR_CLIENT_ID', // required
62
+ clientSecret: 'YOUR_CLIENT_SECRET', // required
63
+ baseUrl: 'https://platform.authenta.ai', // optional — default shown
64
+ });
65
+ ```
66
+
67
+ | Option | Type | Required | Description |
68
+ |---|---|---|---|
69
+ | `clientId` | `string` | Yes | Your Authenta client ID |
70
+ | `clientSecret` | `string` | Yes | Your Authenta client secret |
71
+ | `baseUrl` | `string` | No | API base URL (default: `https://platform.authenta.ai`) |
72
+
73
+ ---
74
+
75
+ ### faceIntelligence()
76
+
77
+ The primary high-level method. Uploads the file, polls until processing is complete, fetches the detection result, and returns a `ProcessedMedia` object.
78
+
79
+ ```ts
80
+ const result = await client.faceIntelligence(uri, modelType, options);
81
+ ```
82
+
83
+ **Parameters**
84
+
85
+ | Parameter | Type | Description |
86
+ |---|---|---|
87
+ | `uri` | `string` | `file://` URI of the photo or video to analyse |
88
+ | `modelType` | `ModelType` | Model to run (e.g. `'FI-1'`) |
89
+ | `options` | `RunOptions` | Detection options — see [RunOptions](#runoptions) |
90
+
91
+ **Examples**
92
+
93
+ ```ts
94
+ // Liveness check — photo
95
+ const result = await client.faceIntelligence('file:///path/to/selfie.jpg', 'FI-1', {
96
+ livenessCheck: true,
97
+ });
98
+
99
+ // Faceswap check — video
100
+ const result = await client.faceIntelligence('file:///path/to/clip.mp4', 'FI-1', {
101
+ faceswapCheck: true,
102
+ });
103
+
104
+ // Liveness + faceswap — video
105
+ const result = await client.faceIntelligence('file:///path/to/clip.mp4', 'FI-1', {
106
+ livenessCheck: true,
107
+ faceswapCheck: true,
108
+ });
109
+
110
+ // Face similarity — photo + reference
111
+ const result = await client.faceIntelligence('file:///path/to/selfie.jpg', 'FI-1', {
112
+ faceSimilarityCheck: true,
113
+ referenceImage: 'file:///path/to/id-photo.jpg',
114
+ });
115
+
116
+ // Liveness + face similarity — photo
117
+ const result = await client.faceIntelligence('file:///path/to/selfie.jpg', 'FI-1', {
118
+ livenessCheck: true,
119
+ faceSimilarityCheck: true,
120
+ referenceImage: 'file:///path/to/id-photo.jpg',
121
+ });
122
+ ```
123
+
124
+ **Returns** `Promise<ProcessedMedia>`
125
+
126
+ ```ts
127
+ {
128
+ mid: string; // unique media ID
129
+ name: string;
130
+ status: 'PROCESSED'; // always PROCESSED on success
131
+ modelType: string; // e.g. "FI-1"
132
+ contentType: string; // MIME type of the uploaded file
133
+ size: number; // file size in bytes
134
+ createdAt: string; // ISO 8601
135
+ srcURL?: string;
136
+ resultURL?: string;
137
+ result?: {
138
+ resultType: string;
139
+ isLiveness?: boolean | string; // liveness check result
140
+ isDeepFake?: boolean | string; // faceswap / deepfake result
141
+ isSimilar?: boolean | string; // face similarity result
142
+ similarityScore?: number | string; // 0–100
143
+ [key: string]: any;
144
+ };
145
+ }
146
+ ```
147
+
148
+ ---
149
+
150
+ ### RunOptions
151
+
152
+ | Option | Type | Default | Description |
153
+ |---|---|---|---|
154
+ | `livenessCheck` | `boolean` | `false` | Run liveness check (FI-1) |
155
+ | `faceswapCheck` | `boolean` | `false` | Run faceswap / deepfake check (FI-1, video required) |
156
+ | `faceSimilarityCheck` | `boolean` | `false` | Run face similarity check (FI-1, photo + reference required) |
157
+ | `referenceImage` | `string` | — | `file://` URI of reference face image (required when `faceSimilarityCheck: true`) |
158
+ | `isSingleFace` | `boolean` | `true` | Hint that only one face is present |
159
+ | `autoPolling` | `boolean` | `true` | Wait for result before returning. Set `false` to return immediately after upload |
160
+ | `interval` | `number` | `5000` | Polling interval in milliseconds |
161
+ | `timeout` | `number` | `600000` | Max polling duration in milliseconds (10 min) |
162
+
163
+ **Check compatibility**
164
+
165
+ | Check | Input required | Can combine with |
166
+ |---|---|---|
167
+ | `livenessCheck` | Photo **or** video | `faceSimilarityCheck` |
168
+ | `faceswapCheck` | Video (max 10 s) | `livenessCheck` |
169
+ | `faceSimilarityCheck` | Photo + `referenceImage` | `livenessCheck` |
170
+ | `faceswapCheck` + `faceSimilarityCheck` | — | **Not allowed** |
171
+
172
+ ---
173
+
174
+ ### Low-level API
175
+
176
+ Call each step individually for full control:
177
+
178
+ ```ts
179
+ // 1. Upload — creates a media record and uploads the file to S3
180
+ const media = await client.upload('file:///path/to/selfie.jpg', 'FI-1', {
181
+ livenessCheck: true,
182
+ });
183
+ console.log(media.mid); // "abc-123"
184
+
185
+ // 2. Poll until processing completes
186
+ const processed = await client.waitForMedia(media.mid, {
187
+ interval: 3000, // poll every 3 s
188
+ timeout: 120000, // give up after 2 min
189
+ });
190
+
191
+ // 3. Fetch the detection result from resultURL
192
+ const result = await client.getResult(processed);
193
+ console.log(result.isLiveness, result.isDeepFake);
194
+
195
+ // CRUD helpers
196
+ const record = await client.getMedia(mid);
197
+ const list = await client.listMedia({ page: 1, pageSize: 20 });
198
+ await client.deleteMedia(mid);
199
+ ```
200
+
201
+ ---
202
+
203
+ ## Models
204
+
205
+ | Model ID | Description | Input |
206
+ |---|---|---|
207
+ | `FI-1` | Face Intelligence — liveness, faceswap, face similarity | Photo or video |
208
+
209
+ ---
210
+
211
+ ## Error Handling
212
+
213
+ All errors extend `AuthentaError`. Import and catch specifically:
214
+
215
+ ```ts
216
+ import {
217
+ AuthentaError,
218
+ AuthenticationError,
219
+ AuthorizationError,
220
+ QuotaExceededError,
221
+ InsufficientCreditsError,
222
+ ValidationError,
223
+ ServerError,
224
+ } from '@authenta/core';
225
+
226
+ try {
227
+ const result = await client.faceIntelligence(uri, 'FI-1', { livenessCheck: true });
228
+ } catch (err) {
229
+ if (err instanceof AuthenticationError) {
230
+ // Invalid clientId / clientSecret — check your credentials
231
+ } else if (err instanceof AuthorizationError) {
232
+ // Account lacks permission for this operation
233
+ } else if (err instanceof QuotaExceededError) {
234
+ // Monthly quota exceeded
235
+ } else if (err instanceof InsufficientCreditsError) {
236
+ // No remaining credits
237
+ } else if (err instanceof ValidationError) {
238
+ // Bad input — see err.message for details
239
+ console.error(err.message, err.code, err.statusCode);
240
+ } else if (err instanceof ServerError) {
241
+ // Platform error — safe to retry
242
+ } else if (err instanceof AuthentaError) {
243
+ // Base class catch-all
244
+ console.error(err.message, err.code, err.statusCode, err.details);
245
+ }
246
+ }
247
+ ```
248
+
249
+ **Error properties**
250
+
251
+ | Property | Type | Description |
252
+ |---|---|---|
253
+ | `message` | `string` | Human-readable description |
254
+ | `code` | `string?` | API error code (e.g. `IAM001`) |
255
+ | `statusCode` | `number?` | HTTP status code |
256
+ | `details` | `object?` | Raw API response body |
257
+
258
+ ---
259
+
260
+ ## TypeScript Types
261
+
262
+ All public types are exported from the package entry point:
263
+
264
+ ```ts
265
+ import type {
266
+ AuthentaClientConfig,
267
+ ModelType,
268
+ MediaStatus,
269
+ FileInfo,
270
+ FIOptions,
271
+ RunOptions,
272
+ PollingOptions,
273
+ CreateMediaResponse,
274
+ MediaRecord,
275
+ ListMediaResponse,
276
+ DetectionResult,
277
+ ProcessedMedia,
278
+ } from '@authenta/core';
279
+ ```
280
+
281
+ ---
282
+
283
+ ## Contributing
284
+
285
+ ### Setup
286
+
287
+ ```bash
288
+ git clone https://github.com/phospheneai/authenta-reactnative-sdk.git
289
+ cd authenta-reactnative-sdk
290
+ npm install
291
+ ```
292
+
293
+ ### Build
294
+
295
+ ```bash
296
+ npm run build --workspace=packages/core
297
+ ```
298
+
299
+ ### Test
300
+
301
+ ```bash
302
+ npm test --workspace=packages/core
303
+ ```
304
+
305
+ ### Guidelines
306
+
307
+ - **No native modules** — this package must work in plain Node.js. Do not import React, React Native, or any native module.
308
+ - **No Node.js built-ins at the top level** — Metro (React Native bundler) cannot resolve `fs`, `path`, etc. Use the `_require = require` alias trick only inside runtime guards (`typeof XMLHttpRequest === 'undefined'`).
309
+ - **Typed errors** — all thrown values must extend `AuthentaError`. New error types go in `errors.ts` and must be exported from `index.ts`.
310
+ - **Types in one place** — all interfaces belong in `src/types/index.ts`.
311
+ - **No breaking changes** — `AuthentaClient` and all exported types are the stable public surface.
312
+
313
+ ### Publish
314
+
315
+ ```bash
316
+ # Bump version in packages/core/package.json, then:
317
+ npm publish --workspace=packages/core --access public
318
+ ```
319
+
320
+ ---
321
+
322
+ ## License
323
+
324
+ MIT © Authenta
@@ -0,0 +1,62 @@
1
+ import { ModelType, FIOptions, PollingOptions, RunOptions, CreateMediaResponse, MediaRecord, ListMediaParams, ListMediaResponse, DetectionResult, ProcessedMedia } from './types';
2
+ export interface AuthentaClientConfig {
3
+ baseUrl?: string;
4
+ clientId: string;
5
+ clientSecret: string;
6
+ }
7
+ export declare class AuthentaClient {
8
+ private readonly baseUrl;
9
+ private readonly clientId;
10
+ private readonly clientSecret;
11
+ constructor({ baseUrl, clientId, clientSecret, }: AuthentaClientConfig);
12
+ private get authHeaders();
13
+ private request;
14
+ private throwApiError;
15
+ /** Fetch a local URI once — derives name, type, size, and blob for upload.
16
+ * In React Native uses XMLHttpRequest (fetch('file://...') fails on Android).
17
+ * In Node.js (tests) uses fs since XMLHttpRequest is not available. */
18
+ private resolveUri;
19
+ private uploadToS3;
20
+ createMedia(params: {
21
+ name: string;
22
+ contentType: string;
23
+ size: number;
24
+ modelType: ModelType;
25
+ metadata?: Record<string, any>;
26
+ }): Promise<CreateMediaResponse>;
27
+ getMedia(mid: string): Promise<MediaRecord>;
28
+ listMedia(params?: ListMediaParams): Promise<ListMediaResponse>;
29
+ deleteMedia(mid: string): Promise<void>;
30
+ /**
31
+ * Two-step upload: derives file info from the URI, creates a media record,
32
+ * then PUTs the file blob to S3. Works for all model types.
33
+ * Pass `fiOptions` only when modelType is "FI-1".
34
+ */
35
+ upload(uri: string, modelType: ModelType, fiOptions?: FIOptions): Promise<CreateMediaResponse>;
36
+ waitForMedia(mid: string, { interval, timeout }?: PollingOptions): Promise<MediaRecord>;
37
+ getResult(media: MediaRecord): Promise<DetectionResult>;
38
+ /**
39
+ * Upload a file URI and process it with the given model.
40
+ * The SDK automatically derives the file name, type, and size from the URI.
41
+ *
42
+ * - For all models: uploads, polls until complete, and fetches the result.
43
+ * - For FI-1: pass any face-check flags; unset flags default to false.
44
+ * - Set `autoPolling: false` to return immediately after upload.
45
+ *
46
+ * @example DF-1 / AC-1
47
+ * const result = await client.faceIntelligence('file:///path/to/video.mp4', 'DF-1');
48
+ *
49
+ * @example FI-1 liveness
50
+ * const result = await client.faceIntelligence('file:///path/to/selfie.jpg', 'FI-1', { livenessCheck: true });
51
+ *
52
+ * @example FI-1 faceswap (video only)
53
+ * const result = await client.faceIntelligence('file:///path/to/video.mp4', 'FI-1', { faceswapCheck: true });
54
+ *
55
+ * @example FI-1 similarity
56
+ * const result = await client.faceIntelligence('file:///path/to/selfie.jpg', 'FI-1', {
57
+ * faceSimilarityCheck: true,
58
+ * referenceImage: 'file:///path/to/id-photo.jpg',
59
+ * });
60
+ */
61
+ faceIntelligence(uri: string, modelType: ModelType, { autoPolling, interval, timeout, isSingleFace, faceswapCheck, livenessCheck, faceSimilarityCheck, referenceImage, }?: RunOptions): Promise<ProcessedMedia>;
62
+ }
package/dist/client.js ADDED
@@ -0,0 +1,234 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AuthentaClient = void 0;
4
+ const errors_1 = require("./errors");
5
+ const helpers_1 = require("./utils/helpers");
6
+ const TERMINAL_STATUSES = new Set(['PROCESSED', 'FAILED', 'ERROR']);
7
+ class AuthentaClient {
8
+ constructor({ baseUrl = 'https://platform.authenta.ai', clientId, clientSecret, }) {
9
+ this.baseUrl = baseUrl.replace(/\/$/, '');
10
+ this.clientId = clientId;
11
+ this.clientSecret = clientSecret;
12
+ }
13
+ // ─── Private helpers ───────────────────────────────────────────────────────
14
+ get authHeaders() {
15
+ return {
16
+ 'x-client-id': this.clientId,
17
+ 'x-client-secret': this.clientSecret,
18
+ 'Content-Type': 'application/json',
19
+ };
20
+ }
21
+ async request(method, path, body, queryParams) {
22
+ let url = `${this.baseUrl}${path}`;
23
+ if (queryParams) {
24
+ const qs = Object.entries(queryParams)
25
+ .filter(([, v]) => v !== undefined && v !== null)
26
+ .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`)
27
+ .join('&');
28
+ if (qs)
29
+ url += `?${qs}`;
30
+ }
31
+ const response = await fetch(url, {
32
+ method,
33
+ headers: this.authHeaders,
34
+ body: body !== undefined ? JSON.stringify(body) : undefined,
35
+ });
36
+ if (!response.ok) {
37
+ await this.throwApiError(response);
38
+ }
39
+ const text = await response.text();
40
+ if (!text.trim())
41
+ return {};
42
+ try {
43
+ return JSON.parse(text);
44
+ }
45
+ catch (_a) {
46
+ throw new errors_1.ValidationError('Expected JSON but received non-JSON response', undefined, response.status, { body: text.slice(0, 200) });
47
+ }
48
+ }
49
+ async throwApiError(response) {
50
+ var _a, _b, _c;
51
+ const status = response.status;
52
+ let data;
53
+ try {
54
+ data = await response.json();
55
+ }
56
+ catch (_d) {
57
+ const text = await response.text().catch(() => '');
58
+ if (status >= 500)
59
+ throw new errors_1.ServerError(text || 'Server error', undefined, status);
60
+ throw new errors_1.ValidationError(text || 'Client error', undefined, status);
61
+ }
62
+ const code = (_a = data === null || data === void 0 ? void 0 : data.code) !== null && _a !== void 0 ? _a : 'unknown';
63
+ const message = (_c = (_b = data === null || data === void 0 ? void 0 : data.message) !== null && _b !== void 0 ? _b : response.statusText) !== null && _c !== void 0 ? _c : 'Unknown error';
64
+ if (code === 'IAM001')
65
+ throw new errors_1.AuthenticationError(message, status, data);
66
+ if (code === 'IAM002')
67
+ throw new errors_1.AuthorizationError(message, status, data);
68
+ if (code === 'AA001')
69
+ throw new errors_1.QuotaExceededError(message, status, data);
70
+ if (code === 'U007')
71
+ throw new errors_1.InsufficientCreditsError(message, status, data);
72
+ if (status >= 500)
73
+ throw new errors_1.ServerError(message, code, status, data);
74
+ if (status >= 400)
75
+ throw new errors_1.ValidationError(message, code, status, data);
76
+ throw new errors_1.AuthentaError(message, code, status, data);
77
+ }
78
+ /** Fetch a local URI once — derives name, type, size, and blob for upload.
79
+ * In React Native uses XMLHttpRequest (fetch('file://...') fails on Android).
80
+ * In Node.js (tests) uses fs since XMLHttpRequest is not available. */
81
+ resolveUri(uri) {
82
+ var _a, _b;
83
+ const name = (_b = (_a = uri.split('/').pop()) === null || _a === void 0 ? void 0 : _a.split('?')[0]) !== null && _b !== void 0 ? _b : 'file';
84
+ const type = (0, helpers_1.getMimeType)(name);
85
+ // Node.js environment — XMLHttpRequest does not exist
86
+ if (typeof XMLHttpRequest === 'undefined') {
87
+ // Use aliased require so Metro's static analyser does not try to bundle 'fs'
88
+ const _require = require;
89
+ const fs = _require('fs');
90
+ const filePath = uri.replace(/^file:\/\//, '');
91
+ const buffer = fs.readFileSync(filePath);
92
+ const blob = new Blob([buffer], { type });
93
+ return Promise.resolve({ name, type, size: buffer.byteLength, blob });
94
+ }
95
+ // React Native — use XHR
96
+ return new Promise((resolve, reject) => {
97
+ const xhr = new XMLHttpRequest();
98
+ xhr.responseType = 'blob';
99
+ xhr.onload = () => resolve({ name, type, size: xhr.response.size, blob: xhr.response });
100
+ xhr.onerror = () => reject(new errors_1.AuthentaError(`Could not read file at URI: ${uri}`));
101
+ xhr.open('GET', uri);
102
+ xhr.send();
103
+ });
104
+ }
105
+ async uploadToS3(uploadUrl, blob, contentType) {
106
+ const putResponse = await fetch(uploadUrl, {
107
+ method: 'PUT',
108
+ headers: { 'Content-Type': contentType },
109
+ body: blob,
110
+ });
111
+ if (!putResponse.ok) {
112
+ throw new errors_1.AuthentaError(`S3 upload failed: HTTP ${putResponse.status}`, undefined, putResponse.status);
113
+ }
114
+ }
115
+ // ─── Core media CRUD ───────────────────────────────────────────────────────
116
+ async createMedia(params) {
117
+ return this.request('POST', '/api/media', params);
118
+ }
119
+ async getMedia(mid) {
120
+ return this.request('GET', `/api/media/${mid}`);
121
+ }
122
+ async listMedia(params) {
123
+ return this.request('GET', '/api/media', undefined, params);
124
+ }
125
+ async deleteMedia(mid) {
126
+ await this.request('DELETE', `/api/media/${mid}`);
127
+ }
128
+ // ─── Upload (common for all models) ───────────────────────────────────────
129
+ /**
130
+ * Two-step upload: derives file info from the URI, creates a media record,
131
+ * then PUTs the file blob to S3. Works for all model types.
132
+ * Pass `fiOptions` only when modelType is "FI-1".
133
+ */
134
+ async upload(uri, modelType, fiOptions) {
135
+ const { name, type, size, blob } = await this.resolveUri(uri);
136
+ const payload = {
137
+ name,
138
+ contentType: type,
139
+ size,
140
+ modelType,
141
+ };
142
+ if (modelType.toUpperCase() === 'FI-1' && fiOptions) {
143
+ const { isSingleFace = true, faceswapCheck = false, livenessCheck = false, faceSimilarityCheck = false, } = fiOptions;
144
+ payload.metadata = { isSingleFace, faceswapCheck, livenessCheck, faceSimilarityCheck };
145
+ }
146
+ const media = await this.createMedia(payload);
147
+ await this.uploadToS3(media.uploadUrl, blob, type);
148
+ if (modelType.toUpperCase() === 'FI-1' && (fiOptions === null || fiOptions === void 0 ? void 0 : fiOptions.faceSimilarityCheck)) {
149
+ if (!fiOptions.referenceImage) {
150
+ throw new errors_1.ValidationError('referenceImage is required when faceSimilarityCheck is true');
151
+ }
152
+ if (!media.referenceUploadUrl) {
153
+ throw new errors_1.AuthentaError('No referenceUploadUrl returned from server');
154
+ }
155
+ const { blob: refBlob, type: refType } = await this.resolveUri(fiOptions.referenceImage);
156
+ await this.uploadToS3(media.referenceUploadUrl, refBlob, refType);
157
+ }
158
+ return media;
159
+ }
160
+ // ─── Polling ───────────────────────────────────────────────────────────────
161
+ async waitForMedia(mid, { interval = 5000, timeout = 600000 } = {}) {
162
+ const deadline = Date.now() + timeout;
163
+ while (true) {
164
+ const media = await this.getMedia(mid);
165
+ if (TERMINAL_STATUSES.has(media.status.toUpperCase()))
166
+ return media;
167
+ if (Date.now() >= deadline) {
168
+ throw new errors_1.AuthentaError(`Timed out waiting for media ${mid} — last status: ${media.status}`);
169
+ }
170
+ await new Promise(resolve => setTimeout(resolve, interval));
171
+ }
172
+ }
173
+ // ─── Result ────────────────────────────────────────────────────────────────
174
+ async getResult(media) {
175
+ if (!media.resultURL) {
176
+ throw new errors_1.ValidationError('media has no resultURL — ensure processing is complete (status=PROCESSED)');
177
+ }
178
+ const response = await fetch(media.resultURL);
179
+ if (!response.ok) {
180
+ throw new errors_1.AuthentaError(`Failed to fetch resultURL: HTTP ${response.status}`, undefined, response.status);
181
+ }
182
+ return response.json();
183
+ }
184
+ // ─── High-level: one function for all models ──────────────────────────────
185
+ /**
186
+ * Upload a file URI and process it with the given model.
187
+ * The SDK automatically derives the file name, type, and size from the URI.
188
+ *
189
+ * - For all models: uploads, polls until complete, and fetches the result.
190
+ * - For FI-1: pass any face-check flags; unset flags default to false.
191
+ * - Set `autoPolling: false` to return immediately after upload.
192
+ *
193
+ * @example DF-1 / AC-1
194
+ * const result = await client.faceIntelligence('file:///path/to/video.mp4', 'DF-1');
195
+ *
196
+ * @example FI-1 liveness
197
+ * const result = await client.faceIntelligence('file:///path/to/selfie.jpg', 'FI-1', { livenessCheck: true });
198
+ *
199
+ * @example FI-1 faceswap (video only)
200
+ * const result = await client.faceIntelligence('file:///path/to/video.mp4', 'FI-1', { faceswapCheck: true });
201
+ *
202
+ * @example FI-1 similarity
203
+ * const result = await client.faceIntelligence('file:///path/to/selfie.jpg', 'FI-1', {
204
+ * faceSimilarityCheck: true,
205
+ * referenceImage: 'file:///path/to/id-photo.jpg',
206
+ * });
207
+ */
208
+ async faceIntelligence(uri, modelType, { autoPolling = true, interval, timeout, isSingleFace = true, faceswapCheck = false, livenessCheck = false, faceSimilarityCheck = false, referenceImage, } = {}) {
209
+ var _a;
210
+ const isFI = modelType.toUpperCase() === 'FI-1';
211
+ if (isFI) {
212
+ const type = (0, helpers_1.getMimeType)((_a = uri.split('/').pop()) !== null && _a !== void 0 ? _a : '');
213
+ if ((0, helpers_1.isImage)(type) && faceswapCheck) {
214
+ throw new errors_1.ValidationError('faceswapCheck cannot be true for image files');
215
+ }
216
+ if ((0, helpers_1.isVideo)(type) && faceSimilarityCheck) {
217
+ throw new errors_1.ValidationError('faceSimilarityCheck cannot be true for video files');
218
+ }
219
+ if (faceSimilarityCheck && !referenceImage) {
220
+ throw new errors_1.ValidationError('referenceImage is required when faceSimilarityCheck is true');
221
+ }
222
+ }
223
+ const fiOptions = isFI
224
+ ? { isSingleFace, faceswapCheck, livenessCheck, faceSimilarityCheck, referenceImage }
225
+ : undefined;
226
+ const meta = await this.upload(uri, modelType, fiOptions);
227
+ if (!autoPolling)
228
+ return meta;
229
+ const media = await this.waitForMedia(meta.mid, { interval, timeout });
230
+ const result = media.resultURL ? await this.getResult(media) : undefined;
231
+ return Object.assign(Object.assign({}, media), { result });
232
+ }
233
+ }
234
+ exports.AuthentaClient = AuthentaClient;
@@ -0,0 +1,24 @@
1
+ export declare class AuthentaError extends Error {
2
+ readonly code?: string | undefined;
3
+ readonly statusCode?: number | undefined;
4
+ readonly details?: Record<string, any> | undefined;
5
+ constructor(message: string, code?: string | undefined, statusCode?: number | undefined, details?: Record<string, any> | undefined);
6
+ }
7
+ export declare class AuthenticationError extends AuthentaError {
8
+ constructor(message: string, statusCode?: number, details?: Record<string, any>);
9
+ }
10
+ export declare class AuthorizationError extends AuthentaError {
11
+ constructor(message: string, statusCode?: number, details?: Record<string, any>);
12
+ }
13
+ export declare class QuotaExceededError extends AuthentaError {
14
+ constructor(message: string, statusCode?: number, details?: Record<string, any>);
15
+ }
16
+ export declare class InsufficientCreditsError extends AuthentaError {
17
+ constructor(message: string, statusCode?: number, details?: Record<string, any>);
18
+ }
19
+ export declare class ValidationError extends AuthentaError {
20
+ constructor(message: string, code?: string, statusCode?: number, details?: Record<string, any>);
21
+ }
22
+ export declare class ServerError extends AuthentaError {
23
+ constructor(message: string, code?: string, statusCode?: number, details?: Record<string, any>);
24
+ }
package/dist/errors.js ADDED
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ServerError = exports.ValidationError = exports.InsufficientCreditsError = exports.QuotaExceededError = exports.AuthorizationError = exports.AuthenticationError = exports.AuthentaError = void 0;
4
+ class AuthentaError extends Error {
5
+ constructor(message, code, statusCode, details) {
6
+ super(message);
7
+ this.code = code;
8
+ this.statusCode = statusCode;
9
+ this.details = details;
10
+ this.name = 'AuthentaError';
11
+ }
12
+ }
13
+ exports.AuthentaError = AuthentaError;
14
+ class AuthenticationError extends AuthentaError {
15
+ constructor(message, statusCode, details) {
16
+ super(message, 'IAM001', statusCode, details);
17
+ this.name = 'AuthenticationError';
18
+ }
19
+ }
20
+ exports.AuthenticationError = AuthenticationError;
21
+ class AuthorizationError extends AuthentaError {
22
+ constructor(message, statusCode, details) {
23
+ super(message, 'IAM002', statusCode, details);
24
+ this.name = 'AuthorizationError';
25
+ }
26
+ }
27
+ exports.AuthorizationError = AuthorizationError;
28
+ class QuotaExceededError extends AuthentaError {
29
+ constructor(message, statusCode, details) {
30
+ super(message, 'AA001', statusCode, details);
31
+ this.name = 'QuotaExceededError';
32
+ }
33
+ }
34
+ exports.QuotaExceededError = QuotaExceededError;
35
+ class InsufficientCreditsError extends AuthentaError {
36
+ constructor(message, statusCode, details) {
37
+ super(message, 'U007', statusCode, details);
38
+ this.name = 'InsufficientCreditsError';
39
+ }
40
+ }
41
+ exports.InsufficientCreditsError = InsufficientCreditsError;
42
+ class ValidationError extends AuthentaError {
43
+ constructor(message, code, statusCode, details) {
44
+ super(message, code, statusCode, details);
45
+ this.name = 'ValidationError';
46
+ }
47
+ }
48
+ exports.ValidationError = ValidationError;
49
+ class ServerError extends AuthentaError {
50
+ constructor(message, code, statusCode, details) {
51
+ super(message, code, statusCode, details);
52
+ this.name = 'ServerError';
53
+ }
54
+ }
55
+ exports.ServerError = ServerError;
@@ -0,0 +1,5 @@
1
+ export { AuthentaClient } from './client';
2
+ export type { AuthentaClientConfig } from './client';
3
+ export * from './types';
4
+ export * from './errors';
5
+ export { getMimeType, isImage, isVideo, safeJsonParse } from './utils/helpers';
package/dist/index.js ADDED
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.safeJsonParse = exports.isVideo = exports.isImage = exports.getMimeType = exports.AuthentaClient = void 0;
18
+ var client_1 = require("./client");
19
+ Object.defineProperty(exports, "AuthentaClient", { enumerable: true, get: function () { return client_1.AuthentaClient; } });
20
+ __exportStar(require("./types"), exports);
21
+ __exportStar(require("./errors"), exports);
22
+ var helpers_1 = require("./utils/helpers");
23
+ Object.defineProperty(exports, "getMimeType", { enumerable: true, get: function () { return helpers_1.getMimeType; } });
24
+ Object.defineProperty(exports, "isImage", { enumerable: true, get: function () { return helpers_1.isImage; } });
25
+ Object.defineProperty(exports, "isVideo", { enumerable: true, get: function () { return helpers_1.isVideo; } });
26
+ Object.defineProperty(exports, "safeJsonParse", { enumerable: true, get: function () { return helpers_1.safeJsonParse; } });
@@ -0,0 +1,85 @@
1
+ export type ModelType = 'DF-1' | 'AC-1' | 'FI-1' | (string & {});
2
+ export type MediaStatus = 'PENDING' | 'PROCESSING' | 'PROCESSED' | 'FAILED' | 'ERROR';
3
+ export interface FileInfo {
4
+ uri: string;
5
+ name: string;
6
+ type: string;
7
+ size: number;
8
+ }
9
+ export interface FIOptions {
10
+ isSingleFace?: boolean;
11
+ faceswapCheck?: boolean;
12
+ livenessCheck?: boolean;
13
+ faceSimilarityCheck?: boolean;
14
+ referenceImage?: string;
15
+ }
16
+ export interface PollingOptions {
17
+ interval?: number;
18
+ timeout?: number;
19
+ }
20
+ export interface RunOptions extends FIOptions, PollingOptions {
21
+ autoPolling?: boolean;
22
+ }
23
+ export interface CreateMediaResponse {
24
+ mid: string;
25
+ name: string;
26
+ status: MediaStatus;
27
+ modelType: string;
28
+ contentType: string;
29
+ size: number;
30
+ createdAt: string;
31
+ uploadUrl: string;
32
+ referenceUploadUrl?: string;
33
+ }
34
+ export interface MediaRecord {
35
+ mid: string;
36
+ name: string;
37
+ status: MediaStatus;
38
+ modelType: string;
39
+ contentType: string;
40
+ size: number;
41
+ createdAt: string;
42
+ srcURL?: string;
43
+ resultURL?: string;
44
+ }
45
+ export interface ListMediaParams {
46
+ page?: number;
47
+ pageSize?: number;
48
+ [key: string]: any;
49
+ }
50
+ export interface ListMediaResponse {
51
+ items: MediaRecord[];
52
+ total?: number;
53
+ page?: number;
54
+ pageSize?: number;
55
+ }
56
+ export interface DetectionResult {
57
+ resultType?: string;
58
+ isDeepFake?: string | boolean;
59
+ RealConfidencePercent?: string | number;
60
+ isLiveness?: string | boolean;
61
+ isSimilar?: string | boolean;
62
+ similarityScore?: string | number;
63
+ identityPredictions?: IdentityPrediction[];
64
+ boundingBoxes?: BoundingBoxesMap;
65
+ [key: string]: any;
66
+ }
67
+ export interface IdentityPrediction {
68
+ identityId: number;
69
+ isDeepFake: boolean;
70
+ }
71
+ export type BoundingBoxCoords = [number, number, number, number];
72
+ export interface FrameBoundingBox {
73
+ [frameId: string]: BoundingBoxCoords;
74
+ }
75
+ export interface IdentityBoundingBox {
76
+ boundingBox: FrameBoundingBox;
77
+ class: 'real' | 'fake';
78
+ confidence: number;
79
+ }
80
+ export interface BoundingBoxesMap {
81
+ [identityId: string]: IdentityBoundingBox;
82
+ }
83
+ export interface ProcessedMedia extends MediaRecord {
84
+ result?: DetectionResult;
85
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,4 @@
1
+ export declare const getMimeType: (path: string) => string;
2
+ export declare const isImage: (mimeType: string) => boolean;
3
+ export declare const isVideo: (mimeType: string) => boolean;
4
+ export declare const safeJsonParse: (jsonString: string) => any;
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.safeJsonParse = exports.isVideo = exports.isImage = exports.getMimeType = void 0;
4
+ const getMimeType = (path) => {
5
+ const mimeTypes = {
6
+ // Images
7
+ '.jpg': 'image/jpeg',
8
+ '.jpeg': 'image/jpeg',
9
+ '.png': 'image/png',
10
+ '.gif': 'image/gif',
11
+ '.webp': 'image/webp',
12
+ '.bmp': 'image/bmp',
13
+ '.svg': 'image/svg+xml',
14
+ // Videos
15
+ '.mp4': 'video/mp4',
16
+ '.mov': 'video/quicktime',
17
+ '.avi': 'video/x-msvideo',
18
+ '.mkv': 'video/x-matroska',
19
+ '.webm': 'video/webm',
20
+ '.flv': 'video/x-flv',
21
+ '.wmv': 'video/x-ms-wmv',
22
+ '.m4v': 'video/x-m4v',
23
+ '.3gp': 'video/3gpp',
24
+ // Audio
25
+ '.mp3': 'audio/mpeg',
26
+ '.wav': 'audio/wav',
27
+ '.m4a': 'audio/mp4',
28
+ '.aac': 'audio/aac',
29
+ '.flac': 'audio/flac',
30
+ '.ogg': 'audio/ogg',
31
+ };
32
+ const extension = path.substring(path.lastIndexOf('.')).toLowerCase();
33
+ return mimeTypes[extension] || 'application/octet-stream';
34
+ };
35
+ exports.getMimeType = getMimeType;
36
+ const isImage = (mimeType) => {
37
+ return mimeType.startsWith('image/');
38
+ };
39
+ exports.isImage = isImage;
40
+ const isVideo = (mimeType) => {
41
+ return mimeType.startsWith('video/');
42
+ };
43
+ exports.isVideo = isVideo;
44
+ const safeJsonParse = (jsonString) => {
45
+ try {
46
+ return JSON.parse(jsonString);
47
+ }
48
+ catch (error) {
49
+ console.error('JSON parsing error:', error);
50
+ return null;
51
+ }
52
+ };
53
+ exports.safeJsonParse = safeJsonParse;
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@authenta/core",
3
+ "version": "1.0.0",
4
+ "description": "Authenta API client — pure TypeScript, works in Node.js and React Native",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "test": "jest --passWithNoTests",
13
+ "prepublishOnly": "npm run build"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://github.com/phospheneai/authenta-reactnative-sdk.git"
18
+ },
19
+ "keywords": ["authenta", "ekyc", "face", "liveness", "typescript"],
20
+ "author": "Authenta",
21
+ "license": "MIT",
22
+ "devDependencies": {
23
+ "@types/jest": "^29.0.0",
24
+ "@types/node": "^20.0.0",
25
+ "jest": "^29.0.0",
26
+ "ts-jest": "^29.0.0",
27
+ "typescript": "^4.0.0"
28
+ }
29
+ }