extractia-sdk 1.4.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/README.md +163 -6
- package/dist/extractia-sdk.browser.js +29 -7
- package/dist/extractia-sdk.cjs.js +30 -7
- package/dist/extractia-sdk.esm.js +30 -7
- package/dist/index.d.ts +82 -7
- package/package.json +3 -2
- package/src/apiClient.js +4 -1
- package/src/errors.js +17 -14
- package/src/index.d.ts +82 -7
- package/src/index.js +1 -6
- package/src/ocrTools.js +48 -0
- package/src/subusers.js +1 -1
- package/src/utils.js +9 -5
- package/vitest.integration.config.js +27 -0
package/README.md
CHANGED
|
@@ -22,6 +22,7 @@ Works in Node.js ≥ 18 and modern browsers via the provided UMD build.
|
|
|
22
22
|
- [AI Features](#ai-features)
|
|
23
23
|
- [Exports](#exports)
|
|
24
24
|
- [OCR Tools](#ocr-tools)
|
|
25
|
+
- [Execution History](#getocrtoolexecutionsoptions)
|
|
25
26
|
- [Credits & Analytics](#credits--analytics)
|
|
26
27
|
- [Sub-Users](#sub-users)
|
|
27
28
|
6. [TypeScript](#typescript)
|
|
@@ -796,6 +797,74 @@ if (docType === "invoice") {
|
|
|
796
797
|
|
|
797
798
|
---
|
|
798
799
|
|
|
800
|
+
#### `getOcrToolExecutions(options?)`
|
|
801
|
+
|
|
802
|
+
Returns a paginated list of OCR tool execution history for the authenticated user.
|
|
803
|
+
Each record includes the original image (`imageBase64`), the AI answer, explanation, and run metadata.
|
|
804
|
+
|
|
805
|
+
> **Image data is included in every record.** Keep page sizes small when rendering thumbnails to avoid large payloads.
|
|
806
|
+
|
|
807
|
+
| Option | Type | Default | Description |
|
|
808
|
+
| -------- | -------- | ------- | -------------------------------------------------------- |
|
|
809
|
+
| `toolId` | `string` | — | Filter by tool ID. Omit to return executions for all tools |
|
|
810
|
+
| `page` | `number` | `0` | Zero-based page index |
|
|
811
|
+
| `size` | `number` | `20` | Page size — values above 50 are capped server-side |
|
|
812
|
+
|
|
813
|
+
```js
|
|
814
|
+
// All executions (most recent first)
|
|
815
|
+
const history = await getOcrToolExecutions();
|
|
816
|
+
console.log(`${history.totalElements} total executions`);
|
|
817
|
+
|
|
818
|
+
for (const exec of history.content) {
|
|
819
|
+
console.log(exec.toolName, exec.answer, exec.status); // "Proof of Residence" "YES" "SUCCESS"
|
|
820
|
+
// exec.imageBase64 — the original image that was analyzed
|
|
821
|
+
// exec.createdAt — ISO 8601 UTC timestamp
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
// Filter by a specific tool
|
|
825
|
+
const residenceHistory = await getOcrToolExecutions({
|
|
826
|
+
toolId: "tool_residence_check",
|
|
827
|
+
page: 0,
|
|
828
|
+
size: 10,
|
|
829
|
+
});
|
|
830
|
+
|
|
831
|
+
// Check for failed executions
|
|
832
|
+
const failures = history.content.filter((e) => e.status === "FAILURE");
|
|
833
|
+
failures.forEach((e) => console.error(e.toolName, e.errorMessage));
|
|
834
|
+
```
|
|
835
|
+
|
|
836
|
+
**Response shape:**
|
|
837
|
+
|
|
838
|
+
```json
|
|
839
|
+
{
|
|
840
|
+
"content": [
|
|
841
|
+
{
|
|
842
|
+
"id": "exec_abc123",
|
|
843
|
+
"toolId": "tool_residence_check",
|
|
844
|
+
"toolName": "Proof of Residence",
|
|
845
|
+
"imageBase64": "...",
|
|
846
|
+
"mimeType": "image/jpeg",
|
|
847
|
+
"paramValues": null,
|
|
848
|
+
"answer": "YES",
|
|
849
|
+
"explanation": "Utility bill dated within 3 months.",
|
|
850
|
+
"booleanAnswer": true,
|
|
851
|
+
"status": "SUCCESS",
|
|
852
|
+
"processingTimeMs": 1240,
|
|
853
|
+
"createdAt": "2026-04-13T10:00:00Z"
|
|
854
|
+
}
|
|
855
|
+
],
|
|
856
|
+
"totalElements": 42,
|
|
857
|
+
"totalPages": 3,
|
|
858
|
+
"size": 20,
|
|
859
|
+
"number": 0,
|
|
860
|
+
"first": true,
|
|
861
|
+
"last": false,
|
|
862
|
+
"empty": false
|
|
863
|
+
}
|
|
864
|
+
```
|
|
865
|
+
|
|
866
|
+
---
|
|
867
|
+
|
|
799
868
|
### Credits & Analytics
|
|
800
869
|
|
|
801
870
|
#### `getCreditsBalance()`
|
|
@@ -833,8 +902,25 @@ history.content.forEach((entry) => {
|
|
|
833
902
|
|
|
834
903
|
> Requires a **Pro or higher** plan. The plan determines the maximum number of sub-users allowed.
|
|
835
904
|
|
|
836
|
-
Sub-users can log in to the web app and operate within the permissions you grant them.
|
|
837
|
-
|
|
905
|
+
Sub-users can log in to the web app and operate within the permissions you grant them.
|
|
906
|
+
|
|
907
|
+
**Available permissions:**
|
|
908
|
+
|
|
909
|
+
| Permission | What it grants |
|
|
910
|
+
| -------------- | ---------------------------------------------------- |
|
|
911
|
+
| `"upload"` | Upload and process new documents |
|
|
912
|
+
| `"view"` | View all documents owned by the account |
|
|
913
|
+
| `"template"` | Create and edit form templates |
|
|
914
|
+
| `"settings"` | View and edit account settings |
|
|
915
|
+
| `"export"` | Export documents to CSV / Excel / JSON |
|
|
916
|
+
| `"api"` | Use the public API with their own API key |
|
|
917
|
+
| `"ocr_tools"` | Access the AI Agents / OCR Tools feature |
|
|
918
|
+
| `"gallery"` | Browse and clone templates from the public gallery |
|
|
919
|
+
| `"smart_scan"` | Use the Smart Scan camera capture flow |
|
|
920
|
+
| `"ia_agent"` | Use the IA Agent for intelligent document extraction |
|
|
921
|
+
| `"assistant"` | Use the built-in AI chatbot assistant |
|
|
922
|
+
|
|
923
|
+
**`allowedFormIds`** (optional): restrict which form templates the sub-user can access. Pass `null` or omit to grant access to all templates.
|
|
838
924
|
|
|
839
925
|
### Document History
|
|
840
926
|
|
|
@@ -862,13 +948,29 @@ const users = await getSubUsers();
|
|
|
862
948
|
```js
|
|
863
949
|
import { createSubUser } from "extractia-sdk";
|
|
864
950
|
|
|
951
|
+
// Basic — access to all templates
|
|
865
952
|
const sub = await createSubUser({
|
|
866
953
|
username: "agent_carlos",
|
|
867
954
|
password: "SecurePass1",
|
|
955
|
+
permissions: ["upload", "view", "ocr_tools"],
|
|
956
|
+
});
|
|
957
|
+
|
|
958
|
+
// Restricted to specific templates only
|
|
959
|
+
const restricted = await createSubUser({
|
|
960
|
+
username: "agent_maria",
|
|
961
|
+
password: "SecurePass2",
|
|
868
962
|
permissions: ["upload", "view"],
|
|
963
|
+
allowedFormIds: ["tpl_invoices", "tpl_receipts"], // null = all templates
|
|
869
964
|
});
|
|
870
965
|
```
|
|
871
966
|
|
|
967
|
+
| Parameter | Type | Required | Description |
|
|
968
|
+
| ---------------- | ---------- | -------- | --------------------------------------------------- |
|
|
969
|
+
| `username` | `string` | ✅ | Must not be an existing account email |
|
|
970
|
+
| `password` | `string` | ✅ | Must differ from the owner's password |
|
|
971
|
+
| `permissions` | `string[]` | ✅ | See permissions table above |
|
|
972
|
+
| `allowedFormIds` | `string[]` | — | Template IDs accessible to this sub-user (null = all) |
|
|
973
|
+
|
|
872
974
|
**Error codes:**
|
|
873
975
|
| Code | Reason |
|
|
874
976
|
|------|--------|
|
|
@@ -876,16 +978,31 @@ const sub = await createSubUser({
|
|
|
876
978
|
| `409` | Username already in use |
|
|
877
979
|
| `400` | Missing fields or password matches the main account |
|
|
878
980
|
|
|
879
|
-
### Update Permissions or Password
|
|
981
|
+
### Update Permissions, Template Access, or Password
|
|
880
982
|
|
|
881
|
-
Only the fields you include are changed. Omit
|
|
983
|
+
Only the fields you include are changed. Omit any field to leave it unchanged.
|
|
882
984
|
|
|
883
985
|
```js
|
|
884
986
|
import { updateSubUser } from "extractia-sdk";
|
|
885
987
|
|
|
988
|
+
// Change permissions
|
|
886
989
|
await updateSubUser("agent_carlos", {
|
|
887
|
-
permissions: ["upload", "view", "export"],
|
|
888
|
-
|
|
990
|
+
permissions: ["upload", "view", "export", "ocr_tools"],
|
|
991
|
+
});
|
|
992
|
+
|
|
993
|
+
// Restrict to specific templates
|
|
994
|
+
await updateSubUser("agent_carlos", {
|
|
995
|
+
allowedFormIds: ["tpl_invoices"],
|
|
996
|
+
});
|
|
997
|
+
|
|
998
|
+
// Remove template restriction (grant access to all)
|
|
999
|
+
await updateSubUser("agent_carlos", {
|
|
1000
|
+
allowedFormIds: null,
|
|
1001
|
+
});
|
|
1002
|
+
|
|
1003
|
+
// Change password only
|
|
1004
|
+
await updateSubUser("agent_carlos", {
|
|
1005
|
+
password: "NewPass99",
|
|
889
1006
|
});
|
|
890
1007
|
```
|
|
891
1008
|
|
|
@@ -914,15 +1031,36 @@ console.log(state.suspended); // true | false
|
|
|
914
1031
|
|
|
915
1032
|
The SDK ships with a full `index.d.ts` declaration file — no `@types` package needed.
|
|
916
1033
|
|
|
1034
|
+
**Key exported types:**
|
|
1035
|
+
|
|
1036
|
+
| Type | Description |
|
|
1037
|
+
| --------------------- | -------------------------------------------------------- |
|
|
1038
|
+
| `FormTemplate` | A template with `id`, `label`, `fields`, `userId` |
|
|
1039
|
+
| `FormField` | A single field: `label`, `type`, `required`, `listLabel` |
|
|
1040
|
+
| `UserDocument` | A processed document with `rawJson`, `createdAt`, etc. |
|
|
1041
|
+
| `OcrToolConfig` | An OCR agent configuration |
|
|
1042
|
+
| `OcrRunResult` | Result of `runOcrTool`: `answer` + `explanation` |
|
|
1043
|
+
| `OcrToolExecution` | A single execution record from `getOcrToolExecutions` |
|
|
1044
|
+
| `OcrExecutionPage` | Paginated response of `OcrToolExecution` records |
|
|
1045
|
+
| `SubUser` | Sub-user with `username`, `permissions`, `allowedFormIds`, `suspended` |
|
|
1046
|
+
| `AppUserProfile` | User profile with quota, plan, and settings fields |
|
|
1047
|
+
| `DocumentAuditEntry` | An entry from `getDocumentHistory` |
|
|
1048
|
+
| `ExtractiaError` | Base error class with `status`, `code`, `userMessage` |
|
|
1049
|
+
|
|
917
1050
|
```ts
|
|
918
1051
|
import {
|
|
919
1052
|
setToken,
|
|
920
1053
|
processImage,
|
|
921
1054
|
runOcrTool,
|
|
1055
|
+
getOcrToolExecutions,
|
|
1056
|
+
createSubUser,
|
|
922
1057
|
suggestFields,
|
|
923
1058
|
exportDocumentsJson,
|
|
924
1059
|
UserDocument,
|
|
925
1060
|
OcrRunResult,
|
|
1061
|
+
OcrToolExecution,
|
|
1062
|
+
OcrExecutionPage,
|
|
1063
|
+
SubUser,
|
|
926
1064
|
FormField,
|
|
927
1065
|
TierError,
|
|
928
1066
|
RateLimitError,
|
|
@@ -948,6 +1086,18 @@ async function classifyAndExtract(
|
|
|
948
1086
|
// Extract with template
|
|
949
1087
|
return processImage(templateId, base64);
|
|
950
1088
|
}
|
|
1089
|
+
|
|
1090
|
+
// Typed execution history
|
|
1091
|
+
const page: OcrExecutionPage = await getOcrToolExecutions({ size: 5 });
|
|
1092
|
+
const recent: OcrToolExecution[] = page.content;
|
|
1093
|
+
|
|
1094
|
+
// Typed sub-user with allowedFormIds
|
|
1095
|
+
const sub: SubUser = await createSubUser({
|
|
1096
|
+
username: "agent_ts",
|
|
1097
|
+
password: "Secure1!",
|
|
1098
|
+
permissions: ["upload", "view", "ocr_tools"],
|
|
1099
|
+
allowedFormIds: ["tpl_invoices"],
|
|
1100
|
+
});
|
|
951
1101
|
```
|
|
952
1102
|
|
|
953
1103
|
---
|
|
@@ -968,6 +1118,13 @@ Purchase extra document packs or upgrade your plan from the dashboard to continu
|
|
|
968
1118
|
|
|
969
1119
|
## Changelog
|
|
970
1120
|
|
|
1121
|
+
### v1.5.0
|
|
1122
|
+
|
|
1123
|
+
- **New:** `getOcrToolExecutions(opts?)` — paginated execution history for AI Agent runs, including the original image, answer, explanation, status, and processing time. Filterable by `toolId`.
|
|
1124
|
+
- **New:** `createSubUser` and `updateSubUser` now support `allowedFormIds` — restrict which form templates a sub-user can access (`null` = all templates).
|
|
1125
|
+
- **Expanded:** Sub-user permissions updated from 6 to 11: added `"ocr_tools"`, `"gallery"`, `"smart_scan"`, `"ia_agent"`, `"assistant"`.
|
|
1126
|
+
- **New TypeScript types:** `OcrToolExecution`, `OcrExecutionPage` interfaces; `SubUser` updated with `allowedFormIds` and all 11 permissions.
|
|
1127
|
+
|
|
971
1128
|
### v1.2.0
|
|
972
1129
|
|
|
973
1130
|
- **New:** `getDocumentHistory(opts?)` — paginated log of all document processing events (successes and failures)
|
|
@@ -2785,7 +2785,10 @@ var ExtractiaSDK = (() => {
|
|
|
2785
2785
|
const fields = (_b = body == null ? void 0 : body.fields) != null ? _b : Array.isArray(body == null ? void 0 : body.fieldErrors) ? Object.fromEntries(
|
|
2786
2786
|
body.fieldErrors.map((f) => {
|
|
2787
2787
|
var _a2;
|
|
2788
|
-
return [
|
|
2788
|
+
return [
|
|
2789
|
+
f.field,
|
|
2790
|
+
(_a2 = f.message) != null ? _a2 : f.defaultMessage
|
|
2791
|
+
];
|
|
2789
2792
|
})
|
|
2790
2793
|
) : null;
|
|
2791
2794
|
error = new ValidationError(detail != null ? detail : STATUS_MESSAGES[400], fields);
|
|
@@ -2813,12 +2816,18 @@ var ExtractiaSDK = (() => {
|
|
|
2813
2816
|
case 429: {
|
|
2814
2817
|
const retryAfterHeader = (_c = err.response.headers) == null ? void 0 : _c["retry-after"];
|
|
2815
2818
|
const retryAfter = retryAfterHeader != null ? parseInt(retryAfterHeader, 10) : null;
|
|
2816
|
-
error = new RateLimitError(
|
|
2819
|
+
error = new RateLimitError(
|
|
2820
|
+
detail != null ? detail : void 0,
|
|
2821
|
+
isNaN(retryAfter) ? null : retryAfter
|
|
2822
|
+
);
|
|
2817
2823
|
break;
|
|
2818
2824
|
}
|
|
2819
2825
|
default:
|
|
2820
2826
|
if (status >= 500) {
|
|
2821
|
-
error = new ServerError(
|
|
2827
|
+
error = new ServerError(
|
|
2828
|
+
(_d = detail != null ? detail : STATUS_MESSAGES[status]) != null ? _d : STATUS_MESSAGES[500],
|
|
2829
|
+
status
|
|
2830
|
+
);
|
|
2822
2831
|
} else {
|
|
2823
2832
|
error = new ExtractiaError(
|
|
2824
2833
|
detail != null ? detail : err.message,
|
|
@@ -3137,6 +3146,7 @@ var ExtractiaSDK = (() => {
|
|
|
3137
3146
|
__export(ocrTools_exports, {
|
|
3138
3147
|
createOcrTool: () => createOcrTool,
|
|
3139
3148
|
deleteOcrTool: () => deleteOcrTool,
|
|
3149
|
+
getOcrToolExecutions: () => getOcrToolExecutions,
|
|
3140
3150
|
getOcrTools: () => getOcrTools,
|
|
3141
3151
|
runOcrTool: () => runOcrTool,
|
|
3142
3152
|
updateOcrTool: () => updateOcrTool
|
|
@@ -3165,6 +3175,16 @@ var ExtractiaSDK = (() => {
|
|
|
3165
3175
|
const res = await apiClient_default.post(`/ocr-tools/${id}/run`, body);
|
|
3166
3176
|
return res.data;
|
|
3167
3177
|
}
|
|
3178
|
+
async function getOcrToolExecutions({
|
|
3179
|
+
toolId,
|
|
3180
|
+
page = 0,
|
|
3181
|
+
size = 20
|
|
3182
|
+
} = {}) {
|
|
3183
|
+
const params = { page, size };
|
|
3184
|
+
if (toolId) params.toolId = toolId;
|
|
3185
|
+
const res = await apiClient_default.get("/ocr-tools/executions", { params });
|
|
3186
|
+
return res.data;
|
|
3187
|
+
}
|
|
3168
3188
|
|
|
3169
3189
|
// src/subusers.js
|
|
3170
3190
|
var subusers_exports = {};
|
|
@@ -3239,16 +3259,18 @@ var ExtractiaSDK = (() => {
|
|
|
3239
3259
|
);
|
|
3240
3260
|
reader.onerror = () => {
|
|
3241
3261
|
var _a;
|
|
3242
|
-
return reject(
|
|
3262
|
+
return reject(
|
|
3263
|
+
new Error(
|
|
3264
|
+
`fileToBase64: failed to read file "${(_a = file.name) != null ? _a : "unknown"}".`
|
|
3265
|
+
)
|
|
3266
|
+
);
|
|
3243
3267
|
};
|
|
3244
3268
|
reader.readAsDataURL(file);
|
|
3245
3269
|
});
|
|
3246
3270
|
}
|
|
3247
3271
|
function stripDataUrlPrefix(base64OrDataUrl) {
|
|
3248
3272
|
if (typeof base64OrDataUrl !== "string") {
|
|
3249
|
-
throw new TypeError(
|
|
3250
|
-
"stripDataUrlPrefix: argument must be a string."
|
|
3251
|
-
);
|
|
3273
|
+
throw new TypeError("stripDataUrlPrefix: argument must be a string.");
|
|
3252
3274
|
}
|
|
3253
3275
|
const idx = base64OrDataUrl.indexOf(";base64,");
|
|
3254
3276
|
return idx !== -1 ? base64OrDataUrl.slice(idx + 8) : base64OrDataUrl;
|
|
@@ -122,6 +122,7 @@ __export(index_exports, {
|
|
|
122
122
|
getDocumentsByTemplateId: () => getDocumentsByTemplateId,
|
|
123
123
|
getMimeType: () => getMimeType,
|
|
124
124
|
getMyProfile: () => getMyProfile,
|
|
125
|
+
getOcrToolExecutions: () => getOcrToolExecutions,
|
|
125
126
|
getOcrTools: () => getOcrTools,
|
|
126
127
|
getRecentDocuments: () => getRecentDocuments,
|
|
127
128
|
getSubUsers: () => getSubUsers,
|
|
@@ -2849,7 +2850,10 @@ function mapAxiosError(err) {
|
|
|
2849
2850
|
const fields = (_b = body == null ? void 0 : body.fields) != null ? _b : Array.isArray(body == null ? void 0 : body.fieldErrors) ? Object.fromEntries(
|
|
2850
2851
|
body.fieldErrors.map((f) => {
|
|
2851
2852
|
var _a2;
|
|
2852
|
-
return [
|
|
2853
|
+
return [
|
|
2854
|
+
f.field,
|
|
2855
|
+
(_a2 = f.message) != null ? _a2 : f.defaultMessage
|
|
2856
|
+
];
|
|
2853
2857
|
})
|
|
2854
2858
|
) : null;
|
|
2855
2859
|
error = new ValidationError(detail != null ? detail : STATUS_MESSAGES[400], fields);
|
|
@@ -2877,12 +2881,18 @@ function mapAxiosError(err) {
|
|
|
2877
2881
|
case 429: {
|
|
2878
2882
|
const retryAfterHeader = (_c = err.response.headers) == null ? void 0 : _c["retry-after"];
|
|
2879
2883
|
const retryAfter = retryAfterHeader != null ? parseInt(retryAfterHeader, 10) : null;
|
|
2880
|
-
error = new RateLimitError(
|
|
2884
|
+
error = new RateLimitError(
|
|
2885
|
+
detail != null ? detail : void 0,
|
|
2886
|
+
isNaN(retryAfter) ? null : retryAfter
|
|
2887
|
+
);
|
|
2881
2888
|
break;
|
|
2882
2889
|
}
|
|
2883
2890
|
default:
|
|
2884
2891
|
if (status >= 500) {
|
|
2885
|
-
error = new ServerError(
|
|
2892
|
+
error = new ServerError(
|
|
2893
|
+
(_d = detail != null ? detail : STATUS_MESSAGES[status]) != null ? _d : STATUS_MESSAGES[500],
|
|
2894
|
+
status
|
|
2895
|
+
);
|
|
2886
2896
|
} else {
|
|
2887
2897
|
error = new ExtractiaError(
|
|
2888
2898
|
detail != null ? detail : err.message,
|
|
@@ -3201,6 +3211,7 @@ var ocrTools_exports = {};
|
|
|
3201
3211
|
__export(ocrTools_exports, {
|
|
3202
3212
|
createOcrTool: () => createOcrTool,
|
|
3203
3213
|
deleteOcrTool: () => deleteOcrTool,
|
|
3214
|
+
getOcrToolExecutions: () => getOcrToolExecutions,
|
|
3204
3215
|
getOcrTools: () => getOcrTools,
|
|
3205
3216
|
runOcrTool: () => runOcrTool,
|
|
3206
3217
|
updateOcrTool: () => updateOcrTool
|
|
@@ -3229,6 +3240,16 @@ async function runOcrTool(id, base64Image, options = {}) {
|
|
|
3229
3240
|
const res = await apiClient_default.post(`/ocr-tools/${id}/run`, body);
|
|
3230
3241
|
return res.data;
|
|
3231
3242
|
}
|
|
3243
|
+
async function getOcrToolExecutions({
|
|
3244
|
+
toolId,
|
|
3245
|
+
page = 0,
|
|
3246
|
+
size = 20
|
|
3247
|
+
} = {}) {
|
|
3248
|
+
const params = { page, size };
|
|
3249
|
+
if (toolId) params.toolId = toolId;
|
|
3250
|
+
const res = await apiClient_default.get("/ocr-tools/executions", { params });
|
|
3251
|
+
return res.data;
|
|
3252
|
+
}
|
|
3232
3253
|
|
|
3233
3254
|
// src/subusers.js
|
|
3234
3255
|
var subusers_exports = {};
|
|
@@ -3303,16 +3324,18 @@ function fileToBase64(file) {
|
|
|
3303
3324
|
);
|
|
3304
3325
|
reader.onerror = () => {
|
|
3305
3326
|
var _a;
|
|
3306
|
-
return reject(
|
|
3327
|
+
return reject(
|
|
3328
|
+
new Error(
|
|
3329
|
+
`fileToBase64: failed to read file "${(_a = file.name) != null ? _a : "unknown"}".`
|
|
3330
|
+
)
|
|
3331
|
+
);
|
|
3307
3332
|
};
|
|
3308
3333
|
reader.readAsDataURL(file);
|
|
3309
3334
|
});
|
|
3310
3335
|
}
|
|
3311
3336
|
function stripDataUrlPrefix(base64OrDataUrl) {
|
|
3312
3337
|
if (typeof base64OrDataUrl !== "string") {
|
|
3313
|
-
throw new TypeError(
|
|
3314
|
-
"stripDataUrlPrefix: argument must be a string."
|
|
3315
|
-
);
|
|
3338
|
+
throw new TypeError("stripDataUrlPrefix: argument must be a string.");
|
|
3316
3339
|
}
|
|
3317
3340
|
const idx = base64OrDataUrl.indexOf(";base64,");
|
|
3318
3341
|
return idx !== -1 ? base64OrDataUrl.slice(idx + 8) : base64OrDataUrl;
|
|
@@ -2767,7 +2767,10 @@ function mapAxiosError(err) {
|
|
|
2767
2767
|
const fields = (_b = body == null ? void 0 : body.fields) != null ? _b : Array.isArray(body == null ? void 0 : body.fieldErrors) ? Object.fromEntries(
|
|
2768
2768
|
body.fieldErrors.map((f) => {
|
|
2769
2769
|
var _a2;
|
|
2770
|
-
return [
|
|
2770
|
+
return [
|
|
2771
|
+
f.field,
|
|
2772
|
+
(_a2 = f.message) != null ? _a2 : f.defaultMessage
|
|
2773
|
+
];
|
|
2771
2774
|
})
|
|
2772
2775
|
) : null;
|
|
2773
2776
|
error = new ValidationError(detail != null ? detail : STATUS_MESSAGES[400], fields);
|
|
@@ -2795,12 +2798,18 @@ function mapAxiosError(err) {
|
|
|
2795
2798
|
case 429: {
|
|
2796
2799
|
const retryAfterHeader = (_c = err.response.headers) == null ? void 0 : _c["retry-after"];
|
|
2797
2800
|
const retryAfter = retryAfterHeader != null ? parseInt(retryAfterHeader, 10) : null;
|
|
2798
|
-
error = new RateLimitError(
|
|
2801
|
+
error = new RateLimitError(
|
|
2802
|
+
detail != null ? detail : void 0,
|
|
2803
|
+
isNaN(retryAfter) ? null : retryAfter
|
|
2804
|
+
);
|
|
2799
2805
|
break;
|
|
2800
2806
|
}
|
|
2801
2807
|
default:
|
|
2802
2808
|
if (status >= 500) {
|
|
2803
|
-
error = new ServerError(
|
|
2809
|
+
error = new ServerError(
|
|
2810
|
+
(_d = detail != null ? detail : STATUS_MESSAGES[status]) != null ? _d : STATUS_MESSAGES[500],
|
|
2811
|
+
status
|
|
2812
|
+
);
|
|
2804
2813
|
} else {
|
|
2805
2814
|
error = new ExtractiaError(
|
|
2806
2815
|
detail != null ? detail : err.message,
|
|
@@ -3119,6 +3128,7 @@ var ocrTools_exports = {};
|
|
|
3119
3128
|
__export(ocrTools_exports, {
|
|
3120
3129
|
createOcrTool: () => createOcrTool,
|
|
3121
3130
|
deleteOcrTool: () => deleteOcrTool,
|
|
3131
|
+
getOcrToolExecutions: () => getOcrToolExecutions,
|
|
3122
3132
|
getOcrTools: () => getOcrTools,
|
|
3123
3133
|
runOcrTool: () => runOcrTool,
|
|
3124
3134
|
updateOcrTool: () => updateOcrTool
|
|
@@ -3147,6 +3157,16 @@ async function runOcrTool(id, base64Image, options = {}) {
|
|
|
3147
3157
|
const res = await apiClient_default.post(`/ocr-tools/${id}/run`, body);
|
|
3148
3158
|
return res.data;
|
|
3149
3159
|
}
|
|
3160
|
+
async function getOcrToolExecutions({
|
|
3161
|
+
toolId,
|
|
3162
|
+
page = 0,
|
|
3163
|
+
size = 20
|
|
3164
|
+
} = {}) {
|
|
3165
|
+
const params = { page, size };
|
|
3166
|
+
if (toolId) params.toolId = toolId;
|
|
3167
|
+
const res = await apiClient_default.get("/ocr-tools/executions", { params });
|
|
3168
|
+
return res.data;
|
|
3169
|
+
}
|
|
3150
3170
|
|
|
3151
3171
|
// src/subusers.js
|
|
3152
3172
|
var subusers_exports = {};
|
|
@@ -3221,16 +3241,18 @@ function fileToBase64(file) {
|
|
|
3221
3241
|
);
|
|
3222
3242
|
reader.onerror = () => {
|
|
3223
3243
|
var _a;
|
|
3224
|
-
return reject(
|
|
3244
|
+
return reject(
|
|
3245
|
+
new Error(
|
|
3246
|
+
`fileToBase64: failed to read file "${(_a = file.name) != null ? _a : "unknown"}".`
|
|
3247
|
+
)
|
|
3248
|
+
);
|
|
3225
3249
|
};
|
|
3226
3250
|
reader.readAsDataURL(file);
|
|
3227
3251
|
});
|
|
3228
3252
|
}
|
|
3229
3253
|
function stripDataUrlPrefix(base64OrDataUrl) {
|
|
3230
3254
|
if (typeof base64OrDataUrl !== "string") {
|
|
3231
|
-
throw new TypeError(
|
|
3232
|
-
"stripDataUrlPrefix: argument must be a string."
|
|
3233
|
-
);
|
|
3255
|
+
throw new TypeError("stripDataUrlPrefix: argument must be a string.");
|
|
3234
3256
|
}
|
|
3235
3257
|
const idx = base64OrDataUrl.indexOf(";base64,");
|
|
3236
3258
|
return idx !== -1 ? base64OrDataUrl.slice(idx + 8) : base64OrDataUrl;
|
|
@@ -3358,6 +3380,7 @@ export {
|
|
|
3358
3380
|
getDocumentsByTemplateId,
|
|
3359
3381
|
getMimeType,
|
|
3360
3382
|
getMyProfile,
|
|
3383
|
+
getOcrToolExecutions,
|
|
3361
3384
|
getOcrTools,
|
|
3362
3385
|
getRecentDocuments,
|
|
3363
3386
|
getSubUsers,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// ─── Extractia SDK v1.
|
|
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
|
-
/**
|
|
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,6 +208,47 @@ 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. */
|
|
@@ -730,6 +777,24 @@ export function deleteOcrTool(id: string): Promise<void>;
|
|
|
730
777
|
*/
|
|
731
778
|
export function runOcrTool(id: string, base64Image: string, options?: OcrRunOptions): Promise<OcrRunResult>;
|
|
732
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
|
+
|
|
733
798
|
// ─── Sub-Users ────────────────────────────────────────────────────────────────
|
|
734
799
|
|
|
735
800
|
/**
|
|
@@ -741,7 +806,8 @@ export function getSubUsers(): Promise<SubUser[]>;
|
|
|
741
806
|
/**
|
|
742
807
|
* Creates a new sub-user.
|
|
743
808
|
*
|
|
744
|
-
* 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"`.
|
|
745
811
|
*
|
|
746
812
|
* @throws {ForbiddenError} 403 if the plan does not support sub-users or the limit is reached.
|
|
747
813
|
* @throws {ExtractiaError} 409 if the username is already taken.
|
|
@@ -750,14 +816,17 @@ export function getSubUsers(): Promise<SubUser[]>;
|
|
|
750
816
|
* await createSubUser({
|
|
751
817
|
* username: 'agent_bob',
|
|
752
818
|
* password: 'SecurePass1',
|
|
753
|
-
* permissions: ['upload', 'view'],
|
|
819
|
+
* permissions: ['upload', 'view', 'ocr_tools'],
|
|
820
|
+
* allowedFormIds: ['tpl_123'], // optional — omit for access to all forms
|
|
754
821
|
* });
|
|
755
822
|
*/
|
|
756
823
|
export function createSubUser(subUser: {
|
|
757
824
|
username: string;
|
|
758
825
|
password: string;
|
|
759
826
|
permissions: string[];
|
|
760
|
-
|
|
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 }>;
|
|
761
830
|
|
|
762
831
|
/**
|
|
763
832
|
* Deletes a sub-user by username.
|
|
@@ -767,7 +836,7 @@ export function createSubUser(subUser: {
|
|
|
767
836
|
export function deleteSubUser(username: string): Promise<{ deleted: string }>;
|
|
768
837
|
|
|
769
838
|
/**
|
|
770
|
-
* Updates the permissions and/or password of a sub-user.
|
|
839
|
+
* Updates the permissions, allowed forms, and/or password of a sub-user.
|
|
771
840
|
* Only the provided fields are changed.
|
|
772
841
|
*
|
|
773
842
|
* @param username The sub-user's username.
|
|
@@ -776,7 +845,12 @@ export function deleteSubUser(username: string): Promise<{ deleted: string }>;
|
|
|
776
845
|
*/
|
|
777
846
|
export function updateSubUser(
|
|
778
847
|
username: string,
|
|
779
|
-
updates: {
|
|
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
|
+
}
|
|
780
854
|
): Promise<{ username: string; updated: boolean }>;
|
|
781
855
|
|
|
782
856
|
/**
|
|
@@ -907,6 +981,7 @@ declare const extractia: {
|
|
|
907
981
|
updateOcrTool: typeof updateOcrTool;
|
|
908
982
|
deleteOcrTool: typeof deleteOcrTool;
|
|
909
983
|
runOcrTool: typeof runOcrTool;
|
|
984
|
+
getOcrToolExecutions: typeof getOcrToolExecutions;
|
|
910
985
|
// Sub-users
|
|
911
986
|
getSubUsers: typeof getSubUsers;
|
|
912
987
|
createSubUser: typeof createSubUser;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "extractia-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "JavaScript SDK for the ExtractIA API — document extraction, OCR tools, AI summaries, templates & more",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "ExtractIA Team",
|
|
@@ -49,7 +49,8 @@
|
|
|
49
49
|
"test": "vitest run",
|
|
50
50
|
"test:watch": "vitest",
|
|
51
51
|
"test:coverage": "vitest run --coverage",
|
|
52
|
-
"test:ui": "vitest --ui"
|
|
52
|
+
"test:ui": "vitest --ui",
|
|
53
|
+
"test:integration": "vitest run --config vitest.integration.config.js"
|
|
53
54
|
},
|
|
54
55
|
"dependencies": {
|
|
55
56
|
"axios": "^1.10.0"
|
package/src/apiClient.js
CHANGED
|
@@ -194,7 +194,10 @@ export function configure(opts = {}) {
|
|
|
194
194
|
if (opts.retryDelay != null) _config.retryDelay = opts.retryDelay;
|
|
195
195
|
if (opts.debug != null) _config.debug = Boolean(opts.debug);
|
|
196
196
|
if (opts.defaultHeaders) {
|
|
197
|
-
_config.defaultHeaders = {
|
|
197
|
+
_config.defaultHeaders = {
|
|
198
|
+
..._config.defaultHeaders,
|
|
199
|
+
...opts.defaultHeaders,
|
|
200
|
+
};
|
|
198
201
|
}
|
|
199
202
|
if (opts.onBeforeRequest) _config.onBeforeRequest = opts.onBeforeRequest;
|
|
200
203
|
if (opts.onAfterResponse) _config.onAfterResponse = opts.onAfterResponse;
|
package/src/errors.js
CHANGED
|
@@ -32,12 +32,7 @@ export class ExtractiaError extends Error {
|
|
|
32
32
|
* @param {string} [userMessage] — Human-friendly sentence shown to end users.
|
|
33
33
|
* @param {string} [code] — Machine-readable error code.
|
|
34
34
|
*/
|
|
35
|
-
constructor(
|
|
36
|
-
message,
|
|
37
|
-
status = 0,
|
|
38
|
-
userMessage = null,
|
|
39
|
-
code = "SDK_ERROR",
|
|
40
|
-
) {
|
|
35
|
+
constructor(message, status = 0, userMessage = null, code = "SDK_ERROR") {
|
|
41
36
|
super(message);
|
|
42
37
|
this.name = "ExtractiaError";
|
|
43
38
|
this.status = status;
|
|
@@ -140,10 +135,7 @@ export class ValidationError extends ExtractiaError {
|
|
|
140
135
|
* @param {string} [message]
|
|
141
136
|
* @param {Record<string,string>|null} [fields] — Field-level errors, if the server provides them.
|
|
142
137
|
*/
|
|
143
|
-
constructor(
|
|
144
|
-
message = STATUS_MESSAGES[400],
|
|
145
|
-
fields = null,
|
|
146
|
-
) {
|
|
138
|
+
constructor(message = STATUS_MESSAGES[400], fields = null) {
|
|
147
139
|
super(message, 400, STATUS_MESSAGES[400], "VALIDATION_ERROR");
|
|
148
140
|
this.name = "ValidationError";
|
|
149
141
|
/** @type {Record<string,string>|null} */
|
|
@@ -256,7 +248,9 @@ export function mapAxiosError(err) {
|
|
|
256
248
|
const detail = extractServerDetail(err.response.data);
|
|
257
249
|
const userMessage =
|
|
258
250
|
STATUS_MESSAGES[status] ??
|
|
259
|
-
(status >= 500
|
|
251
|
+
(status >= 500
|
|
252
|
+
? STATUS_MESSAGES[500]
|
|
253
|
+
: "Something went wrong. Please try again.");
|
|
260
254
|
|
|
261
255
|
let error;
|
|
262
256
|
|
|
@@ -268,7 +262,10 @@ export function mapAxiosError(err) {
|
|
|
268
262
|
body?.fields ??
|
|
269
263
|
(Array.isArray(body?.fieldErrors)
|
|
270
264
|
? Object.fromEntries(
|
|
271
|
-
body.fieldErrors.map((f) => [
|
|
265
|
+
body.fieldErrors.map((f) => [
|
|
266
|
+
f.field,
|
|
267
|
+
f.message ?? f.defaultMessage,
|
|
268
|
+
]),
|
|
272
269
|
)
|
|
273
270
|
: null);
|
|
274
271
|
error = new ValidationError(detail ?? STATUS_MESSAGES[400], fields);
|
|
@@ -303,12 +300,18 @@ export function mapAxiosError(err) {
|
|
|
303
300
|
const retryAfterHeader = err.response.headers?.["retry-after"];
|
|
304
301
|
const retryAfter =
|
|
305
302
|
retryAfterHeader != null ? parseInt(retryAfterHeader, 10) : null;
|
|
306
|
-
error = new RateLimitError(
|
|
303
|
+
error = new RateLimitError(
|
|
304
|
+
detail ?? undefined,
|
|
305
|
+
isNaN(retryAfter) ? null : retryAfter,
|
|
306
|
+
);
|
|
307
307
|
break;
|
|
308
308
|
}
|
|
309
309
|
default:
|
|
310
310
|
if (status >= 500) {
|
|
311
|
-
error = new ServerError(
|
|
311
|
+
error = new ServerError(
|
|
312
|
+
detail ?? STATUS_MESSAGES[status] ?? STATUS_MESSAGES[500],
|
|
313
|
+
status,
|
|
314
|
+
);
|
|
312
315
|
} else {
|
|
313
316
|
error = new ExtractiaError(
|
|
314
317
|
detail ?? err.message,
|
package/src/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// ─── Extractia SDK v1.
|
|
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
|
-
/**
|
|
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,6 +208,47 @@ 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. */
|
|
@@ -730,6 +777,24 @@ export function deleteOcrTool(id: string): Promise<void>;
|
|
|
730
777
|
*/
|
|
731
778
|
export function runOcrTool(id: string, base64Image: string, options?: OcrRunOptions): Promise<OcrRunResult>;
|
|
732
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
|
+
|
|
733
798
|
// ─── Sub-Users ────────────────────────────────────────────────────────────────
|
|
734
799
|
|
|
735
800
|
/**
|
|
@@ -741,7 +806,8 @@ export function getSubUsers(): Promise<SubUser[]>;
|
|
|
741
806
|
/**
|
|
742
807
|
* Creates a new sub-user.
|
|
743
808
|
*
|
|
744
|
-
* 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"`.
|
|
745
811
|
*
|
|
746
812
|
* @throws {ForbiddenError} 403 if the plan does not support sub-users or the limit is reached.
|
|
747
813
|
* @throws {ExtractiaError} 409 if the username is already taken.
|
|
@@ -750,14 +816,17 @@ export function getSubUsers(): Promise<SubUser[]>;
|
|
|
750
816
|
* await createSubUser({
|
|
751
817
|
* username: 'agent_bob',
|
|
752
818
|
* password: 'SecurePass1',
|
|
753
|
-
* permissions: ['upload', 'view'],
|
|
819
|
+
* permissions: ['upload', 'view', 'ocr_tools'],
|
|
820
|
+
* allowedFormIds: ['tpl_123'], // optional — omit for access to all forms
|
|
754
821
|
* });
|
|
755
822
|
*/
|
|
756
823
|
export function createSubUser(subUser: {
|
|
757
824
|
username: string;
|
|
758
825
|
password: string;
|
|
759
826
|
permissions: string[];
|
|
760
|
-
|
|
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 }>;
|
|
761
830
|
|
|
762
831
|
/**
|
|
763
832
|
* Deletes a sub-user by username.
|
|
@@ -767,7 +836,7 @@ export function createSubUser(subUser: {
|
|
|
767
836
|
export function deleteSubUser(username: string): Promise<{ deleted: string }>;
|
|
768
837
|
|
|
769
838
|
/**
|
|
770
|
-
* Updates the permissions and/or password of a sub-user.
|
|
839
|
+
* Updates the permissions, allowed forms, and/or password of a sub-user.
|
|
771
840
|
* Only the provided fields are changed.
|
|
772
841
|
*
|
|
773
842
|
* @param username The sub-user's username.
|
|
@@ -776,7 +845,12 @@ export function deleteSubUser(username: string): Promise<{ deleted: string }>;
|
|
|
776
845
|
*/
|
|
777
846
|
export function updateSubUser(
|
|
778
847
|
username: string,
|
|
779
|
-
updates: {
|
|
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
|
+
}
|
|
780
854
|
): Promise<{ username: string; updated: boolean }>;
|
|
781
855
|
|
|
782
856
|
/**
|
|
@@ -907,6 +981,7 @@ declare const extractia: {
|
|
|
907
981
|
updateOcrTool: typeof updateOcrTool;
|
|
908
982
|
deleteOcrTool: typeof deleteOcrTool;
|
|
909
983
|
runOcrTool: typeof runOcrTool;
|
|
984
|
+
getOcrToolExecutions: typeof getOcrToolExecutions;
|
|
910
985
|
// Sub-users
|
|
911
986
|
getSubUsers: typeof getSubUsers;
|
|
912
987
|
createSubUser: typeof createSubUser;
|
package/src/index.js
CHANGED
|
@@ -20,12 +20,7 @@ export {
|
|
|
20
20
|
TimeoutError,
|
|
21
21
|
mapAxiosError,
|
|
22
22
|
} from "./errors.js";
|
|
23
|
-
export {
|
|
24
|
-
getToken,
|
|
25
|
-
hasToken,
|
|
26
|
-
clearToken,
|
|
27
|
-
getConfig,
|
|
28
|
-
} from "./apiClient.js";
|
|
23
|
+
export { getToken, hasToken, clearToken, getConfig } from "./apiClient.js";
|
|
29
24
|
|
|
30
25
|
import * as auth from "./auth.js";
|
|
31
26
|
import * as templates from "./templates.js";
|
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).
|
package/src/utils.js
CHANGED
|
@@ -30,7 +30,11 @@ export function fileToBase64(file) {
|
|
|
30
30
|
const reader = new FileReader();
|
|
31
31
|
reader.onload = () => resolve(/** @type {string} */ (reader.result));
|
|
32
32
|
reader.onerror = () =>
|
|
33
|
-
reject(
|
|
33
|
+
reject(
|
|
34
|
+
new Error(
|
|
35
|
+
`fileToBase64: failed to read file "${file.name ?? "unknown"}".`,
|
|
36
|
+
),
|
|
37
|
+
);
|
|
34
38
|
reader.readAsDataURL(file);
|
|
35
39
|
});
|
|
36
40
|
}
|
|
@@ -45,9 +49,7 @@ export function fileToBase64(file) {
|
|
|
45
49
|
*/
|
|
46
50
|
export function stripDataUrlPrefix(base64OrDataUrl) {
|
|
47
51
|
if (typeof base64OrDataUrl !== "string") {
|
|
48
|
-
throw new TypeError(
|
|
49
|
-
"stripDataUrlPrefix: argument must be a string.",
|
|
50
|
-
);
|
|
52
|
+
throw new TypeError("stripDataUrlPrefix: argument must be a string.");
|
|
51
53
|
}
|
|
52
54
|
const idx = base64OrDataUrl.indexOf(";base64,");
|
|
53
55
|
return idx !== -1 ? base64OrDataUrl.slice(idx + 8) : base64OrDataUrl;
|
|
@@ -76,7 +78,9 @@ export function isBase64(str) {
|
|
|
76
78
|
if (typeof str !== "string" || !str.trim()) return false;
|
|
77
79
|
const raw = stripDataUrlPrefix(str);
|
|
78
80
|
// Must be divisible by 4 and only contain valid base64 characters
|
|
79
|
-
return
|
|
81
|
+
return (
|
|
82
|
+
raw.length > 0 && raw.length % 4 === 0 && /^[A-Za-z0-9+/]*={0,2}$/.test(raw)
|
|
83
|
+
);
|
|
80
84
|
}
|
|
81
85
|
|
|
82
86
|
/**
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { defineConfig } from "vitest/config";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Vitest config for integration tests against the real ExtractIA API.
|
|
5
|
+
*
|
|
6
|
+
* Required env vars:
|
|
7
|
+
* EXTRACTIA_API_TOKEN — valid bearer token (all integration tests)
|
|
8
|
+
* EXTRACTIA_ALLOW_WRITE — "true" to enable create/update/delete tests
|
|
9
|
+
* EXTRACTIA_ALLOW_CREDITS — "true" to enable tests that consume doc/AI credits
|
|
10
|
+
*
|
|
11
|
+
* Run:
|
|
12
|
+
* EXTRACTIA_API_TOKEN=sk_xxx EXTRACTIA_ALLOW_WRITE=true EXTRACTIA_ALLOW_CREDITS=true \
|
|
13
|
+
* npm run test:integration
|
|
14
|
+
*/
|
|
15
|
+
export default defineConfig({
|
|
16
|
+
test: {
|
|
17
|
+
globals: true,
|
|
18
|
+
environment: "node",
|
|
19
|
+
include: ["tests/integration/**/*.integration.test.js"],
|
|
20
|
+
// Real HTTP calls — 30 s per test, 30 s for setup/teardown hooks
|
|
21
|
+
testTimeout: 30_000,
|
|
22
|
+
hookTimeout: 30_000,
|
|
23
|
+
// Sequential: avoid hammering the API and triggering rate-limits
|
|
24
|
+
sequence: { concurrent: false },
|
|
25
|
+
reporters: ["verbose"],
|
|
26
|
+
},
|
|
27
|
+
});
|