extractia-sdk 1.3.0 → 1.4.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/errors.js CHANGED
@@ -1,95 +1,333 @@
1
1
  /**
2
- * Base error for all Extractia SDK errors.
3
- * Always carries the HTTP `status` code and a human-readable `message`.
2
+ * SDK error classes every error is a typed class that extends `ExtractiaError`.
3
+ *
4
+ * Every instance carries:
5
+ * • `status` — HTTP status code (0 for network / timeout errors)
6
+ * • `message` — Technical detail (may be a raw server string)
7
+ * • `userMessage` — Always a polished, user-facing English sentence
8
+ * • `code` — Short machine-readable string (e.g. "AUTH_ERROR")
9
+ * • `requestId` — X-Request-Id / X-Correlation-Id header when present
4
10
  */
11
+
12
+ // ─── Human-readable defaults per status ──────────────────────────────────────
13
+ const STATUS_MESSAGES = {
14
+ 400: "The request contains invalid data. Please check your input.",
15
+ 401: "Your API token is invalid or has expired. Please check your credentials.",
16
+ 402: "Your current plan does not support this feature or your document quota is exhausted.",
17
+ 403: "You do not have permission to perform this action.",
18
+ 404: "The requested resource could not be found.",
19
+ 409: "A resource with this identifier already exists.",
20
+ 429: "Too many requests. Please wait a moment and try again.",
21
+ 500: "The server encountered an unexpected error. Please try again in a moment.",
22
+ 502: "The server received an unexpected response. Please try again.",
23
+ 503: "The service is temporarily unavailable. Please try again in a few minutes.",
24
+ 504: "The server timed out while processing the request. Please try again.",
25
+ };
26
+
27
+ // ─── Base class ───────────────────────────────────────────────────────────────
5
28
  export class ExtractiaError extends Error {
6
- /** @param {string} message @param {number} status */
7
- constructor(message, status) {
29
+ /**
30
+ * @param {string} message Technical detail string.
31
+ * @param {number} [status] — HTTP status code (0 = no response).
32
+ * @param {string} [userMessage] — Human-friendly sentence shown to end users.
33
+ * @param {string} [code] — Machine-readable error code.
34
+ */
35
+ constructor(
36
+ message,
37
+ status = 0,
38
+ userMessage = null,
39
+ code = "SDK_ERROR",
40
+ ) {
8
41
  super(message);
9
42
  this.name = "ExtractiaError";
10
43
  this.status = status;
44
+ this.userMessage = userMessage ?? message;
45
+ this.code = code;
46
+ /** @type {string|null} Populated from X-Request-Id / X-Correlation-Id response header. */
47
+ this.requestId = null;
48
+ }
49
+
50
+ /** Returns true if automatically retrying the same request may succeed. */
51
+ isRetryable() {
52
+ return this.status === 429 || this.status >= 500;
53
+ }
54
+
55
+ toJSON() {
56
+ return {
57
+ name: this.name,
58
+ code: this.code,
59
+ status: this.status,
60
+ message: this.message,
61
+ userMessage: this.userMessage,
62
+ requestId: this.requestId,
63
+ };
11
64
  }
12
65
  }
13
66
 
14
- /**
15
- * Thrown when the API token is missing or invalid (HTTP 401).
16
- */
67
+ // ─── Typed subclasses ─────────────────────────────────────────────────────────
68
+
69
+ /** HTTP 401 — token missing, expired, or malformed. */
17
70
  export class AuthError extends ExtractiaError {
18
- constructor(message = "Unauthorized. Check your API token.") {
19
- super(message, 401);
71
+ constructor(message = STATUS_MESSAGES[401]) {
72
+ super(message, 401, STATUS_MESSAGES[401], "AUTH_ERROR");
20
73
  this.name = "AuthError";
21
74
  }
22
75
  }
23
76
 
24
77
  /**
25
- * Thrown when the account has no permission to perform the action (HTTP 403).
26
- * Typically means the email is unconfirmed or a sub-user lacks the required permission.
78
+ * HTTP 403 authenticated but lacking required permission.
79
+ * Common causes: unconfirmed email, sub-user permission not granted, plan restriction.
27
80
  */
28
81
  export class ForbiddenError extends ExtractiaError {
29
- constructor(message = "Forbidden. Insufficient permissions.") {
30
- super(message, 403);
82
+ constructor(message = STATUS_MESSAGES[403]) {
83
+ super(message, 403, STATUS_MESSAGES[403], "FORBIDDEN");
31
84
  this.name = "ForbiddenError";
32
85
  }
33
86
  }
34
87
 
35
- /**
36
- * Thrown when the active plan does not allow the requested operation (HTTP 402 / 429 tier).
37
- * Check `error.status` to distinguish payment-required (402) from rate-limit (429).
38
- */
88
+ /** HTTP 402 — plan tier does not include this feature. */
39
89
  export class TierError extends ExtractiaError {
90
+ constructor(message = STATUS_MESSAGES[402], status = 402) {
91
+ super(message, status, STATUS_MESSAGES[402], "TIER_LIMIT");
92
+ this.name = "TierError";
93
+ }
94
+ }
95
+
96
+ /** HTTP 402 — document processing quota exhausted for this billing period. */
97
+ export class QuotaError extends ExtractiaError {
40
98
  constructor(
41
- message = "Tier limit reached. Upgrade your plan.",
42
- status = 402,
99
+ message = "You have reached your document processing quota for this billing period. Please upgrade or wait for the next cycle.",
43
100
  ) {
44
- super(message, status);
45
- this.name = "TierError";
101
+ super(message, 402, message, "QUOTA_EXCEEDED");
102
+ this.name = "QuotaError";
46
103
  }
47
104
  }
48
105
 
49
106
  /**
50
- * Thrown when the API rate limit is exceeded (HTTP 429).
107
+ * HTTP 429 requests sent too fast.
108
+ * Check `error.retryAfter` (seconds) from the `Retry-After` header when available.
51
109
  */
52
110
  export class RateLimitError extends ExtractiaError {
53
- constructor(message = "Too many requests. Please slow down.") {
54
- super(message, 429);
111
+ /**
112
+ * @param {string} [message]
113
+ * @param {number|null} [retryAfter] — Seconds to wait before retrying (from Retry-After header).
114
+ */
115
+ constructor(message = STATUS_MESSAGES[429], retryAfter = null) {
116
+ super(message, 429, STATUS_MESSAGES[429], "RATE_LIMITED");
55
117
  this.name = "RateLimitError";
118
+ /** @type {number|null} */
119
+ this.retryAfter = retryAfter;
120
+ }
121
+ isRetryable() {
122
+ return true;
56
123
  }
57
124
  }
58
125
 
59
- /**
60
- * Thrown when a requested resource was not found (HTTP 404).
61
- */
126
+ /** HTTP 404 — the requested resource does not exist. */
62
127
  export class NotFoundError extends ExtractiaError {
63
- constructor(message = "Resource not found.") {
64
- super(message, 404);
128
+ constructor(message = STATUS_MESSAGES[404]) {
129
+ super(message, 404, STATUS_MESSAGES[404], "NOT_FOUND");
65
130
  this.name = "NotFoundError";
66
131
  }
67
132
  }
68
133
 
134
+ /**
135
+ * HTTP 400 — request body / parameters failed server-side validation.
136
+ * May include per-field errors in `error.fields`.
137
+ */
138
+ export class ValidationError extends ExtractiaError {
139
+ /**
140
+ * @param {string} [message]
141
+ * @param {Record<string,string>|null} [fields] — Field-level errors, if the server provides them.
142
+ */
143
+ constructor(
144
+ message = STATUS_MESSAGES[400],
145
+ fields = null,
146
+ ) {
147
+ super(message, 400, STATUS_MESSAGES[400], "VALIDATION_ERROR");
148
+ this.name = "ValidationError";
149
+ /** @type {Record<string,string>|null} */
150
+ this.fields = fields;
151
+ }
152
+ }
153
+
154
+ /** HTTP 409 — a resource with the same unique identifier already exists. */
155
+ export class ConflictError extends ExtractiaError {
156
+ constructor(message = STATUS_MESSAGES[409]) {
157
+ super(message, 409, STATUS_MESSAGES[409], "CONFLICT");
158
+ this.name = "ConflictError";
159
+ }
160
+ }
161
+
162
+ /** HTTP 5xx — unexpected server-side failure. Always retryable. */
163
+ export class ServerError extends ExtractiaError {
164
+ constructor(message = STATUS_MESSAGES[500], status = 500) {
165
+ super(
166
+ message,
167
+ status,
168
+ STATUS_MESSAGES[status] ?? STATUS_MESSAGES[500],
169
+ "SERVER_ERROR",
170
+ );
171
+ this.name = "ServerError";
172
+ }
173
+ isRetryable() {
174
+ return true;
175
+ }
176
+ }
177
+
178
+ /** No HTTP response — connection refused, DNS failure, etc. */
179
+ export class NetworkError extends ExtractiaError {
180
+ constructor(
181
+ message = "Unable to reach the Extractia API. Please check your network connection and try again.",
182
+ ) {
183
+ super(message, 0, message, "NETWORK_ERROR");
184
+ this.name = "NetworkError";
185
+ }
186
+ isRetryable() {
187
+ return true;
188
+ }
189
+ }
190
+
191
+ /** Request timed out before the server responded. */
192
+ export class TimeoutError extends ExtractiaError {
193
+ constructor(
194
+ message = "The request timed out. The server may be under heavy load — please try again in a moment.",
195
+ ) {
196
+ super(message, 0, message, "TIMEOUT");
197
+ this.name = "TimeoutError";
198
+ }
199
+ isRetryable() {
200
+ return true;
201
+ }
202
+ }
203
+
204
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
205
+
206
+ /**
207
+ * Extracts a developer-friendly detail string from any server response body.
208
+ * Handles plain strings, `{ message }`, `{ error }`, `{ detail }`, `{ title }`,
209
+ * `{ errors[] }`, and Spring `{ fieldErrors[] }`.
210
+ *
211
+ * @param {any} data
212
+ * @returns {string|null}
213
+ */
214
+ function extractServerDetail(data) {
215
+ if (!data) return null;
216
+ if (typeof data === "string" && data.trim()) return data.trim();
217
+ if (typeof data === "object") {
218
+ const msg = data.message ?? data.error ?? data.detail ?? data.title;
219
+ if (msg && typeof msg === "string") return msg.trim();
220
+ if (Array.isArray(data.errors) && data.errors.length > 0) {
221
+ const first = data.errors[0];
222
+ return typeof first === "string"
223
+ ? first
224
+ : (first.message ?? first.msg ?? JSON.stringify(first));
225
+ }
226
+ if (Array.isArray(data.fieldErrors) && data.fieldErrors.length > 0) {
227
+ return data.fieldErrors
228
+ .map((e) => `${e.field ?? "field"}: ${e.message ?? e.defaultMessage}`)
229
+ .join("; ");
230
+ }
231
+ }
232
+ return null;
233
+ }
234
+
69
235
  /**
70
236
  * Maps an Axios error to the appropriate typed SDK error.
71
- * Falls back to a generic `ExtractiaError` for unexpected status codes.
237
+ * Always returns an `ExtractiaError` subclass never re-throws.
72
238
  *
73
239
  * @param {import('axios').AxiosError} err
74
240
  * @returns {ExtractiaError}
75
241
  */
76
242
  export function mapAxiosError(err) {
77
- const status = err.response?.status;
78
- const serverMessage = err.response?.data;
79
- const detail = typeof serverMessage === "string" ? serverMessage : undefined;
243
+ // Network / DNS / connection error — no HTTP response at all
244
+ if (!err.response) {
245
+ if (
246
+ err.code === "ECONNABORTED" ||
247
+ err.code === "ETIMEDOUT" ||
248
+ (err.message && err.message.toLowerCase().includes("timeout"))
249
+ ) {
250
+ return new TimeoutError();
251
+ }
252
+ return new NetworkError(err.message || undefined);
253
+ }
254
+
255
+ const status = err.response.status;
256
+ const detail = extractServerDetail(err.response.data);
257
+ const userMessage =
258
+ STATUS_MESSAGES[status] ??
259
+ (status >= 500 ? STATUS_MESSAGES[500] : "Something went wrong. Please try again.");
260
+
261
+ let error;
80
262
 
81
263
  switch (status) {
264
+ case 400: {
265
+ // Collect field-level errors if available
266
+ const body = err.response.data;
267
+ const fields =
268
+ body?.fields ??
269
+ (Array.isArray(body?.fieldErrors)
270
+ ? Object.fromEntries(
271
+ body.fieldErrors.map((f) => [f.field, f.message ?? f.defaultMessage]),
272
+ )
273
+ : null);
274
+ error = new ValidationError(detail ?? STATUS_MESSAGES[400], fields);
275
+ break;
276
+ }
82
277
  case 401:
83
- return new AuthError(detail);
278
+ error = new AuthError(detail ?? undefined);
279
+ break;
84
280
  case 402:
85
- return new TierError(detail);
281
+ // Distinguish document quota exhaustion from a plan-tier restriction
282
+ if (
283
+ detail &&
284
+ (detail.toLowerCase().includes("quota") ||
285
+ detail.toLowerCase().includes("document") ||
286
+ detail.toLowerCase().includes("limit reached"))
287
+ ) {
288
+ error = new QuotaError(detail);
289
+ } else {
290
+ error = new TierError(detail ?? undefined);
291
+ }
292
+ break;
86
293
  case 403:
87
- return new ForbiddenError(detail);
294
+ error = new ForbiddenError(detail ?? undefined);
295
+ break;
88
296
  case 404:
89
- return new NotFoundError(detail);
90
- case 429:
91
- return new RateLimitError(detail);
297
+ error = new NotFoundError(detail ?? undefined);
298
+ break;
299
+ case 409:
300
+ error = new ConflictError(detail ?? undefined);
301
+ break;
302
+ case 429: {
303
+ const retryAfterHeader = err.response.headers?.["retry-after"];
304
+ const retryAfter =
305
+ retryAfterHeader != null ? parseInt(retryAfterHeader, 10) : null;
306
+ error = new RateLimitError(detail ?? undefined, isNaN(retryAfter) ? null : retryAfter);
307
+ break;
308
+ }
92
309
  default:
93
- return new ExtractiaError(detail ?? err.message, status ?? 0);
310
+ if (status >= 500) {
311
+ error = new ServerError(detail ?? STATUS_MESSAGES[status] ?? STATUS_MESSAGES[500], status);
312
+ } else {
313
+ error = new ExtractiaError(
314
+ detail ?? err.message,
315
+ status,
316
+ userMessage,
317
+ `HTTP_${status}`,
318
+ );
319
+ }
94
320
  }
321
+
322
+ // Always set the polished user message
323
+ error.userMessage = userMessage;
324
+
325
+ // Attach request correlation ID if the server provided one
326
+ const reqId =
327
+ err.response.headers?.["x-request-id"] ??
328
+ err.response.headers?.["x-correlation-id"] ??
329
+ null;
330
+ if (reqId) error.requestId = reqId;
331
+
332
+ return error;
95
333
  }
package/src/index.d.ts CHANGED
@@ -206,45 +206,94 @@ export interface OcrRunResult {
206
206
 
207
207
  /** Base error class for all Extractia SDK errors. */
208
208
  export class ExtractiaError extends Error {
209
- /** HTTP status code associated with the error. */
209
+ /** HTTP status code (0 = no response). */
210
210
  status: number;
211
- constructor(message: string, status: number);
211
+ /** Polished, user-facing English sentence always suitable for display. */
212
+ userMessage: string;
213
+ /** Machine-readable error code (e.g. "AUTH_ERROR", "QUOTA_EXCEEDED"). */
214
+ code: string;
215
+ /** X-Request-Id / X-Correlation-Id from the server response header, when present. */
216
+ requestId: string | null;
217
+ constructor(message: string, status?: number, userMessage?: string, code?: string);
218
+ /** Returns true if automatically retrying the same request may succeed. */
219
+ isRetryable(): boolean;
220
+ toJSON(): { name: string; code: string; status: number; message: string; userMessage: string; requestId: string | null };
212
221
  }
213
222
 
214
- /** Thrown when the API token is missing or invalid (HTTP 401). */
223
+ /** HTTP 401 token missing, expired, or malformed. */
215
224
  export class AuthError extends ExtractiaError {
216
225
  constructor(message?: string);
217
226
  }
218
227
 
219
- /** Thrown when the account lacks permission to perform the action (HTTP 403). */
228
+ /** HTTP 403 authenticated but lacking required permission. */
220
229
  export class ForbiddenError extends ExtractiaError {
221
230
  constructor(message?: string);
222
231
  }
223
232
 
224
- /** Thrown when the active plan does not allow the requested operation (HTTP 402). */
233
+ /** HTTP 402 plan tier does not include this feature. */
225
234
  export class TierError extends ExtractiaError {
226
235
  constructor(message?: string, status?: number);
227
236
  }
228
237
 
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;
238
+ /** HTTP 402 document processing quota exhausted for this billing period. */
239
+ export class QuotaError extends ExtractiaError {
233
240
  constructor(message?: string);
234
241
  }
235
242
 
236
- /** Thrown when a requested resource was not found (HTTP 404). */
243
+ /** HTTP 429 requests sent too fast. Check `retryAfter` for the suggested wait time. */
244
+ export class RateLimitError extends ExtractiaError {
245
+ /** Seconds to wait before retrying (from the Retry-After header), or null. */
246
+ retryAfter: number | null;
247
+ constructor(message?: string, retryAfter?: number | null);
248
+ isRetryable(): true;
249
+ }
250
+
251
+ /** HTTP 404 — the requested resource does not exist. */
237
252
  export class NotFoundError extends ExtractiaError {
238
253
  constructor(message?: string);
239
254
  }
240
255
 
256
+ /** HTTP 400 — request body / parameters failed server-side validation. */
257
+ export class ValidationError extends ExtractiaError {
258
+ /** Field-level errors, if the server provided them. */
259
+ fields: Record<string, string> | null;
260
+ constructor(message?: string, fields?: Record<string, string> | null);
261
+ }
262
+
263
+ /** HTTP 409 — a resource with the same unique identifier already exists. */
264
+ export class ConflictError extends ExtractiaError {
265
+ constructor(message?: string);
266
+ }
267
+
268
+ /** HTTP 5xx — unexpected server-side failure. Always retryable. */
269
+ export class ServerError extends ExtractiaError {
270
+ constructor(message?: string, status?: number);
271
+ isRetryable(): true;
272
+ }
273
+
274
+ /** No HTTP response — connection refused, DNS failure, etc. */
275
+ export class NetworkError extends ExtractiaError {
276
+ constructor(message?: string);
277
+ isRetryable(): true;
278
+ }
279
+
280
+ /** Request timed out before the server responded. */
281
+ export class TimeoutError extends ExtractiaError {
282
+ constructor(message?: string);
283
+ isRetryable(): true;
284
+ }
285
+
286
+ /** Maps an Axios error to the appropriate typed SDK error. */
287
+ export function mapAxiosError(err: unknown): ExtractiaError;
288
+
241
289
  // ─── Setup ────────────────────────────────────────────────────────────────────
242
290
 
243
291
  /**
244
292
  * Sets the Bearer API token used for all subsequent requests.
245
293
  * **Must be called before any SDK method.**
246
294
  *
247
- * @param token Your Extractia API token (from Account → API Tokens).
295
+ * @param token Your Extractia API key (from Account → API Tokens).
296
+ * @throws {Error} If the token is empty or not a string.
248
297
  *
249
298
  * @example
250
299
  * import { setToken } from 'extractia-sdk';
@@ -252,12 +301,59 @@ export class NotFoundError extends ExtractiaError {
252
301
  */
253
302
  export function setToken(token: string): void;
254
303
 
304
+ /** Returns the currently configured API token, or `null` if not set. */
305
+ export function getToken(): string | null;
306
+
307
+ /** Returns `true` if an API token has been set. */
308
+ export function hasToken(): boolean;
309
+
310
+ /** Clears the stored API token. Useful for logout flows or test teardown. */
311
+ export function clearToken(): void;
312
+
313
+ /** Returns a snapshot of the current SDK configuration (token excluded). */
314
+ export function getConfig(): {
315
+ baseURL: string;
316
+ timeout: number;
317
+ retries: number;
318
+ retryDelay: number;
319
+ debug: boolean;
320
+ defaultHeaders: Record<string, string>;
321
+ onBeforeRequest: Function | null;
322
+ onAfterResponse: Function | null;
323
+ onError: Function | null;
324
+ };
325
+
255
326
  /**
256
- * Configures SDK options.
257
- * @param opts
258
- * @param [opts.baseURL] Override the default API base URL (useful for self-hosted instances).
327
+ * Configures SDK behaviour. All properties are optional.
328
+ *
329
+ * @example
330
+ * configure({
331
+ * timeout: 30_000,
332
+ * retries: 2,
333
+ * debug: true,
334
+ * onError: (err) => sentryCapture(err),
335
+ * });
259
336
  */
260
- export function configure(opts: { baseURL?: string }): void;
337
+ export function configure(opts: {
338
+ /** Override the API base URL (useful for staging or proxies). */
339
+ baseURL?: string;
340
+ /** Request timeout in milliseconds. Default: 60 000. */
341
+ timeout?: number;
342
+ /** Automatic retries on retryable errors (429 / 5xx / network). Default: 1. */
343
+ retries?: number;
344
+ /** Base delay (ms) between retries; multiplied by attempt number. Default: 1 000. */
345
+ retryDelay?: number;
346
+ /** Log requests / responses / retries to console.debug. Default: false. */
347
+ debug?: boolean;
348
+ /** Extra headers merged into every request. */
349
+ defaultHeaders?: Record<string, string>;
350
+ /** Hook called before each request. Return a modified config or void. */
351
+ onBeforeRequest?: (config: unknown) => unknown | void;
352
+ /** Hook called after each successful response. */
353
+ onAfterResponse?: (response: unknown) => void;
354
+ /** Hook called with the mapped SDK error whenever a request fails (after retries). */
355
+ onError?: (error: ExtractiaError) => void;
356
+ }): void;
261
357
 
262
358
  // ─── Auth / Profile ───────────────────────────────────────────────────────────
263
359
 
@@ -694,11 +790,87 @@ export function toggleSuspendSubUser(username: string): Promise<{ username: stri
694
790
 
695
791
  // ─── Default export ───────────────────────────────────────────────────────────
696
792
 
793
+ // ─── Utilities ───────────────────────────────────────────────────────────────
794
+
795
+ /**
796
+ * Converts a browser `File` or `Blob` to a base64 data-URL string.
797
+ * @throws {Error} In Node.js environments where `FileReader` is unavailable.
798
+ */
799
+ export function fileToBase64(file: File | Blob): Promise<string>;
800
+
801
+ /**
802
+ * Strips the `data:…;base64,` prefix from a base64 string.
803
+ * Safe to call on a plain base64 string — returns it unchanged.
804
+ */
805
+ export function stripDataUrlPrefix(base64OrDataUrl: string): string;
806
+
807
+ /**
808
+ * Returns the MIME type embedded in a data-URL prefix, or `null`.
809
+ * @example getMimeType('data:image/png;base64,...') // → 'image/png'
810
+ */
811
+ export function getMimeType(dataUrl: string): string | null;
812
+
813
+ /**
814
+ * Returns `true` if the string is a valid base64-encoded value
815
+ * (with or without a data-URL prefix).
816
+ */
817
+ export function isBase64(str: string): boolean;
818
+
819
+ /**
820
+ * Accepts a `File`, `Blob`, or base64 string and always resolves to a
821
+ * base64 string — the data-URL for files and the string as-is otherwise.
822
+ */
823
+ export function ensureBase64(fileOrBase64: File | Blob | string): Promise<string>;
824
+
825
+ /**
826
+ * Async generator that yields individual items across all pages of a
827
+ * paginated SDK function.
828
+ *
829
+ * @example
830
+ * for await (const entry of paginate(getCreditsHistory, { size: 100 })) {
831
+ * console.log(entry);
832
+ * }
833
+ */
834
+ export function paginate<T>(
835
+ fn: (opts: { page: number; size: number }) => Promise<{ content: T[]; totalPages: number }>,
836
+ opts?: { size?: number; startPage?: number; maxPages?: number }
837
+ ): AsyncGenerator<T>;
838
+
839
+ /**
840
+ * Collects all pages of a paginated SDK function into a flat array.
841
+ */
842
+ export function paginateAll<T>(
843
+ fn: (opts: { page: number; size: number }) => Promise<{ content: T[]; totalPages: number }>,
844
+ opts?: { size?: number; startPage?: number; maxPages?: number }
845
+ ): Promise<T[]>;
846
+
847
+ /** Returns a Promise that resolves after `ms` milliseconds. */
848
+ export function delay(ms: number): Promise<void>;
849
+
850
+ /**
851
+ * Calls an async function and retries it with exponential back-off on
852
+ * retryable `ExtractiaError`s.
853
+ */
854
+ export function withRetry<T>(
855
+ fn: () => Promise<T>,
856
+ opts?: {
857
+ retries?: number;
858
+ initialDelay?: number;
859
+ shouldRetry?: (err: Error) => boolean;
860
+ }
861
+ ): Promise<T>;
862
+
863
+ // ─── Default export ───────────────────────────────────────────────────────────
864
+
697
865
  /** Namespace object bundling all SDK methods for UMD / CommonJS consumers. */
698
866
  declare const extractia: {
699
867
  // Setup
700
868
  setToken: typeof setToken;
869
+ getToken: typeof getToken;
870
+ hasToken: typeof hasToken;
871
+ clearToken: typeof clearToken;
701
872
  configure: typeof configure;
873
+ getConfig: typeof getConfig;
702
874
  // Auth
703
875
  getMyProfile: typeof getMyProfile;
704
876
  updateWebhook: typeof updateWebhook;
@@ -741,6 +913,16 @@ declare const extractia: {
741
913
  deleteSubUser: typeof deleteSubUser;
742
914
  updateSubUser: typeof updateSubUser;
743
915
  toggleSuspendSubUser: typeof toggleSuspendSubUser;
916
+ // Utilities
917
+ fileToBase64: typeof fileToBase64;
918
+ stripDataUrlPrefix: typeof stripDataUrlPrefix;
919
+ getMimeType: typeof getMimeType;
920
+ isBase64: typeof isBase64;
921
+ ensureBase64: typeof ensureBase64;
922
+ paginate: typeof paginate;
923
+ paginateAll: typeof paginateAll;
924
+ delay: typeof delay;
925
+ withRetry: typeof withRetry;
744
926
  };
745
927
 
746
928
  export default extractia;