extractia-sdk 1.3.0 → 1.5.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/src/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- // ─── Extractia SDK v1.1 — TypeScript type declarations ───────────────────────
1
+ // ─── Extractia SDK v1.5 — TypeScript type declarations ───────────────────────
2
2
  // Keep in sync with JavaScript source files.
3
3
 
4
4
  // ─── Primitives ───────────────────────────────────────────────────────────────
@@ -111,8 +111,14 @@ export interface DocumentAuditEntry {
111
111
  /** A sub-user belonging to an account. */
112
112
  export interface SubUser {
113
113
  username: string;
114
- /** Granted permission strings (e.g. "upload", "view", "template", "settings", "export", "api"). */
114
+ /**
115
+ * Granted permission strings.
116
+ * Valid values: `"upload"`, `"view"`, `"template"`, `"settings"`, `"export"`, `"api"`,
117
+ * `"ocr_tools"`, `"gallery"`, `"smart_scan"`, `"ia_agent"`, `"assistant"`.
118
+ */
115
119
  permissions: string[];
120
+ /** Optional list of form template IDs this sub-user may access. Null = access to all. */
121
+ allowedFormIds?: string[] | null;
116
122
  /** Whether the sub-user is currently suspended. */
117
123
  suspended: boolean;
118
124
  /** Last known geographic location ("lat,lng" format). */
@@ -202,49 +208,139 @@ export interface OcrRunResult {
202
208
  explanation: string;
203
209
  }
204
210
 
211
+ /** A single AI Agent execution record stored in `ocr_tool_executions`. */
212
+ export interface OcrToolExecution {
213
+ id: string;
214
+ toolId: string;
215
+ /** Snapshot of the tool name at execution time. */
216
+ toolName: string;
217
+ /** Original image encoded as base64 (with or without data-URL prefix). */
218
+ imageBase64: string;
219
+ mimeType: string;
220
+ /** Dynamic parameter values supplied at run time, keyed 1-based (e.g. `{ "1": "Main St" }`). */
221
+ paramValues?: Record<string, string> | null;
222
+ /** AI answer text (YES/NO, label, or free-form text). */
223
+ answer: string;
224
+ /** Short rationale explaining the answer. */
225
+ explanation: string;
226
+ /** Parsed boolean for `YES_NO` tools; `null` for `LABEL` and `TEXT` tools. */
227
+ booleanAnswer: boolean | null;
228
+ /** `"SUCCESS"` or `"FAILURE"`. */
229
+ status: 'SUCCESS' | 'FAILURE';
230
+ /** Error message when `status` is `"FAILURE"`. */
231
+ errorMessage?: string;
232
+ /** Wall-clock time in milliseconds for the AI provider call. */
233
+ processingTimeMs?: number;
234
+ /** ISO 8601 UTC timestamp. */
235
+ createdAt: string;
236
+ }
237
+
238
+ /** Spring-style paginated response wrapping a list of {@link OcrToolExecution} records. */
239
+ export interface OcrExecutionPage {
240
+ content: OcrToolExecution[];
241
+ totalElements: number;
242
+ totalPages: number;
243
+ /** Applied page size. */
244
+ size: number;
245
+ /** 0-based page index. */
246
+ number: number;
247
+ first: boolean;
248
+ last: boolean;
249
+ empty: boolean;
250
+ }
251
+
205
252
  // ─── Error classes ────────────────────────────────────────────────────────────
206
253
 
207
254
  /** Base error class for all Extractia SDK errors. */
208
255
  export class ExtractiaError extends Error {
209
- /** HTTP status code associated with the error. */
256
+ /** HTTP status code (0 = no response). */
210
257
  status: number;
211
- constructor(message: string, status: number);
258
+ /** Polished, user-facing English sentence always suitable for display. */
259
+ userMessage: string;
260
+ /** Machine-readable error code (e.g. "AUTH_ERROR", "QUOTA_EXCEEDED"). */
261
+ code: string;
262
+ /** X-Request-Id / X-Correlation-Id from the server response header, when present. */
263
+ requestId: string | null;
264
+ constructor(message: string, status?: number, userMessage?: string, code?: string);
265
+ /** Returns true if automatically retrying the same request may succeed. */
266
+ isRetryable(): boolean;
267
+ toJSON(): { name: string; code: string; status: number; message: string; userMessage: string; requestId: string | null };
212
268
  }
213
269
 
214
- /** Thrown when the API token is missing or invalid (HTTP 401). */
270
+ /** HTTP 401 token missing, expired, or malformed. */
215
271
  export class AuthError extends ExtractiaError {
216
272
  constructor(message?: string);
217
273
  }
218
274
 
219
- /** Thrown when the account lacks permission to perform the action (HTTP 403). */
275
+ /** HTTP 403 authenticated but lacking required permission. */
220
276
  export class ForbiddenError extends ExtractiaError {
221
277
  constructor(message?: string);
222
278
  }
223
279
 
224
- /** Thrown when the active plan does not allow the requested operation (HTTP 402). */
280
+ /** HTTP 402 plan tier does not include this feature. */
225
281
  export class TierError extends ExtractiaError {
226
282
  constructor(message?: string, status?: number);
227
283
  }
228
284
 
229
- /** Thrown when the API rate limit is exceeded (HTTP 429). */
230
- export class RateLimitError extends ExtractiaError {
231
- /** Seconds to wait before the next request. */
232
- retryAfter?: number;
285
+ /** HTTP 402 document processing quota exhausted for this billing period. */
286
+ export class QuotaError extends ExtractiaError {
233
287
  constructor(message?: string);
234
288
  }
235
289
 
236
- /** Thrown when a requested resource was not found (HTTP 404). */
290
+ /** HTTP 429 requests sent too fast. Check `retryAfter` for the suggested wait time. */
291
+ export class RateLimitError extends ExtractiaError {
292
+ /** Seconds to wait before retrying (from the Retry-After header), or null. */
293
+ retryAfter: number | null;
294
+ constructor(message?: string, retryAfter?: number | null);
295
+ isRetryable(): true;
296
+ }
297
+
298
+ /** HTTP 404 — the requested resource does not exist. */
237
299
  export class NotFoundError extends ExtractiaError {
238
300
  constructor(message?: string);
239
301
  }
240
302
 
303
+ /** HTTP 400 — request body / parameters failed server-side validation. */
304
+ export class ValidationError extends ExtractiaError {
305
+ /** Field-level errors, if the server provided them. */
306
+ fields: Record<string, string> | null;
307
+ constructor(message?: string, fields?: Record<string, string> | null);
308
+ }
309
+
310
+ /** HTTP 409 — a resource with the same unique identifier already exists. */
311
+ export class ConflictError extends ExtractiaError {
312
+ constructor(message?: string);
313
+ }
314
+
315
+ /** HTTP 5xx — unexpected server-side failure. Always retryable. */
316
+ export class ServerError extends ExtractiaError {
317
+ constructor(message?: string, status?: number);
318
+ isRetryable(): true;
319
+ }
320
+
321
+ /** No HTTP response — connection refused, DNS failure, etc. */
322
+ export class NetworkError extends ExtractiaError {
323
+ constructor(message?: string);
324
+ isRetryable(): true;
325
+ }
326
+
327
+ /** Request timed out before the server responded. */
328
+ export class TimeoutError extends ExtractiaError {
329
+ constructor(message?: string);
330
+ isRetryable(): true;
331
+ }
332
+
333
+ /** Maps an Axios error to the appropriate typed SDK error. */
334
+ export function mapAxiosError(err: unknown): ExtractiaError;
335
+
241
336
  // ─── Setup ────────────────────────────────────────────────────────────────────
242
337
 
243
338
  /**
244
339
  * Sets the Bearer API token used for all subsequent requests.
245
340
  * **Must be called before any SDK method.**
246
341
  *
247
- * @param token Your Extractia API token (from Account → API Tokens).
342
+ * @param token Your Extractia API key (from Account → API Tokens).
343
+ * @throws {Error} If the token is empty or not a string.
248
344
  *
249
345
  * @example
250
346
  * import { setToken } from 'extractia-sdk';
@@ -252,12 +348,59 @@ export class NotFoundError extends ExtractiaError {
252
348
  */
253
349
  export function setToken(token: string): void;
254
350
 
351
+ /** Returns the currently configured API token, or `null` if not set. */
352
+ export function getToken(): string | null;
353
+
354
+ /** Returns `true` if an API token has been set. */
355
+ export function hasToken(): boolean;
356
+
357
+ /** Clears the stored API token. Useful for logout flows or test teardown. */
358
+ export function clearToken(): void;
359
+
360
+ /** Returns a snapshot of the current SDK configuration (token excluded). */
361
+ export function getConfig(): {
362
+ baseURL: string;
363
+ timeout: number;
364
+ retries: number;
365
+ retryDelay: number;
366
+ debug: boolean;
367
+ defaultHeaders: Record<string, string>;
368
+ onBeforeRequest: Function | null;
369
+ onAfterResponse: Function | null;
370
+ onError: Function | null;
371
+ };
372
+
255
373
  /**
256
- * Configures SDK options.
257
- * @param opts
258
- * @param [opts.baseURL] Override the default API base URL (useful for self-hosted instances).
374
+ * Configures SDK behaviour. All properties are optional.
375
+ *
376
+ * @example
377
+ * configure({
378
+ * timeout: 30_000,
379
+ * retries: 2,
380
+ * debug: true,
381
+ * onError: (err) => sentryCapture(err),
382
+ * });
259
383
  */
260
- export function configure(opts: { baseURL?: string }): void;
384
+ export function configure(opts: {
385
+ /** Override the API base URL (useful for staging or proxies). */
386
+ baseURL?: string;
387
+ /** Request timeout in milliseconds. Default: 60 000. */
388
+ timeout?: number;
389
+ /** Automatic retries on retryable errors (429 / 5xx / network). Default: 1. */
390
+ retries?: number;
391
+ /** Base delay (ms) between retries; multiplied by attempt number. Default: 1 000. */
392
+ retryDelay?: number;
393
+ /** Log requests / responses / retries to console.debug. Default: false. */
394
+ debug?: boolean;
395
+ /** Extra headers merged into every request. */
396
+ defaultHeaders?: Record<string, string>;
397
+ /** Hook called before each request. Return a modified config or void. */
398
+ onBeforeRequest?: (config: unknown) => unknown | void;
399
+ /** Hook called after each successful response. */
400
+ onAfterResponse?: (response: unknown) => void;
401
+ /** Hook called with the mapped SDK error whenever a request fails (after retries). */
402
+ onError?: (error: ExtractiaError) => void;
403
+ }): void;
261
404
 
262
405
  // ─── Auth / Profile ───────────────────────────────────────────────────────────
263
406
 
@@ -634,6 +777,24 @@ export function deleteOcrTool(id: string): Promise<void>;
634
777
  */
635
778
  export function runOcrTool(id: string, base64Image: string, options?: OcrRunOptions): Promise<OcrRunResult>;
636
779
 
780
+ /**
781
+ * Returns a paginated list of OCR tool execution history for the authenticated user.
782
+ *
783
+ * Each record includes the original image, answer, explanation, and run metadata.
784
+ * Keep page sizes small when rendering image thumbnails.
785
+ *
786
+ * @param options.toolId Filter by tool ID. Omit to return executions for all tools.
787
+ * @param options.page 0-based page index (default `0`).
788
+ * @param options.size Page size, 1–50 (default `20`; values above 50 are capped server-side).
789
+ *
790
+ * @throws {AuthError} 401 if the user is not authenticated.
791
+ */
792
+ export function getOcrToolExecutions(options?: {
793
+ toolId?: string;
794
+ page?: number;
795
+ size?: number;
796
+ }): Promise<OcrExecutionPage>;
797
+
637
798
  // ─── Sub-Users ────────────────────────────────────────────────────────────────
638
799
 
639
800
  /**
@@ -645,7 +806,8 @@ export function getSubUsers(): Promise<SubUser[]>;
645
806
  /**
646
807
  * Creates a new sub-user.
647
808
  *
648
- * Available permissions: `"upload"`, `"view"`, `"template"`, `"settings"`, `"export"`, `"api"`.
809
+ * Available permissions: `"upload"`, `"view"`, `"template"`, `"settings"`, `"export"`, `"api"`,
810
+ * `"ocr_tools"`, `"gallery"`, `"smart_scan"`, `"ia_agent"`, `"assistant"`.
649
811
  *
650
812
  * @throws {ForbiddenError} 403 if the plan does not support sub-users or the limit is reached.
651
813
  * @throws {ExtractiaError} 409 if the username is already taken.
@@ -654,14 +816,17 @@ export function getSubUsers(): Promise<SubUser[]>;
654
816
  * await createSubUser({
655
817
  * username: 'agent_bob',
656
818
  * password: 'SecurePass1',
657
- * permissions: ['upload', 'view'],
819
+ * permissions: ['upload', 'view', 'ocr_tools'],
820
+ * allowedFormIds: ['tpl_123'], // optional — omit for access to all forms
658
821
  * });
659
822
  */
660
823
  export function createSubUser(subUser: {
661
824
  username: string;
662
825
  password: string;
663
826
  permissions: string[];
664
- }): Promise<{ username: string; permissions: string[] }>;
827
+ /** Restrict this sub-user to specific form templates. Omit or pass null for all forms. */
828
+ allowedFormIds?: string[] | null;
829
+ }): Promise<{ username: string; permissions: string[]; allowedFormIds?: string[] | null }>;
665
830
 
666
831
  /**
667
832
  * Deletes a sub-user by username.
@@ -671,7 +836,7 @@ export function createSubUser(subUser: {
671
836
  export function deleteSubUser(username: string): Promise<{ deleted: string }>;
672
837
 
673
838
  /**
674
- * Updates the permissions and/or password of a sub-user.
839
+ * Updates the permissions, allowed forms, and/or password of a sub-user.
675
840
  * Only the provided fields are changed.
676
841
  *
677
842
  * @param username The sub-user's username.
@@ -680,7 +845,12 @@ export function deleteSubUser(username: string): Promise<{ deleted: string }>;
680
845
  */
681
846
  export function updateSubUser(
682
847
  username: string,
683
- updates: { permissions?: string[]; password?: string }
848
+ updates: {
849
+ permissions?: string[];
850
+ password?: string;
851
+ /** New allowed form ID list (replaces existing). Pass empty array to remove all restrictions. */
852
+ allowedFormIds?: string[] | null;
853
+ }
684
854
  ): Promise<{ username: string; updated: boolean }>;
685
855
 
686
856
  /**
@@ -694,11 +864,87 @@ export function toggleSuspendSubUser(username: string): Promise<{ username: stri
694
864
 
695
865
  // ─── Default export ───────────────────────────────────────────────────────────
696
866
 
867
+ // ─── Utilities ───────────────────────────────────────────────────────────────
868
+
869
+ /**
870
+ * Converts a browser `File` or `Blob` to a base64 data-URL string.
871
+ * @throws {Error} In Node.js environments where `FileReader` is unavailable.
872
+ */
873
+ export function fileToBase64(file: File | Blob): Promise<string>;
874
+
875
+ /**
876
+ * Strips the `data:…;base64,` prefix from a base64 string.
877
+ * Safe to call on a plain base64 string — returns it unchanged.
878
+ */
879
+ export function stripDataUrlPrefix(base64OrDataUrl: string): string;
880
+
881
+ /**
882
+ * Returns the MIME type embedded in a data-URL prefix, or `null`.
883
+ * @example getMimeType('data:image/png;base64,...') // → 'image/png'
884
+ */
885
+ export function getMimeType(dataUrl: string): string | null;
886
+
887
+ /**
888
+ * Returns `true` if the string is a valid base64-encoded value
889
+ * (with or without a data-URL prefix).
890
+ */
891
+ export function isBase64(str: string): boolean;
892
+
893
+ /**
894
+ * Accepts a `File`, `Blob`, or base64 string and always resolves to a
895
+ * base64 string — the data-URL for files and the string as-is otherwise.
896
+ */
897
+ export function ensureBase64(fileOrBase64: File | Blob | string): Promise<string>;
898
+
899
+ /**
900
+ * Async generator that yields individual items across all pages of a
901
+ * paginated SDK function.
902
+ *
903
+ * @example
904
+ * for await (const entry of paginate(getCreditsHistory, { size: 100 })) {
905
+ * console.log(entry);
906
+ * }
907
+ */
908
+ export function paginate<T>(
909
+ fn: (opts: { page: number; size: number }) => Promise<{ content: T[]; totalPages: number }>,
910
+ opts?: { size?: number; startPage?: number; maxPages?: number }
911
+ ): AsyncGenerator<T>;
912
+
913
+ /**
914
+ * Collects all pages of a paginated SDK function into a flat array.
915
+ */
916
+ export function paginateAll<T>(
917
+ fn: (opts: { page: number; size: number }) => Promise<{ content: T[]; totalPages: number }>,
918
+ opts?: { size?: number; startPage?: number; maxPages?: number }
919
+ ): Promise<T[]>;
920
+
921
+ /** Returns a Promise that resolves after `ms` milliseconds. */
922
+ export function delay(ms: number): Promise<void>;
923
+
924
+ /**
925
+ * Calls an async function and retries it with exponential back-off on
926
+ * retryable `ExtractiaError`s.
927
+ */
928
+ export function withRetry<T>(
929
+ fn: () => Promise<T>,
930
+ opts?: {
931
+ retries?: number;
932
+ initialDelay?: number;
933
+ shouldRetry?: (err: Error) => boolean;
934
+ }
935
+ ): Promise<T>;
936
+
937
+ // ─── Default export ───────────────────────────────────────────────────────────
938
+
697
939
  /** Namespace object bundling all SDK methods for UMD / CommonJS consumers. */
698
940
  declare const extractia: {
699
941
  // Setup
700
942
  setToken: typeof setToken;
943
+ getToken: typeof getToken;
944
+ hasToken: typeof hasToken;
945
+ clearToken: typeof clearToken;
701
946
  configure: typeof configure;
947
+ getConfig: typeof getConfig;
702
948
  // Auth
703
949
  getMyProfile: typeof getMyProfile;
704
950
  updateWebhook: typeof updateWebhook;
@@ -735,12 +981,23 @@ declare const extractia: {
735
981
  updateOcrTool: typeof updateOcrTool;
736
982
  deleteOcrTool: typeof deleteOcrTool;
737
983
  runOcrTool: typeof runOcrTool;
984
+ getOcrToolExecutions: typeof getOcrToolExecutions;
738
985
  // Sub-users
739
986
  getSubUsers: typeof getSubUsers;
740
987
  createSubUser: typeof createSubUser;
741
988
  deleteSubUser: typeof deleteSubUser;
742
989
  updateSubUser: typeof updateSubUser;
743
990
  toggleSuspendSubUser: typeof toggleSuspendSubUser;
991
+ // Utilities
992
+ fileToBase64: typeof fileToBase64;
993
+ stripDataUrlPrefix: typeof stripDataUrlPrefix;
994
+ getMimeType: typeof getMimeType;
995
+ isBase64: typeof isBase64;
996
+ ensureBase64: typeof ensureBase64;
997
+ paginate: typeof paginate;
998
+ paginateAll: typeof paginateAll;
999
+ delay: typeof delay;
1000
+ withRetry: typeof withRetry;
744
1001
  };
745
1002
 
746
1003
  export default extractia;
package/src/index.js CHANGED
@@ -4,14 +4,23 @@ export * from "./documents.js";
4
4
  export * from "./analytics.js";
5
5
  export * from "./ocrTools.js";
6
6
  export * from "./subusers.js";
7
+ export * from "./utils.js";
7
8
  export {
8
9
  ExtractiaError,
9
10
  AuthError,
10
11
  ForbiddenError,
11
12
  TierError,
13
+ QuotaError,
12
14
  RateLimitError,
13
15
  NotFoundError,
16
+ ValidationError,
17
+ ConflictError,
18
+ ServerError,
19
+ NetworkError,
20
+ TimeoutError,
21
+ mapAxiosError,
14
22
  } from "./errors.js";
23
+ export { getToken, hasToken, clearToken, getConfig } from "./apiClient.js";
15
24
 
16
25
  import * as auth from "./auth.js";
17
26
  import * as templates from "./templates.js";
@@ -19,6 +28,8 @@ import * as documents from "./documents.js";
19
28
  import * as analytics from "./analytics.js";
20
29
  import * as ocrTools from "./ocrTools.js";
21
30
  import * as subusers from "./subusers.js";
31
+ import * as utils from "./utils.js";
32
+ import { getToken, hasToken, clearToken, getConfig } from "./apiClient.js";
22
33
 
23
34
  const extractia = {
24
35
  ...auth,
@@ -27,6 +38,11 @@ const extractia = {
27
38
  ...analytics,
28
39
  ...ocrTools,
29
40
  ...subusers,
41
+ ...utils,
42
+ getToken,
43
+ hasToken,
44
+ clearToken,
45
+ getConfig,
30
46
  };
31
47
 
32
48
  export default extractia;
package/src/ocrTools.js CHANGED
@@ -82,3 +82,51 @@ export async function runOcrTool(id, base64Image, options = {}) {
82
82
  const res = await api.post(`/ocr-tools/${id}/run`, body);
83
83
  return res.data;
84
84
  }
85
+
86
+ /**
87
+ * Returns a paginated list of OCR tool execution history for the authenticated user.
88
+ *
89
+ * Each record contains the original image, the answer, explanation, and metadata about
90
+ * the run. Image data (`imageBase64`) is included, so pages should be kept small when
91
+ * rendering thumbnails.
92
+ *
93
+ * @param {Object} [options] - Query options.
94
+ * @param {string} [options.toolId] - Filter by tool ID. Omit to return executions for all tools.
95
+ * @param {number} [options.page=0] - 0-based page index.
96
+ * @param {number} [options.size=20] - Page size (1–50; values above 50 are capped server-side).
97
+ * @returns {Promise<{
98
+ * content: Array<{
99
+ * id: string,
100
+ * toolId: string,
101
+ * toolName: string,
102
+ * imageBase64: string,
103
+ * mimeType: string,
104
+ * paramValues: Record<string, string> | null,
105
+ * answer: string,
106
+ * explanation: string,
107
+ * booleanAnswer: boolean | null,
108
+ * status: 'SUCCESS' | 'FAILURE',
109
+ * errorMessage?: string,
110
+ * processingTimeMs: number,
111
+ * createdAt: string
112
+ * }>,
113
+ * totalElements: number,
114
+ * totalPages: number,
115
+ * size: number,
116
+ * number: number,
117
+ * first: boolean,
118
+ * last: boolean,
119
+ * empty: boolean
120
+ * }>}
121
+ * @throws {AuthError} 401 if the user is not authenticated.
122
+ */
123
+ export async function getOcrToolExecutions({
124
+ toolId,
125
+ page = 0,
126
+ size = 20,
127
+ } = {}) {
128
+ const params = { page, size };
129
+ if (toolId) params.toolId = toolId;
130
+ const res = await api.get("/ocr-tools/executions", { params });
131
+ return res.data;
132
+ }
package/src/subusers.js CHANGED
@@ -26,7 +26,7 @@ export async function getSubUsers() {
26
26
  * Creates a new sub-user for the authenticated account.
27
27
  *
28
28
  * Available permissions: `"upload"`, `"view"`, `"template"`, `"settings"`,
29
- * `"export"`, `"ocr_tools"`, `"gallery"`, `"smart_scan"`, `"ia_agent"`.
29
+ * `"export"`, `"api"`, `"ocr_tools"`, `"gallery"`, `"smart_scan"`, `"ia_agent"`, `"assistant"`.
30
30
  *
31
31
  * @param {Object} subUser - Sub-user definition.
32
32
  * @param {string} subUser.username - Unique username (must not be an existing account email).