@anyformat/sdk 0.2.2 → 0.2.3

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/dist/index.js CHANGED
@@ -1,3 +1,46 @@
1
+ // package.json
2
+ var package_default = {
3
+ name: "@anyformat/sdk",
4
+ version: "0.2.3",
5
+ description: "TypeScript SDK for the AnyFormat API",
6
+ license: "MIT",
7
+ author: "Anyformat <info@anyformat.ai>",
8
+ type: "module",
9
+ main: "./dist/index.cjs",
10
+ module: "./dist/index.js",
11
+ types: "./dist/index.d.ts",
12
+ exports: {
13
+ ".": {
14
+ types: "./dist/index.d.ts",
15
+ import: "./dist/index.js",
16
+ require: "./dist/index.cjs"
17
+ }
18
+ },
19
+ files: [
20
+ "dist"
21
+ ],
22
+ engines: {
23
+ node: ">=18"
24
+ },
25
+ scripts: {
26
+ "gen:spec": 'ROOT=$(unset GIT_DIR && git rev-parse --show-toplevel) && cd "$ROOT/anyformat/services/api" && uv run python "$ROOT/anyformat/libs/public/sdk-js/scripts/dump_openapi.py" "$ROOT/anyformat/libs/public/sdk-js/openapi/v2.json"',
27
+ "gen:client": "openapi-ts",
28
+ gen: "bun run gen:spec && bun run gen:client",
29
+ "gen:check": "bun run gen && git diff --exit-code -- openapi/v2.json src/sdk",
30
+ build: "tsup",
31
+ typecheck: "tsc --noEmit",
32
+ test: "vitest run",
33
+ prepublishOnly: "bun run build"
34
+ },
35
+ devDependencies: {
36
+ "@hey-api/openapi-ts": "^0.99.0",
37
+ "@types/node": "^25.9.1",
38
+ tsup: "^8.3.0",
39
+ typescript: "^5.6.0",
40
+ vitest: "^4.1.7"
41
+ }
42
+ };
43
+
1
44
  // src/builder.ts
2
45
  import { readFileSync } from "fs";
3
46
  import { basename } from "path";
@@ -62,10 +105,14 @@ var Result = class {
62
105
  * Convenience accessor for a top-level scalar field on the first
63
106
  * extraction (linear workflows). Returns `undefined` for missing fields
64
107
  * or list-valued fields — read `extractions` directly for those.
108
+ *
109
+ * Parameterise with the declared schema type to type `.value`:
110
+ * `result.field<number>("total").value` is `number | null`.
65
111
  */
66
112
  field(name) {
67
113
  const value = this.extractions[0]?.fields?.[name];
68
- return value !== void 0 && !Array.isArray(value) ? value : void 0;
114
+ if (value === void 0 || Array.isArray(value)) return void 0;
115
+ return value;
69
116
  }
70
117
  };
71
118
 
@@ -82,35 +129,41 @@ var APIError = class extends AnyformatError {
82
129
  status;
83
130
  body;
84
131
  };
