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.
@@ -392,15 +392,15 @@ var ExtractiaSDK = (() => {
392
392
  if (setImmediateSupported) {
393
393
  return setImmediate;
394
394
  }
395
- return postMessageSupported ? ((token2, callbacks) => {
395
+ return postMessageSupported ? ((token, callbacks) => {
396
396
  _global.addEventListener("message", ({ source, data }) => {
397
- if (source === _global && data === token2) {
397
+ if (source === _global && data === token) {
398
398
  callbacks.length && callbacks.shift()();
399
399
  }
400
400
  }, false);
401
401
  return (cb) => {
402
402
  callbacks.push(cb);
403
- _global.postMessage(token2, "*");
403
+ _global.postMessage(token, "*");
404
404
  };
405
405
  })(`axios@${Math.random()}`, []) : (cb) => setTimeout(cb);
406
406
  })(
@@ -556,9 +556,9 @@ var ExtractiaSDK = (() => {
556
556
  }
557
557
  function renderKey(path, key, dots) {
558
558
  if (!path) return key;
559
- return path.concat(key).map(function each(token2, i) {
560
- token2 = removeBrackets(token2);
561
- return !dots && i ? "[" + token2 + "]" : token2;
559
+ return path.concat(key).map(function each(token, i) {
560
+ token = removeBrackets(token);
561
+ return !dots && i ? "[" + token + "]" : token;
562
562
  }).join(dots ? "." : "");
563
563
  }
564
564
  function isFlatArray(arr) {
@@ -1601,7 +1601,7 @@ var ExtractiaSDK = (() => {
1601
1601
  if (platform_default.hasStandardBrowserEnv || platform_default.hasStandardBrowserWebWorkerEnv) {
1602
1602
  headers.setContentType(void 0);
1603
1603
  } else if ((contentType = headers.getContentType()) !== false) {
1604
- const [type, ...tokens] = contentType ? contentType.split(";").map((token2) => token2.trim()).filter(Boolean) : [];
1604
+ const [type, ...tokens] = contentType ? contentType.split(";").map((token) => token.trim()).filter(Boolean) : [];
1605
1605
  headers.setContentType([type || "multipart/form-data", ...tokens].join("; "));
1606
1606
  }
1607
1607
  }
@@ -1621,22 +1621,22 @@ var ExtractiaSDK = (() => {
1621
1621
  var isXHRAdapterSupported = typeof XMLHttpRequest !== "undefined";
1622
1622
  var xhr_default = isXHRAdapterSupported && function(config) {
1623
1623
  return new Promise(function dispatchXhrRequest(resolve, reject) {
1624
- const _config = resolveConfig_default(config);
1625
- let requestData = _config.data;
1626
- const requestHeaders = AxiosHeaders_default.from(_config.headers).normalize();
1627
- let { responseType, onUploadProgress, onDownloadProgress } = _config;
1624
+ const _config2 = resolveConfig_default(config);
1625
+ let requestData = _config2.data;
1626
+ const requestHeaders = AxiosHeaders_default.from(_config2.headers).normalize();
1627
+ let { responseType, onUploadProgress, onDownloadProgress } = _config2;
1628
1628
  let onCanceled;
1629
1629
  let uploadThrottled, downloadThrottled;
1630
1630
  let flushUpload, flushDownload;
1631
1631
  function done() {
1632
1632
  flushUpload && flushUpload();
1633
1633
  flushDownload && flushDownload();
1634
- _config.cancelToken && _config.cancelToken.unsubscribe(onCanceled);
1635
- _config.signal && _config.signal.removeEventListener("abort", onCanceled);
1634
+ _config2.cancelToken && _config2.cancelToken.unsubscribe(onCanceled);
1635
+ _config2.signal && _config2.signal.removeEventListener("abort", onCanceled);
1636
1636
  }
1637
1637
  let request = new XMLHttpRequest();
1638
- request.open(_config.method.toUpperCase(), _config.url, true);
1639
- request.timeout = _config.timeout;
1638
+ request.open(_config2.method.toUpperCase(), _config2.url, true);
1639
+ request.timeout = _config2.timeout;
1640
1640
  function onloadend() {
1641
1641
  if (!request) {
1642
1642
  return;
@@ -1687,10 +1687,10 @@ var ExtractiaSDK = (() => {
1687
1687
  request = null;
1688
1688
  };
1689
1689
  request.ontimeout = function handleTimeout() {
1690
- let timeoutErrorMessage = _config.timeout ? "timeout of " + _config.timeout + "ms exceeded" : "timeout exceeded";
1691
- const transitional2 = _config.transitional || transitional_default;
1692
- if (_config.timeoutErrorMessage) {
1693
- timeoutErrorMessage = _config.timeoutErrorMessage;
1690
+ let timeoutErrorMessage = _config2.timeout ? "timeout of " + _config2.timeout + "ms exceeded" : "timeout exceeded";
1691
+ const transitional2 = _config2.transitional || transitional_default;
1692
+ if (_config2.timeoutErrorMessage) {
1693
+ timeoutErrorMessage = _config2.timeoutErrorMessage;
1694
1694
  }
1695
1695
  reject(new AxiosError_default(
1696
1696
  timeoutErrorMessage,
@@ -1706,11 +1706,11 @@ var ExtractiaSDK = (() => {
1706
1706
  request.setRequestHeader(key, val);
1707
1707
  });
1708
1708
  }
1709
- if (!utils_default.isUndefined(_config.withCredentials)) {
1710
- request.withCredentials = !!_config.withCredentials;
1709
+ if (!utils_default.isUndefined(_config2.withCredentials)) {
1710
+ request.withCredentials = !!_config2.withCredentials;
1711
1711
  }
1712
1712
  if (responseType && responseType !== "json") {
1713
- request.responseType = _config.responseType;
1713
+ request.responseType = _config2.responseType;
1714
1714
  }
1715
1715
  if (onDownloadProgress) {
1716
1716
  [downloadThrottled, flushDownload] = progressEventReducer(onDownloadProgress, true);
@@ -1721,7 +1721,7 @@ var ExtractiaSDK = (() => {
1721
1721
  request.upload.addEventListener("progress", uploadThrottled);
1722
1722
  request.upload.addEventListener("loadend", flushUpload);
1723
1723
  }
1724
- if (_config.cancelToken || _config.signal) {
1724
+ if (_config2.cancelToken || _config2.signal) {
1725
1725
  onCanceled = (cancel) => {
1726
1726
  if (!request) {
1727
1727
  return;
@@ -1730,12 +1730,12 @@ var ExtractiaSDK = (() => {
1730
1730
  request.abort();
1731
1731
  request = null;
1732
1732
  };
1733
- _config.cancelToken && _config.cancelToken.subscribe(onCanceled);
1734
- if (_config.signal) {
1735
- _config.signal.aborted ? onCanceled() : _config.signal.addEventListener("abort", onCanceled);
1733
+ _config2.cancelToken && _config2.cancelToken.subscribe(onCanceled);
1734
+ if (_config2.signal) {
1735
+ _config2.signal.aborted ? onCanceled() : _config2.signal.addEventListener("abort", onCanceled);
1736
1736
  }
1737
1737
  }
1738
- const protocol = parseProtocol(_config.url);
1738
+ const protocol = parseProtocol(_config2.url);
1739
1739
  if (protocol && platform_default.protocols.indexOf(protocol) === -1) {
1740
1740
  reject(new AxiosError_default("Unsupported protocol " + protocol + ":", AxiosError_default.ERR_BAD_REQUEST, config));
1741
1741
  return;
@@ -2385,32 +2385,32 @@ var ExtractiaSDK = (() => {
2385
2385
  this.promise = new Promise(function promiseExecutor(resolve) {
2386
2386
  resolvePromise = resolve;
2387
2387
  });
2388
- const token2 = this;
2388
+ const token = this;
2389
2389
  this.promise.then((cancel) => {
2390
- if (!token2._listeners) return;
2391
- let i = token2._listeners.length;
2390
+ if (!token._listeners) return;
2391
+ let i = token._listeners.length;
2392
2392
  while (i-- > 0) {
2393
- token2._listeners[i](cancel);
2393
+ token._listeners[i](cancel);
2394
2394
  }
2395
- token2._listeners = null;
2395
+ token._listeners = null;
2396
2396
  });
2397
2397
  this.promise.then = (onfulfilled) => {
2398
2398
  let _resolve;
2399
2399
  const promise = new Promise((resolve) => {
2400
- token2.subscribe(resolve);
2400
+ token.subscribe(resolve);
2401
2401
  _resolve = resolve;
2402
2402
  }).then(onfulfilled);
2403
2403
  promise.cancel = function reject() {
2404
- token2.unsubscribe(_resolve);
2404
+ token.unsubscribe(_resolve);
2405
2405
  };
2406
2406
  return promise;
2407
2407
  };
2408
2408
  executor(function cancel(message, config, request) {
2409
- if (token2.reason) {
2409
+ if (token.reason) {
2410
2410
  return;
2411
2411
  }
2412
- token2.reason = new CanceledError_default(message, config, request);
2413
- resolvePromise(token2.reason);
2412
+ token.reason = new CanceledError_default(message, config, request);
2413
+ resolvePromise(token.reason);
2414
2414
  });
2415
2415
  }
2416
2416
  /**
@@ -2462,11 +2462,11 @@ var ExtractiaSDK = (() => {
2462
2462
  */
2463
2463
  static source() {
2464
2464
  let cancel;
2465
- const token2 = new _CancelToken(function executor(c) {
2465
+ const token = new _CancelToken(function executor(c) {
2466
2466
  cancel = c;
2467
2467
  });
2468
2468
  return {
2469
- token: token2,
2469
+ token,
2470
2470
  cancel
2471
2471
  };
2472
2472
  }
@@ -2610,93 +2610,347 @@ var ExtractiaSDK = (() => {
2610
2610
  } = axios_default;
2611
2611
 
2612
2612
  // src/errors.js
2613
+ var STATUS_MESSAGES = {
2614
+ 400: "The request contains invalid data. Please check your input.",
2615
+ 401: "Your API token is invalid or has expired. Please check your credentials.",
2616
+ 402: "Your current plan does not support this feature or your document quota is exhausted.",
2617
+ 403: "You do not have permission to perform this action.",
2618
+ 404: "The requested resource could not be found.",
2619
+ 409: "A resource with this identifier already exists.",
2620
+ 429: "Too many requests. Please wait a moment and try again.",
2621
+ 500: "The server encountered an unexpected error. Please try again in a moment.",
2622
+ 502: "The server received an unexpected response. Please try again.",
2623
+ 503: "The service is temporarily unavailable. Please try again in a few minutes.",
2624
+ 504: "The server timed out while processing the request. Please try again."
2625
+ };
2613
2626
  var ExtractiaError = class extends Error {
2614
- /** @param {string} message @param {number} status */
2615
- constructor(message, status) {
2627
+ /**
2628
+ * @param {string} message Technical detail string.
2629
+ * @param {number} [status] — HTTP status code (0 = no response).
2630
+ * @param {string} [userMessage] — Human-friendly sentence shown to end users.
2631
+ * @param {string} [code] — Machine-readable error code.
2632
+ */
2633
+ constructor(message, status = 0, userMessage = null, code = "SDK_ERROR") {
2616
2634
  super(message);
2617
2635
  this.name = "ExtractiaError";
2618
2636
  this.status = status;
2637
+ this.userMessage = userMessage != null ? userMessage : message;
2638
+ this.code = code;
2639
+ this.requestId = null;
2640
+ }
2641
+ /** Returns true if automatically retrying the same request may succeed. */
2642
+ isRetryable() {
2643
+ return this.status === 429 || this.status >= 500;
2644
+ }
2645
+ toJSON() {
2646
+ return {
2647
+ name: this.name,
2648
+ code: this.code,
2649
+ status: this.status,
2650
+ message: this.message,
2651
+ userMessage: this.userMessage,
2652
+ requestId: this.requestId
2653
+ };
2619
2654
  }
2620
2655
  };
2621
2656
  var AuthError = class extends ExtractiaError {
2622
- constructor(message = "Unauthorized. Check your API token.") {
2623
- super(message, 401);
2657
+ constructor(message = STATUS_MESSAGES[401]) {
2658
+ super(message, 401, STATUS_MESSAGES[401], "AUTH_ERROR");
2624
2659
  this.name = "AuthError";
2625
2660
  }
2626
2661
  };
2627
2662
  var ForbiddenError = class extends ExtractiaError {
2628
- constructor(message = "Forbidden. Insufficient permissions.") {
2629
- super(message, 403);
2663
+ constructor(message = STATUS_MESSAGES[403]) {
2664
+ super(message, 403, STATUS_MESSAGES[403], "FORBIDDEN");
2630
2665
  this.name = "ForbiddenError";
2631
2666
  }
2632
2667
  };
2633
2668
  var TierError = class extends ExtractiaError {
2634
- constructor(message = "Tier limit reached. Upgrade your plan.", status = 402) {
2635
- super(message, status);
2669
+ constructor(message = STATUS_MESSAGES[402], status = 402) {
2670
+ super(message, status, STATUS_MESSAGES[402], "TIER_LIMIT");
2636
2671
  this.name = "TierError";
2637
2672
  }
2638
2673
  };
2674
+ var QuotaError = class extends ExtractiaError {
2675
+ constructor(message = "You have reached your document processing quota for this billing period. Please upgrade or wait for the next cycle.") {
2676
+ super(message, 402, message, "QUOTA_EXCEEDED");
2677
+ this.name = "QuotaError";
2678
+ }
2679
+ };
2639
2680
  var RateLimitError = class extends ExtractiaError {
2640
- constructor(message = "Too many requests. Please slow down.") {
2641
- super(message, 429);
2681
+ /**
2682
+ * @param {string} [message]
2683
+ * @param {number|null} [retryAfter] — Seconds to wait before retrying (from Retry-After header).
2684
+ */
2685
+ constructor(message = STATUS_MESSAGES[429], retryAfter = null) {
2686
+ super(message, 429, STATUS_MESSAGES[429], "RATE_LIMITED");
2642
2687
  this.name = "RateLimitError";
2688
+ this.retryAfter = retryAfter;
2689
+ }
2690
+ isRetryable() {
2691
+ return true;
2643
2692
  }
2644
2693
  };
2645
2694
  var NotFoundError = class extends ExtractiaError {
2646
- constructor(message = "Resource not found.") {
2647
- super(message, 404);
2695
+ constructor(message = STATUS_MESSAGES[404]) {
2696
+ super(message, 404, STATUS_MESSAGES[404], "NOT_FOUND");
2648
2697
  this.name = "NotFoundError";
2649
2698
  }
2650
2699
  };
2700
+ var ValidationError = class extends ExtractiaError {
2701
+ /**
2702
+ * @param {string} [message]
2703
+ * @param {Record<string,string>|null} [fields] — Field-level errors, if the server provides them.
2704
+ */
2705
+ constructor(message = STATUS_MESSAGES[400], fields = null) {
2706
+ super(message, 400, STATUS_MESSAGES[400], "VALIDATION_ERROR");
2707
+ this.name = "ValidationError";
2708
+ this.fields = fields;
2709
+ }
2710
+ };
2711
+ var ConflictError = class extends ExtractiaError {
2712
+ constructor(message = STATUS_MESSAGES[409]) {
2713
+ super(message, 409, STATUS_MESSAGES[409], "CONFLICT");
2714
+ this.name = "ConflictError";
2715
+ }
2716
+ };
2717
+ var ServerError = class extends ExtractiaError {
2718
+ constructor(message = STATUS_MESSAGES[500], status = 500) {
2719
+ var _a;
2720
+ super(
2721
+ message,
2722
+ status,
2723
+ (_a = STATUS_MESSAGES[status]) != null ? _a : STATUS_MESSAGES[500],
2724
+ "SERVER_ERROR"
2725
+ );
2726
+ this.name = "ServerError";
2727
+ }
2728
+ isRetryable() {
2729
+ return true;
2730
+ }
2731
+ };
2732
+ var NetworkError = class extends ExtractiaError {
2733
+ constructor(message = "Unable to reach the Extractia API. Please check your network connection and try again.") {
2734
+ super(message, 0, message, "NETWORK_ERROR");
2735
+ this.name = "NetworkError";
2736
+ }
2737
+ isRetryable() {
2738
+ return true;
2739
+ }
2740
+ };
2741
+ var TimeoutError = class extends ExtractiaError {
2742
+ constructor(message = "The request timed out. The server may be under heavy load \u2014 please try again in a moment.") {
2743
+ super(message, 0, message, "TIMEOUT");
2744
+ this.name = "TimeoutError";
2745
+ }
2746
+ isRetryable() {
2747
+ return true;
2748
+ }
2749
+ };
2750
+ function extractServerDetail(data) {
2751
+ var _a, _b, _c, _d, _e;
2752
+ if (!data) return null;
2753
+ if (typeof data === "string" && data.trim()) return data.trim();
2754
+ if (typeof data === "object") {
2755
+ const msg = (_c = (_b = (_a = data.message) != null ? _a : data.error) != null ? _b : data.detail) != null ? _c : data.title;
2756
+ if (msg && typeof msg === "string") return msg.trim();
2757
+ if (Array.isArray(data.errors) && data.errors.length > 0) {
2758
+ const first = data.errors[0];
2759
+ return typeof first === "string" ? first : (_e = (_d = first.message) != null ? _d : first.msg) != null ? _e : JSON.stringify(first);
2760
+ }
2761
+ if (Array.isArray(data.fieldErrors) && data.fieldErrors.length > 0) {
2762
+ return data.fieldErrors.map((e) => {
2763
+ var _a2, _b2;
2764
+ return `${(_a2 = e.field) != null ? _a2 : "field"}: ${(_b2 = e.message) != null ? _b2 : e.defaultMessage}`;
2765
+ }).join("; ");
2766
+ }
2767
+ }
2768
+ return null;
2769
+ }
2651
2770
  function mapAxiosError(err) {
2652
- var _a, _b;
2653
- const status = (_a = err.response) == null ? void 0 : _a.status;
2654
- const serverMessage = (_b = err.response) == null ? void 0 : _b.data;
2655
- const detail = typeof serverMessage === "string" ? serverMessage : void 0;
2771
+ var _a, _b, _c, _d, _e, _f, _g, _h;
2772
+ if (!err.response) {
2773
+ if (err.code === "ECONNABORTED" || err.code === "ETIMEDOUT" || err.message && err.message.toLowerCase().includes("timeout")) {
2774
+ return new TimeoutError();
2775
+ }
2776
+ return new NetworkError(err.message || void 0);
2777
+ }
2778
+ const status = err.response.status;
2779
+ const detail = extractServerDetail(err.response.data);
2780
+ const userMessage = (_a = STATUS_MESSAGES[status]) != null ? _a : status >= 500 ? STATUS_MESSAGES[500] : "Something went wrong. Please try again.";
2781
+ let error;
2656
2782
  switch (status) {
2783
+ case 400: {
2784
+ const body = err.response.data;
2785
+ const fields = (_b = body == null ? void 0 : body.fields) != null ? _b : Array.isArray(body == null ? void 0 : body.fieldErrors) ? Object.fromEntries(
2786
+ body.fieldErrors.map((f) => {
2787
+ var _a2;
2788
+ return [f.field, (_a2 = f.message) != null ? _a2 : f.defaultMessage];
2789
+ })
2790
+ ) : null;
2791
+ error = new ValidationError(detail != null ? detail : STATUS_MESSAGES[400], fields);
2792
+ break;
2793
+ }
2657
2794
  case 401:
2658
- return new AuthError(detail);
2795
+ error = new AuthError(detail != null ? detail : void 0);
2796
+ break;
2659
2797
  case 402:
2660
- return new TierError(detail);
2798
+ if (detail && (detail.toLowerCase().includes("quota") || detail.toLowerCase().includes("document") || detail.toLowerCase().includes("limit reached"))) {
2799
+ error = new QuotaError(detail);
2800
+ } else {
2801
+ error = new TierError(detail != null ? detail : void 0);
2802
+ }
2803
+ break;
2661
2804
  case 403:
2662
- return new ForbiddenError(detail);
2805
+ error = new ForbiddenError(detail != null ? detail : void 0);
2806
+ break;
2663
2807
  case 404:
2664
- return new NotFoundError(detail);
2665
- case 429:
2666
- return new RateLimitError(detail);
2808
+ error = new NotFoundError(detail != null ? detail : void 0);
2809
+ break;
2810
+ case 409:
2811
+ error = new ConflictError(detail != null ? detail : void 0);
2812
+ break;
2813
+ case 429: {
2814
+ const retryAfterHeader = (_c = err.response.headers) == null ? void 0 : _c["retry-after"];
2815
+ const retryAfter = retryAfterHeader != null ? parseInt(retryAfterHeader, 10) : null;
2816
+ error = new RateLimitError(detail != null ? detail : void 0, isNaN(retryAfter) ? null : retryAfter);
2817
+ break;
2818
+ }
2667
2819
  default:
2668
- return new ExtractiaError(detail != null ? detail : err.message, status != null ? status : 0);
2820
+ if (status >= 500) {
2821
+ error = new ServerError((_d = detail != null ? detail : STATUS_MESSAGES[status]) != null ? _d : STATUS_MESSAGES[500], status);
2822
+ } else {
2823
+ error = new ExtractiaError(
2824
+ detail != null ? detail : err.message,
2825
+ status,
2826
+ userMessage,
2827
+ `HTTP_${status}`
2828
+ );
2829
+ }
2669
2830
  }
2831
+ error.userMessage = userMessage;
2832
+ const reqId = (_h = (_g = (_e = err.response.headers) == null ? void 0 : _e["x-request-id"]) != null ? _g : (_f = err.response.headers) == null ? void 0 : _f["x-correlation-id"]) != null ? _h : null;
2833
+ if (reqId) error.requestId = reqId;
2834
+ return error;
2670
2835
  }
2671
2836
 
2672
2837
  // src/apiClient.js
2673
- var token = null;
2674
- var DEFAULT_BASE_URL = "https://api.extractia.info/api/public";
2675
- var api = axios_default.create({
2676
- baseURL: DEFAULT_BASE_URL,
2677
- timeout: 6e4
2678
- // 60s — AI processing can take 10–30s
2838
+ var _token = null;
2839
+ var _config = {
2840
+ baseURL: "https://api.extractia.info/api/public",
2841
+ timeout: 6e4,
2842
+ retries: 1,
2843
+ retryDelay: 1e3,
2844
+ debug: false,
2845
+ defaultHeaders: {},
2846
+ onBeforeRequest: null,
2847
+ onAfterResponse: null,
2848
+ onError: null
2849
+ };
2850
+ var _api = axios_default.create({
2851
+ baseURL: _config.baseURL,
2852
+ timeout: _config.timeout
2679
2853
  });
2680
- api.interceptors.request.use((config) => {
2681
- if (!token) {
2682
- throw new Error(
2683
- "API token is required. Call setToken(yourApiToken) before making requests."
2854
+ _api.interceptors.request.use((config) => {
2855
+ var _a, _b, _c, _d, _e;
2856
+ if (!_token) {
2857
+ const err = new ExtractiaError(
2858
+ "API token not set. Call setToken(token) before making requests.",
2859
+ 0,
2860
+ "API token not set. Call setToken(token) before making requests.",
2861
+ "TOKEN_MISSING"
2684
2862
  );
2863
+ return Promise.reject(err);
2864
+ }
2865
+ config.headers = (_a = config.headers) != null ? _a : {};
2866
+ config.headers["Authorization"] = `Bearer ${_token}`;
2867
+ Object.assign(config.headers, _config.defaultHeaders);
2868
+ if (_config.debug) {
2869
+ console.debug(
2870
+ `[ExtractIA SDK] \u2192 ${((_b = config.method) != null ? _b : "GET").toUpperCase()} ${(_c = config.baseURL) != null ? _c : ""}${(_d = config.url) != null ? _d : ""}`,
2871
+ (_e = config.params) != null ? _e : ""
2872
+ );
2873
+ }
2874
+ if (typeof _config.onBeforeRequest === "function") {
2875
+ const modified = _config.onBeforeRequest(config);
2876
+ return modified != null ? modified : config;
2685
2877
  }
2686
- config.headers.Authorization = `Bearer ${token}`;
2687
2878
  return config;
2688
2879
  });
2689
- api.interceptors.response.use(
2690
- (response) => response,
2691
- (err) => Promise.reject(mapAxiosError(err))
2880
+ _api.interceptors.response.use(
2881
+ (response) => {
2882
+ var _a, _b;
2883
+ if (_config.debug) {
2884
+ console.debug(
2885
+ `[ExtractIA SDK] \u2190 ${response.status} ${(_b = (_a = response.config) == null ? void 0 : _a.url) != null ? _b : ""}`
2886
+ );
2887
+ }
2888
+ if (typeof _config.onAfterResponse === "function") {
2889
+ _config.onAfterResponse(response);
2890
+ }
2891
+ return response;
2892
+ },
2893
+ async (err) => {
2894
+ var _a, _b;
2895
+ if (err instanceof ExtractiaError) {
2896
+ if (typeof _config.onError === "function") _config.onError(err);
2897
+ return Promise.reject(err);
2898
+ }
2899
+ const mapped = mapAxiosError(err);
2900
+ const cfg = err.config;
2901
+ if (cfg && mapped.isRetryable() && ((_a = cfg._retryCount) != null ? _a : 0) < _config.retries) {
2902
+ cfg._retryCount = ((_b = cfg._retryCount) != null ? _b : 0) + 1;
2903
+ const delayMs = mapped.retryAfter != null ? mapped.retryAfter * 1e3 : _config.retryDelay * cfg._retryCount;
2904
+ if (_config.debug) {
2905
+ console.debug(
2906
+ `[ExtractIA SDK] retrying (${cfg._retryCount}/${_config.retries}) in ${delayMs}ms\u2026`
2907
+ );
2908
+ }
2909
+ await new Promise((r) => setTimeout(r, delayMs));
2910
+ return _api(cfg);
2911
+ }
2912
+ if (typeof _config.onError === "function") _config.onError(mapped);
2913
+ return Promise.reject(mapped);
2914
+ }
2692
2915
  );
2693
- function setToken(newToken) {
2694
- token = newToken;
2916
+ function setToken(token) {
2917
+ if (!token || typeof token !== "string" || !token.trim()) {
2918
+ throw new Error("setToken: token must be a non-empty string.");
2919
+ }
2920
+ _token = token.trim();
2921
+ }
2922
+ function getToken() {
2923
+ return _token;
2924
+ }
2925
+ function hasToken() {
2926
+ return Boolean(_token);
2695
2927
  }
2696
- function configure({ baseURL } = {}) {
2697
- if (baseURL) api.defaults.baseURL = baseURL;
2928
+ function clearToken() {
2929
+ _token = null;
2930
+ }
2931
+ function configure(opts = {}) {
2932
+ if (opts.baseURL) {
2933
+ _config.baseURL = opts.baseURL;
2934
+ _api.defaults.baseURL = opts.baseURL;
2935
+ }
2936
+ if (opts.timeout != null) {
2937
+ _config.timeout = opts.timeout;
2938
+ _api.defaults.timeout = opts.timeout;
2939
+ }
2940
+ if (opts.retries != null) _config.retries = opts.retries;
2941
+ if (opts.retryDelay != null) _config.retryDelay = opts.retryDelay;
2942
+ if (opts.debug != null) _config.debug = Boolean(opts.debug);
2943
+ if (opts.defaultHeaders) {
2944
+ _config.defaultHeaders = __spreadValues(__spreadValues({}, _config.defaultHeaders), opts.defaultHeaders);
2945
+ }
2946
+ if (opts.onBeforeRequest) _config.onBeforeRequest = opts.onBeforeRequest;
2947
+ if (opts.onAfterResponse) _config.onAfterResponse = opts.onAfterResponse;
2948
+ if (opts.onError) _config.onError = opts.onError;
2698
2949
  }
2699
- var apiClient_default = api;
2950
+ function getConfig() {
2951
+ return __spreadValues({}, _config);
2952
+ }
2953
+ var apiClient_default = _api;
2700
2954
 
2701
2955
  // src/auth.js
2702
2956
  async function getMyProfile() {
@@ -2912,8 +3166,188 @@ var ExtractiaSDK = (() => {
2912
3166
  return res.data;
2913
3167
  }
2914
3168
 
3169
+ // src/subusers.js
3170
+ var subusers_exports = {};
3171
+ __export(subusers_exports, {
3172
+ createSubUser: () => createSubUser,
3173
+ deleteSubUser: () => deleteSubUser,
3174
+ getSubUsers: () => getSubUsers,
3175
+ toggleSuspendSubUser: () => toggleSuspendSubUser,
3176
+ updateSubUser: () => updateSubUser
3177
+ });
3178
+ async function getSubUsers() {
3179
+ const res = await apiClient_default.get("/me/subusers");
3180
+ return res.data;
3181
+ }
3182
+ async function createSubUser({
3183
+ username,
3184
+ password,
3185
+ permissions,
3186
+ allowedFormIds
3187
+ }) {
3188
+ const res = await apiClient_default.post("/me/subusers", __spreadValues({
3189
+ username,
3190
+ password,
3191
+ permissions
3192
+ }, allowedFormIds !== void 0 ? { allowedFormIds } : {}));
3193
+ return res.data;
3194
+ }
3195
+ async function deleteSubUser(username) {
3196
+ const res = await apiClient_default.delete(`/me/subusers/${encodeURIComponent(username)}`);
3197
+ return res.data;
3198
+ }
3199
+ async function updateSubUser(username, updates = {}) {
3200
+ const res = await apiClient_default.put(
3201
+ `/me/subusers/${encodeURIComponent(username)}`,
3202
+ updates
3203
+ );
3204
+ return res.data;
3205
+ }
3206
+ async function toggleSuspendSubUser(username) {
3207
+ const res = await apiClient_default.put(
3208
+ `/me/subusers/${encodeURIComponent(username)}/suspend`
3209
+ );
3210
+ return res.data;
3211
+ }
3212
+
3213
+ // src/utils.js
3214
+ var utils_exports2 = {};
3215
+ __export(utils_exports2, {
3216
+ delay: () => delay,
3217
+ ensureBase64: () => ensureBase64,
3218
+ fileToBase64: () => fileToBase64,
3219
+ getMimeType: () => getMimeType,
3220
+ isBase64: () => isBase64,
3221
+ paginate: () => paginate,
3222
+ paginateAll: () => paginateAll,
3223
+ stripDataUrlPrefix: () => stripDataUrlPrefix,
3224
+ withRetry: () => withRetry
3225
+ });
3226
+ function fileToBase64(file) {
3227
+ if (typeof FileReader === "undefined") {
3228
+ return Promise.reject(
3229
+ new Error(
3230
+ "fileToBase64: FileReader is not available in this environment. In Node.js, read the file manually and encode it with Buffer.from(data).toString('base64')."
3231
+ )
3232
+ );
3233
+ }
3234
+ return new Promise((resolve, reject) => {
3235
+ const reader = new FileReader();
3236
+ reader.onload = () => resolve(
3237
+ /** @type {string} */
3238
+ reader.result
3239
+ );
3240
+ reader.onerror = () => {
3241
+ var _a;
3242
+ return reject(new Error(`fileToBase64: failed to read file "${(_a = file.name) != null ? _a : "unknown"}".`));
3243
+ };
3244
+ reader.readAsDataURL(file);
3245
+ });
3246
+ }
3247
+ function stripDataUrlPrefix(base64OrDataUrl) {
3248
+ if (typeof base64OrDataUrl !== "string") {
3249
+ throw new TypeError(
3250
+ "stripDataUrlPrefix: argument must be a string."
3251
+ );
3252
+ }
3253
+ const idx = base64OrDataUrl.indexOf(";base64,");
3254
+ return idx !== -1 ? base64OrDataUrl.slice(idx + 8) : base64OrDataUrl;
3255
+ }
3256
+ function getMimeType(dataUrl) {
3257
+ if (typeof dataUrl !== "string") return null;
3258
+ const match = dataUrl.match(/^data:([^;]+);base64,/);
3259
+ return match ? match[1] : null;
3260
+ }
3261
+ function isBase64(str) {
3262
+ if (typeof str !== "string" || !str.trim()) return false;
3263
+ const raw = stripDataUrlPrefix(str);
3264
+ return raw.length > 0 && raw.length % 4 === 0 && /^[A-Za-z0-9+/]*={0,2}$/.test(raw);
3265
+ }
3266
+ async function ensureBase64(fileOrBase64) {
3267
+ if (typeof fileOrBase64 === "string") return fileOrBase64;
3268
+ if (typeof File !== "undefined" && fileOrBase64 instanceof File || typeof Blob !== "undefined" && fileOrBase64 instanceof Blob) {
3269
+ return fileToBase64(fileOrBase64);
3270
+ }
3271
+ throw new TypeError(
3272
+ "ensureBase64: argument must be a File, Blob, or a base64 string."
3273
+ );
3274
+ }
3275
+ function paginate(_0) {
3276
+ return __asyncGenerator(this, arguments, function* (fn, { size = 50, startPage = 0, maxPages = 1e3 } = {}) {
3277
+ var _a;
3278
+ let page = startPage;
3279
+ let pagesRead = 0;
3280
+ while (pagesRead < maxPages) {
3281
+ const result = yield new __await(fn({ page, size }));
3282
+ const items = Array.isArray(result == null ? void 0 : result.content) ? result.content : [];
3283
+ for (const item of items) yield item;
3284
+ const totalPages = (_a = result == null ? void 0 : result.totalPages) != null ? _a : 1;
3285
+ if (items.length === 0 || page + 1 >= totalPages) break;
3286
+ page++;
3287
+ pagesRead++;
3288
+ }
3289
+ });
3290
+ }
3291
+ async function paginateAll(fn, opts) {
3292
+ const items = [];
3293
+ try {
3294
+ for (var iter = __forAwait(paginate(fn, opts)), more, temp, error; more = !(temp = await iter.next()).done; more = false) {
3295
+ const item = temp.value;
3296
+ items.push(item);
3297
+ }
3298
+ } catch (temp) {
3299
+ error = [temp];
3300
+ } finally {
3301
+ try {
3302
+ more && (temp = iter.return) && await temp.call(iter);
3303
+ } finally {
3304
+ if (error)
3305
+ throw error[0];
3306
+ }
3307
+ }
3308
+ return items;
3309
+ }
3310
+ function delay(ms) {
3311
+ return new Promise((resolve) => setTimeout(resolve, ms));
3312
+ }
3313
+ async function withRetry(fn, { retries = 3, initialDelay = 500, shouldRetry } = {}) {
3314
+ const isRetryable = shouldRetry != null ? shouldRetry : (err) => typeof err.isRetryable === "function" && err.isRetryable();
3315
+ let lastErr;
3316
+ for (let attempt = 0; attempt <= retries; attempt++) {
3317
+ try {
3318
+ return await fn();
3319
+ } catch (err) {
3320
+ lastErr = err;
3321
+ if (attempt < retries && isRetryable(err)) {
3322
+ const wait = err.retryAfter != null ? err.retryAfter * 1e3 : initialDelay * 2 ** attempt;
3323
+ await delay(wait);
3324
+ continue;
3325
+ }
3326
+ throw err;
3327
+ }
3328
+ }
3329
+ throw lastErr;
3330
+ }
3331
+
2915
3332
  // src/browser-entry.js
2916
- var extractia = __spreadValues(__spreadValues(__spreadValues(__spreadValues(__spreadValues({}, auth_exports), templates_exports), documents_exports), analytics_exports), ocrTools_exports);
3333
+ var extractia = __spreadProps(__spreadValues(__spreadValues(__spreadValues(__spreadValues(__spreadValues(__spreadValues(__spreadValues({}, auth_exports), templates_exports), documents_exports), analytics_exports), ocrTools_exports), subusers_exports), utils_exports2), {
3334
+ getToken,
3335
+ hasToken,
3336
+ clearToken,
3337
+ getConfig,
3338
+ ExtractiaError,
3339
+ AuthError,
3340
+ ForbiddenError,
3341
+ TierError,
3342
+ QuotaError,
3343
+ RateLimitError,
3344
+ NotFoundError,
3345
+ ValidationError,
3346
+ ConflictError,
3347
+ ServerError,
3348
+ NetworkError,
3349
+ TimeoutError
3350
+ });
2917
3351
  var browser_entry_default = extractia;
2918
3352
  return __toCommonJS(browser_entry_exports);
2919
3353
  })();