85
- var BadRequest = class extends APIError {
132
+ var BadRequestError = class extends APIError {
86
133
  };
87
- var Unauthorized = class extends APIError {
134
+ var UnauthorizedError = class extends APIError {
88
135
  };
89
- var Forbidden = class extends APIError {
136
+ var ForbiddenError = class extends APIError {
90
137
  };
91
- var NotFound = class extends APIError {
138
+ var NotFoundError = class extends APIError {
92
139
  };
93
- var RateLimited = class extends APIError {
140
+ var RateLimitedError = class extends APIError {
94
141
  };
95
142
  var ServerError = class extends APIError {
96
143
  };
97
- var SDKTimeout = class extends AnyformatError {
144
+ var BadRequest = BadRequestError;
145
+ var Unauthorized = UnauthorizedError;
146
+ var Forbidden = ForbiddenError;
147
+ var NotFound = NotFoundError;
148
+ var RateLimited = RateLimitedError;
149
+ var SDKTimeoutError = class extends AnyformatError {
98
150
  };
151
+ var SDKTimeout = SDKTimeoutError;
99
152
  var WorkflowBuilderError = class extends AnyformatError {
100
153
  };
101
154
  function toApiError(status, body) {
102
155
  const message = `AnyFormat API error (${status})`;
103
156
  switch (status) {
104
157
  case 400:
105
- return new BadRequest(status, message, body);
158
+ return new BadRequestError(status, message, body);
106
159
  case 401:
107
- return new Unauthorized(status, message, body);
160
+ return new UnauthorizedError(status, message, body);
108
161
  case 403:
109
- return new Forbidden(status, message, body);
162
+ return new ForbiddenError(status, message, body);
110
163
  case 404:
111
- return new NotFound(status, message, body);
164
+ return new NotFoundError(status, message, body);
112
165
  case 429:
113
- return new RateLimited(status, message, body);
166
+ return new RateLimitedError(status, message, body);
114
167
  default:
115
168
  return status >= 500 ? new ServerError(status, message, body) : new APIError(status, message, body);
116
169
  }
@@ -927,9 +980,24 @@ var createClient = (config = {}) => {
927
980
  };
928
981
 
929
982
  // src/sdk/client.gen.ts
930
- var client = createClient(createConfig({ baseUrl: "https://api-dev.anyformat.ai" }));
983
+ var client = createClient(createConfig({ baseUrl: "https://api.anyformat.dev" }));
931
984
 
932
985
  // src/sdk/sdk.gen.ts
986
+ var v2Parse = (options) => (options.client ?? client).post({
987
+ ...formDataBodySerializer,
988
+ security: [{ scheme: "bearer", type: "http" }],
989
+ url: "/v2/operations/parse",
990
+ ...options,
991
+ headers: {
992
+ "Content-Type": null,
993
+ ...options.headers
994
+ }
995
+ });
996
+ var v2ParseResult = (options) => (options.client ?? client).get({
997
+ security: [{ scheme: "bearer", type: "http" }],
998
+ url: "/v2/operations/parse/{job_id}",
999
+ ...options
1000
+ });
933
1001
  var v2ListWorkflows = (options) => (options?.client ?? client).get({
934
1002
  security: [{ scheme: "bearer", type: "http" }],
935
1003
  url: "/v2/workflows/",
@@ -963,6 +1031,11 @@ var v2GetFileResults = (options) => (options.client ?? client).get({
963
1031
  url: "/v2/workflows/{workflow_id}/files/{collection_id}/results/",
964
1032
  ...options
965
1033
  });
1034
+ var v2RunFile = (options) => (options.client ?? client).post({
1035
+ security: [{ scheme: "bearer", type: "http" }],
1036
+ url: "/v2/workflows/{workflow_id}/files/{file_id}/run/",
1037
+ ...options
1038
+ });
966
1039
  var v2RunWorkflow = (options) => (options.client ?? client).post({
967
1040
  ...formDataBodySerializer,
968
1041
  security: [{ scheme: "bearer", type: "http" }],
@@ -973,6 +1046,16 @@ var v2RunWorkflow = (options) => (options.client ?? client).post({
973
1046
  ...options.headers
974
1047
  }
975
1048
  });
1049
+ var v2UploadFile = (options) => (options.client ?? client).post({
1050
+ ...formDataBodySerializer,
1051
+ security: [{ scheme: "bearer", type: "http" }],
1052
+ url: "/v2/workflows/{workflow_id}/upload/",
1053
+ ...options,
1054
+ headers: {
1055
+ "Content-Type": null,
1056
+ ...options.headers
1057
+ }
1058
+ });
976
1059
 
977
1060
  // src/run.ts
978
1061
  var Run = class {
@@ -1000,11 +1083,49 @@ var Run = class {
1000
1083
  }
1001
1084
  await new Promise((resolve) => setTimeout(resolve, pollMs));
1002
1085
  }
1003
- throw new SDKTimeout(`Extraction did not complete within ${timeoutMs}ms`);
1086
+ throw new SDKTimeoutError(`Extraction did not complete within ${timeoutMs}ms`);
1087
+ }
1088
+ };
1089
+
1090
+ // src/upload.ts
1091
+ var Upload = class {
1092
+ /** @internal */
1093
+ constructor(client2, workflowId, collectionId, fileId) {
1094
+ this.client = client2;
1095
+ this.workflowId = workflowId;
1096
+ this.collectionId = collectionId;
1097
+ this.fileId = fileId;
1098
+ }
1099
+ client;
1100
+ workflowId;
1101
+ collectionId;
1102
+ fileId;
1103
+ /** Trigger extraction on the staged file and return a pending `Run`. */
1104
+ async run() {
1105
+ if (!this.fileId) {
1106
+ throw new WorkflowBuilderError("This upload has no file_id \u2014 cannot run it");
1107
+ }
1108
+ const res = await v2RunFile({
1109
+ client: this.client,
1110
+ path: { workflow_id: this.workflowId, file_id: this.fileId }
1111
+ });
1112
+ if (res.error) throw toApiError(res.response?.status ?? 500, res.error);
1113
+ return new Run(this.client, this.workflowId, res.data.id ?? this.collectionId);
1004
1114
  }
1005
1115
  };
1006
1116
 
1007
1117
  // src/workflow.ts
1118
+ function validateDocument(file, op) {
1119
+ if (file === null) {
1120
+ throw new WorkflowBuilderError(`${op}() requires a named file`);
1121
+ }
1122
+ const filename = file.name;
1123
+ if (typeof filename !== "string" || filename.length === 0) {
1124
+ throw new WorkflowBuilderError(
1125
+ `${op}() needs a named file so the server can detect its type \u2014 pass a File, e.g. new File([bytes], "invoice.pdf"), not a bare Blob`
1126
+ );
1127
+ }
1128
+ }
1008
1129
  var Workflow = class {
1009
1130
  /** @internal */
1010
1131
  constructor(client2, id, name) {
@@ -1016,29 +1137,36 @@ var Workflow = class {
1016
1137
  id;
1017
1138
  name;
1018
1139
  /** Submit a document against this workflow and return a pending `Run`. */
1019
- async run(file = null, opts = {}) {
1020
- if (file === null && !opts.text) {
1021
- throw new WorkflowBuilderError("run() requires a document \u2014 pass a file or non-empty opts.text");
1022
- }
1023
- if (file !== null && opts.text !== void 0) {
1024
- throw new WorkflowBuilderError("run() accepts either a file or text, not both");
1025
- }
1026
- if (file !== null) {
1027
- const filename = file.name;
1028
- if (typeof filename !== "string" || filename.length === 0) {
1029
- throw new WorkflowBuilderError(
1030
- 'run() needs a named file so the server can detect its type \u2014 pass a File, e.g. new File([bytes], "invoice.pdf"), not a bare Blob'
1031
- );
1032
- }
1033
- }
1140
+ async run(file = null) {
1141
+ validateDocument(file, "run");
1034
1142
  const submitted = await v2RunWorkflow({
1035
1143
  client: this.client,
1036
1144
  path: { workflow_id: this.id },
1037
- body: { file, text: opts.text ?? null }
1145
+ body: { file }
1038
1146
  });
1039
1147
  if (submitted.error) throw toApiError(submitted.response?.status ?? 500, submitted.error);
1040
1148
  return new Run(this.client, this.id, submitted.data.id);
1041
1149
  }
1150
+ /**
1151
+ * Stage a document in this workflow WITHOUT running it (no credits spent).
1152
+ * Returns an `Upload`; call `.run()` on it to start extraction.
1153
+ */
1154
+ async upload(file = null) {
1155
+ validateDocument(file, "upload");
1156
+ const staged = await v2UploadFile({
1157
+ client: this.client,
1158
+ path: { workflow_id: this.id },
1159
+ body: { file }
1160
+ });
1161
+ if (staged.error) throw toApiError(staged.response?.status ?? 500, staged.error);
1162
+ const collectionId = staged.data.file_collection_id;
1163
+ if (!collectionId) {
1164
+ throw new WorkflowBuilderError(
1165
+ "Upload succeeded but the server did not return a file_collection_id \u2014 cannot build an Upload handle"
1166
+ );
1167
+ }
1168
+ return new Upload(this.client, this.id, collectionId, staged.data.file_id ?? null);
1169
+ }
1042
1170
  };
1043
1171
 
1044
1172
  // src/builder.ts
@@ -1067,13 +1195,21 @@ var WorkflowBuilder = class {
1067
1195
  }
1068
1196
  parse(opts = {}) {
1069
1197
  this.require(this.parseNode === null, "parse() can only be called once");
1198
+ const o = opts;
1070
1199
  this.parseNode = {
1071
1200
  type: "parse",
1072
- id: opts.id ?? this.nextId("parse"),
1073
- mode: opts.mode ?? "standard",
1074
- ...opts.promptHint !== void 0 ? { prompt_hint: opts.promptHint } : {},
1075
- figure_enhancement: opts.figureEnhancement ?? false,
1076
- cache: opts.cache ?? true
1201
+ id: o.id ?? this.nextId("parse"),
1202
+ mode: o.mode ?? "standard",
1203
+ ...o.promptHint !== void 0 ? { prompt_hint: o.promptHint } : {},
1204
+ figure_enhancement: o.figureEnhancement ?? false,
1205
+ cache: o.cache ?? true,
1206
+ ...o.effort !== void 0 ? { effort: o.effort } : {},
1207
+ ...o.ocrEffort !== void 0 ? { ocr_effort: o.ocrEffort } : {},
1208
+ ...o.region !== void 0 ? { region: o.region } : {},
1209
+ ...o.skipRoutingReview !== void 0 ? { skip_routing_review: o.skipRoutingReview } : {},
1210
+ ...o.figures !== void 0 ? { figures: o.figures } : {},
1211
+ ...o.textFormatting !== void 0 ? { text_formatting: o.textFormatting } : {},
1212
+ ...o.routingEffort !== void 0 ? { routing_effort: o.routingEffort } : {}
1077
1213
  };
1078
1214
  return this;
1079
1215
  }
@@ -1120,9 +1256,12 @@ var WorkflowBuilder = class {
1120
1256
  const node = {
1121
1257
  type: "extract",
1122
1258
  id: opts.id ?? this.nextId("extract"),
1259
+ ...opts.mode !== void 0 ? { mode: opts.mode } : {},
1123
1260
  extraction_schema: { fields },
1124
1261
  ...lookupFileUploads.length > 0 ? { lookup_file_uploads: lookupFileUploads } : {},
1125
- ...opts.lookupSuggestion !== void 0 ? { lookup_suggestion: opts.lookupSuggestion } : {}
1262
+ ...opts.lookupSuggestion !== void 0 ? { lookup_suggestion: opts.lookupSuggestion } : {},
1263
+ ...opts.lookupReasoningEffort !== void 0 ? { lookup_reasoning_effort: opts.lookupReasoningEffort } : {},
1264
+ ...opts.useImages !== void 0 ? { use_images: opts.useImages } : {}
1126
1265
  };
1127
1266
  this.extracts.push([node, branchId]);
1128
1267
  return this;
@@ -1219,6 +1358,7 @@ var WorkflowBuilder = class {
1219
1358
  };
1220
1359
 
1221
1360
  // src/client.ts
1361
+ var SDK_USER_AGENT = `anyformat-typescript-sdk-${package_default.version}`;
1222
1362
  var Anyformat = class {
1223
1363
  /** @internal */
1224
1364
  _client;
@@ -1228,8 +1368,12 @@ var Anyformat = class {
1228
1368
  baseUrl: opts.baseUrl ?? "https://api.anyformat.ai",
1229
1369
  auth: opts.apiKey,
1230
1370
  // Identifies the caller in our logs (Datadog facet). Plain HTTP callers
1231
- // send no such header and are logged as origin "api".
1232
- headers: { "x-anyformat-origin": "js-sdk" }
1371
+ // send no such header and are logged as origin "api". User-Agent is
1372
+ // ignored by browsers (forbidden header) but used by Node fetch.
1373
+ headers: {
1374
+ "x-anyformat-origin": "js-sdk",
1375
+ "User-Agent": SDK_USER_AGENT
1376
+ }
1233
1377
  })
1234
1378
  );
1235
1379
  }
@@ -1252,6 +1396,42 @@ var Anyformat = class {
1252
1396
  if (res.error) throw toApiError(res.response?.status ?? 500, res.error);
1253
1397
  return workflowId;
1254
1398
  }
1399
+ /** Stage a document in a workflow without running it. Returns an `Upload`. */
1400
+ upload(workflowId, file = null) {
1401
+ return new Workflow(this._client, workflowId, "").upload(file);
1402
+ }
1403
+ /** Trigger extraction on already-uploaded files. Returns a pending `Run` per file_id. */
1404
+ async runWorkflow(workflowId, fileIds) {
1405
+ const runs = [];
1406
+ for (const fileId of fileIds) {
1407
+ const res = await v2RunFile({
1408
+ client: this._client,
1409
+ path: { workflow_id: workflowId, file_id: fileId }
1410
+ });
1411
+ if (res.error) throw toApiError(res.response?.status ?? 500, res.error);
1412
+ runs.push(new Run(this._client, workflowId, res.data.id ?? fileId));
1413
+ }
1414
+ return runs;
1415
+ }
1416
+ /**
1417
+ * Parse a document with the platform's atomic `/parse` operation (fast lite
1418
+ * parse). Returns an opaque `jobId`; poll `getParseMarkdown(jobId)` for the
1419
+ * parsed markdown.
1420
+ */
1421
+ async parse(file) {
1422
+ const res = await v2Parse({ client: this._client, body: { file } });
1423
+ if (res.error) throw toApiError(res.response?.status ?? 500, res.error);
1424
+ return res.data.job_id;
1425
+ }
1426
+ /**
1427
+ * Poll a parse job. Returns the parsed markdown once the job is done, or
1428
+ * `null` while it is still processing.
1429
+ */
1430
+ async getParseMarkdown(jobId) {
1431
+ const res = await v2ParseResult({ client: this._client, path: { job_id: jobId } });
1432
+ if (res.error) throw toApiError(res.response?.status ?? 500, res.error);
1433
+ return res.data.status === "done" ? res.data.markdown ?? "" : null;
1434
+ }
1255
1435
  };
1256
1436
 
1257
1437
  // src/schema.ts
@@ -1276,16 +1456,23 @@ export {
1276
1456
  Anyformat,
1277
1457
  AnyformatError,
1278
1458
  BadRequest,
1459
+ BadRequestError,
1279
1460
  Forbidden,
1461
+ ForbiddenError,
1280
1462
  NotFound,
1463
+ NotFoundError,
1281
1464
  ParseView,
1282
1465
  RateLimited,
1466
+ RateLimitedError,
1283
1467
  Result,
1284
1468
  Run,
1285
1469
  SDKTimeout,
1470
+ SDKTimeoutError,
1286
1471
  Schema,
1287
1472
  ServerError,
1288
1473
  Unauthorized,
1474
+ UnauthorizedError,
1475
+ Upload,
1289
1476
  Workflow,
1290
1477
  WorkflowBuilder,
1291
1478
  WorkflowBuilderError