@docmana/sdk 0.3.5 → 0.8.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.
Files changed (53) hide show
  1. package/CHANGELOG.md +33 -4
  2. package/README.md +175 -232
  3. package/dist/api/execution-result.d.ts +3 -0
  4. package/dist/api/execution-status.d.ts +6 -0
  5. package/dist/api/flow-definition.d.ts +3 -0
  6. package/dist/api/flow-metadata.d.ts +3 -0
  7. package/dist/api/list-flows.d.ts +3 -0
  8. package/dist/api/list-organizations.d.ts +3 -0
  9. package/dist/api/organization-documents.d.ts +7 -0
  10. package/dist/api/run-flow.d.ts +6 -0
  11. package/dist/cli.mjs +1022 -695
  12. package/dist/cli.mjs.map +1 -1
  13. package/dist/client.d.ts +26 -0
  14. package/dist/config.d.ts +21 -0
  15. package/dist/errors.d.ts +55 -0
  16. package/dist/files/resolve-input.d.ts +9 -0
  17. package/dist/http/http-client.d.ts +31 -0
  18. package/dist/index.d.ts +4 -349
  19. package/dist/index.js +323 -339
  20. package/dist/index.js.map +1 -1
  21. package/dist/index.mjs +319 -335
  22. package/dist/index.mjs.map +1 -1
  23. package/dist/models/docmana-api-document-execution-result.model.d.ts +9 -0
  24. package/dist/models/docmana-api-document-request.model.d.ts +15 -0
  25. package/dist/models/docmana-api-execution-result.model.d.ts +24 -0
  26. package/dist/models/docmana-api-node-result.model.d.ts +28 -0
  27. package/dist/models/docmana-classification-result.model.d.ts +3 -0
  28. package/dist/models/docmana-conclusion-result.model.d.ts +3 -0
  29. package/dist/models/docmana-cross-validation-result.model.d.ts +3 -0
  30. package/dist/models/docmana-document-mapping.model.d.ts +4 -0
  31. package/dist/models/docmana-document-result.model.d.ts +11 -0
  32. package/dist/models/docmana-execution-result.model.d.ts +15 -0
  33. package/dist/models/docmana-extraction-result.model.d.ts +3 -0
  34. package/dist/models/docmana-metadata-extraction-result.model.d.ts +3 -0
  35. package/dist/models/docmana-node-result.model.d.ts +7 -0
  36. package/dist/models/docmana-score-node-result.model.d.ts +5 -0
  37. package/dist/models/docmana-validation-result.model.d.ts +3 -0
  38. package/dist/models/document-content.model.d.ts +4 -0
  39. package/dist/models/execution-progress.model.d.ts +4 -0
  40. package/dist/models/execution-status.enum.d.ts +1 -0
  41. package/dist/models/flow-configs.model.d.ts +16 -0
  42. package/dist/models/flow-edge.model.d.ts +4 -0
  43. package/dist/models/flow-node-type.enum.d.ts +1 -0
  44. package/dist/models/flow-node.model.d.ts +33 -0
  45. package/dist/models/flow.model.d.ts +11 -0
  46. package/dist/models/index.d.ts +26 -0
  47. package/dist/models/node-translation-result.model.d.ts +7 -0
  48. package/dist/models/physical-document.model.d.ts +9 -0
  49. package/dist/models/virtual-document.model.d.ts +10 -0
  50. package/dist/polling/poll.d.ts +15 -0
  51. package/dist/types.d.ts +145 -0
  52. package/package.json +17 -14
  53. package/dist/index.d.cts +0 -349
package/dist/index.mjs CHANGED
@@ -1,30 +1,34 @@
1
- // src/config.ts
1
+ // src/sdk/config.ts
2
2
  var DEFAULTS = {
3
- apiBaseUrl: "https://api.docmana.ai",
4
3
  tokenEndpoint: "https://4fe70f5b-e013-4f65-9fa7-3109a33beba5.ciamlogin.com/4fe70f5b-e013-4f65-9fa7-3109a33beba5/oauth2/v2.0/token",
5
4
  scope: "api://d781e6ba-cc08-4618-8099-ad968abd2b9e/.default",
6
5
  timeoutMs: 3e5,
7
- pollIntervalMs: 2e3
6
+ pollIntervalMs: 2e3,
7
+ pollMaxIntervalMs: 1e4
8
8
  };
9
9
  function resolveConfig(config) {
10
- if (!config.clientId) throw new Error("DocmanaConfig.clientId is required");
11
- if (!config.clientSecret) throw new Error("DocmanaConfig.clientSecret is required");
12
- const apiBaseUrl = (config.apiBaseUrl ?? DEFAULTS.apiBaseUrl).replace(/\/+$/, "");
10
+ if (!config.apiBaseUrl) throw new Error("DocmanaConfig.apiBaseUrl is required");
11
+ if (!config.accessToken && !config.getAccessToken) {
12
+ throw new Error("DocmanaConfig.accessToken or DocmanaConfig.getAccessToken is required");
13
+ }
14
+ const apiBaseUrl = normalizeApiBaseUrl(config.apiBaseUrl);
13
15
  return {
14
- clientId: config.clientId,
15
- clientSecret: config.clientSecret,
16
16
  apiBaseUrl,
17
- tokenEndpoint: config.tokenEndpoint ?? DEFAULTS.tokenEndpoint,
18
- scope: config.scope ?? DEFAULTS.scope,
17
+ accessToken: config.accessToken,
18
+ getAccessToken: config.getAccessToken,
19
19
  timeoutMs: config.timeoutMs ?? DEFAULTS.timeoutMs,
20
20
  pollIntervalMs: config.pollIntervalMs ?? DEFAULTS.pollIntervalMs,
21
+ pollMaxIntervalMs: config.pollMaxIntervalMs ?? DEFAULTS.pollMaxIntervalMs,
21
22
  fetchImpl: config.fetch ?? fetch,
22
- headers: config.headers ?? {},
23
- tokenCache: config.tokenCache
23
+ headers: config.headers ?? {}
24
24
  };
25
25
  }
26
+ function normalizeApiBaseUrl(value) {
27
+ const trimmed = value.replace(/\/+$/, "");
28
+ return /\/v\d+$/i.test(trimmed) ? trimmed : `${trimmed}/v1`;
29
+ }
26
30
 
27
- // src/errors.ts
31
+ // src/sdk/errors.ts
28
32
  var DocmanaError = class extends Error {
29
33
  status;
30
34
  requestId;
@@ -77,7 +81,14 @@ var DocmanaAbortError = class extends DocmanaError {
77
81
  super(message, { code: "aborted" });
78
82
  }
79
83
  };
80
- function errorFromResponse(status, message, requestId) {
84
+ var DocmanaRateLimitError = class extends DocmanaError {
85
+ retryAfterMs;
86
+ constructor(message, opts = {}) {
87
+ super(message, { ...opts, code: "rate_limited" });
88
+ this.retryAfterMs = opts.retryAfterMs;
89
+ }
90
+ };
91
+ function errorFromResponse(status, message, requestId, retryAfterMs) {
81
92
  const opts = { status, requestId };
82
93
  switch (status) {
83
94
  case 400:
@@ -88,92 +99,14 @@ function errorFromResponse(status, message, requestId) {
88
99
  return new DocmanaPermissionError(message, opts);
89
100
  case 404:
90
101
  return new DocmanaNotFoundError(message, opts);
102
+ case 429:
103
+ return new DocmanaRateLimitError(message, { ...opts, retryAfterMs });
91
104
  default:
92
105
  return new DocmanaError(message, opts);
93
106
  }
94
107
  }
95
108
 
96
- // src/auth/token-manager.ts
97
- var SAFETY_MARGIN_MS = 6e4;
98
- var TokenManager = class {
99
- constructor(opts) {
100
- this.opts = opts;
101
- }
102
- opts;
103
- token = null;
104
- expiresAt = 0;
105
- inflight = null;
106
- invalidate() {
107
- this.token = null;
108
- this.expiresAt = 0;
109
- void this.opts.tokenCache?.clear().catch(() => void 0);
110
- }
111
- async getToken() {
112
- if (this.token && Date.now() < this.expiresAt) return this.token;
113
- const cached = await this.readCachedToken();
114
- if (cached) return cached;
115
- if (this.inflight) return this.inflight;
116
- this.inflight = this.acquire().finally(() => {
117
- this.inflight = null;
118
- });
119
- return this.inflight;
120
- }
121
- async acquire() {
122
- const body = new URLSearchParams({
123
- grant_type: "client_credentials",
124
- client_id: this.opts.clientId,
125
- client_secret: this.opts.clientSecret,
126
- scope: this.opts.scope
127
- });
128
- const res = await this.opts.fetchImpl(this.opts.tokenEndpoint, {
129
- method: "POST",
130
- headers: { "content-type": "application/x-www-form-urlencoded" },
131
- body: body.toString()
132
- });
133
- if (!res.ok) {
134
- throw new DocmanaAuthError("Failed to acquire Docmana access token", { status: res.status });
135
- }
136
- const json = await res.json();
137
- if (typeof json.access_token !== "string" || json.access_token.length === 0 || typeof json.expires_in !== "number" || !Number.isFinite(json.expires_in)) {
138
- throw new DocmanaAuthError("Malformed token response from Docmana CIAM", {
139
- status: res.status
140
- });
141
- }
142
- this.token = json.access_token;
143
- this.expiresAt = Date.now() + json.expires_in * 1e3 - SAFETY_MARGIN_MS;
144
- await this.writeCachedToken().catch(() => void 0);
145
- return this.token;
146
- }
147
- async readCachedToken() {
148
- if (!this.opts.tokenCache) return null;
149
- let entry;
150
- try {
151
- entry = await this.opts.tokenCache.read();
152
- } catch {
153
- return null;
154
- }
155
- if (!entry) return null;
156
- if (entry.clientId !== this.opts.clientId || entry.tokenEndpoint !== this.opts.tokenEndpoint || entry.scope !== this.opts.scope || Date.now() >= entry.expiresAt) {
157
- await this.opts.tokenCache.clear().catch(() => void 0);
158
- return null;
159
- }
160
- this.token = entry.accessToken;
161
- this.expiresAt = entry.expiresAt;
162
- return this.token;
163
- }
164
- async writeCachedToken() {
165
- if (!this.opts.tokenCache || !this.token) return;
166
- await this.opts.tokenCache.write({
167
- accessToken: this.token,
168
- expiresAt: this.expiresAt,
169
- clientId: this.opts.clientId,
170
- tokenEndpoint: this.opts.tokenEndpoint,
171
- scope: this.opts.scope
172
- });
173
- }
174
- };
175
-
176
- // src/http/http-client.ts
109
+ // src/sdk/http/http-client.ts
177
110
  var HttpClient = class {
178
111
  constructor(opts) {
179
112
  this.opts = opts;
@@ -189,21 +122,29 @@ var HttpClient = class {
189
122
  throw new DocmanaError("Invalid JSON response from Docmana", { status: res.status });
190
123
  }
191
124
  }
125
+ async requestBytes(method, path, options = {}) {
126
+ const res = await this.requestRaw(method, path, options);
127
+ return { bytes: new Uint8Array(await res.arrayBuffer()), headers: res.headers };
128
+ }
192
129
  async requestRaw(method, path, options = {}) {
193
130
  let res = await this.send(method, path, options);
194
- if (res.status === 401) {
195
- this.opts.tokenManager.invalidate();
196
- res = await this.send(method, path, options);
131
+ if (res.status === 401 && this.opts.getAccessToken) {
132
+ res = await this.send(method, path, options, true);
197
133
  }
198
134
  if (!res.ok) {
199
135
  const requestId = res.headers.get("x-request-id") ?? void 0;
200
136
  const message = await this.extractMessage(res);
201
- throw errorFromResponse(res.status, message, requestId);
137
+ throw errorFromResponse(
138
+ res.status,
139
+ message,
140
+ requestId,
141
+ parseRetryAfterMs(res.headers.get("retry-after"))
142
+ );
202
143
  }
203
144
  return res;
204
145
  }
205
- async send(method, path, options) {
206
- const token = await this.opts.tokenManager.getToken();
146
+ async send(method, path, options, forceRefresh = false) {
147
+ const token = await this.resolveToken(forceRefresh);
207
148
  const url = new URL(this.opts.apiBaseUrl + path);
208
149
  for (const [k, v] of Object.entries(options.query ?? {})) url.searchParams.set(k, v);
209
150
  const headers = {
@@ -225,6 +166,13 @@ var HttpClient = class {
225
166
  throw err;
226
167
  }
227
168
  }
169
+ async resolveToken(forceRefresh) {
170
+ const token = this.opts.getAccessToken ? await this.opts.getAccessToken(forceRefresh ? { forceRefresh: true } : void 0) : this.opts.accessToken;
171
+ if (!token) {
172
+ throw new DocmanaError("Missing Docmana access token", { code: "auth_missing" });
173
+ }
174
+ return token;
175
+ }
228
176
  async extractMessage(res) {
229
177
  try {
230
178
  const data = await res.clone().json();
@@ -234,8 +182,20 @@ var HttpClient = class {
234
182
  }
235
183
  }
236
184
  };
185
+ function parseRetryAfterMs(value) {
186
+ if (!value) return void 0;
187
+ const seconds = Number(value);
188
+ if (Number.isFinite(seconds) && seconds >= 0) {
189
+ return seconds * 1e3;
190
+ }
191
+ const retryAt = Date.parse(value);
192
+ if (!Number.isNaN(retryAt)) {
193
+ return Math.max(0, retryAt - Date.now());
194
+ }
195
+ return void 0;
196
+ }
237
197
 
238
- // src/files/resolve-input.ts
198
+ // src/sdk/files/resolve-input.ts
239
199
  import { readFile } from "fs/promises";
240
200
  import { basename } from "path";
241
201
  var CONTENT_TYPES = {
@@ -260,310 +220,310 @@ function isReadable(x) {
260
220
  function isPathInput(x) {
261
221
  return typeof x === "object" && x !== null && typeof x.path === "string";
262
222
  }
223
+ function isNamedFileInput(x) {
224
+ if (typeof x !== "object" || x === null) return false;
225
+ const value = x;
226
+ return typeof value.filename === "string" && (value.data instanceof Uint8Array || isReadable(value.data));
227
+ }
263
228
  async function streamToBuffer(stream) {
264
229
  const chunks = [];
265
230
  for await (const chunk of stream) chunks.push(Buffer.from(chunk));
266
231
  return Buffer.concat(chunks);
267
232
  }
268
- async function resolveOne(input, fallbackName) {
233
+ function toBlobBytes(bytes) {
234
+ return new Uint8Array(bytes);
235
+ }
236
+ async function resolveOne(input) {
269
237
  if (typeof input === "string" || isPathInput(input)) {
270
238
  const path = typeof input === "string" ? input : input.path;
271
239
  const data = await readFile(path);
272
240
  const filename = basename(path);
273
- return { data: new Blob([data]), filename, contentType: contentTypeFor(filename) };
274
- }
275
- if (input instanceof Uint8Array) {
276
- return {
277
- data: new Blob([input]),
278
- filename: fallbackName,
279
- contentType: contentTypeFor(fallbackName)
280
- };
241
+ return { data: new Blob([toBlobBytes(data)]), filename, contentType: contentTypeFor(filename) };
281
242
  }
282
- if (isReadable(input)) {
283
- const buf = await streamToBuffer(input);
243
+ if (isNamedFileInput(input)) {
244
+ const bytes = input.data instanceof Uint8Array ? input.data : await streamToBuffer(input.data);
284
245
  return {
285
- data: new Blob([buf]),
286
- filename: fallbackName,
287
- contentType: contentTypeFor(fallbackName)
246
+ data: new Blob([toBlobBytes(bytes)]),
247
+ filename: input.filename,
248
+ contentType: input.contentType ?? contentTypeFor(input.filename)
288
249
  };
289
250
  }
290
251
  throw new Error("Unsupported file input type");
291
252
  }
292
253
  async function resolveInputs(input) {
293
- const list = input.files ?? (input.file !== void 0 ? [input.file] : []);
294
- if (list.length === 0) throw new Error("runFlow requires `file` or `files`");
295
- const fallback = input.fileName ?? "upload.bin";
296
- return Promise.all(list.map((item) => resolveOne(item, fallback)));
254
+ const list = input.files;
255
+ if (list.length === 0) throw new Error("runFlow requires `files`");
256
+ return Promise.all(list.map((item) => resolveOne(item)));
297
257
  }
298
258
 
299
- // src/api/upload.ts
300
- async function uploadFiles(http, parts, signal) {
259
+ // src/sdk/api/run-flow.ts
260
+ async function runFlow(http, flowId, parts, signal, useDraftVersion = false, context, callbackUrl, language) {
301
261
  const form = new FormData();
302
262
  for (const part of parts) {
303
263
  form.append("files", new File([part.data], part.filename, { type: part.contentType }));
304
264
  }
305
- const res = await http.requestJson("POST", "/upload", { body: form, signal });
306
- return [res.id];
265
+ if (context !== void 0) form.append("json_context", JSON.stringify(context));
266
+ if (callbackUrl !== void 0) form.append("callback_url", callbackUrl);
267
+ if (language !== void 0) form.append("language", language);
268
+ if (useDraftVersion) form.append("useDraftVersion", "true");
269
+ return http.requestJson(
270
+ "POST",
271
+ `/flows/${flowId}/execute-async`,
272
+ { body: form, signal }
273
+ );
274
+ }
275
+
276
+ // src/sdk/api/execution-status.ts
277
+ async function getStatus(http, executionResultId, signal) {
278
+ const res = await http.requestRaw("GET", `/executions/${executionResultId}/status`, { signal });
279
+ const text = await res.text();
280
+ const body = text ? JSON.parse(text) : { status: "" };
281
+ const pollAfterMs = parsePositiveInt(res.headers.get("x-poll-after"));
282
+ return pollAfterMs === void 0 ? body : { ...body, pollAfterMs };
283
+ }
284
+ function parsePositiveInt(value) {
285
+ if (!value) return void 0;
286
+ const parsed = Number(value);
287
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
307
288
  }
308
289
 
309
- // src/api/run-flow.ts
310
- async function runFlow(http, flowId, uuidFiles, signal, once = false, context, callbackUrl) {
311
- const path = once ? `/flows/run_once_flow/${flowId}` : `/flows/run_flow/${flowId}`;
312
- const body = { uuid_files: uuidFiles };
313
- if (context !== void 0) body.json_context = context;
314
- if (callbackUrl !== void 0) body.callback_url = callbackUrl;
315
- const res = await http.requestJson("POST", path, {
316
- query: { async_mode: "true" },
317
- headers: { "content-type": "application/json" },
318
- body: JSON.stringify(body),
290
+ // src/sdk/api/execution-result.ts
291
+ async function getResult(http, executionResultId, signal, language) {
292
+ return http.requestJson("GET", `/executions/${executionResultId}/result`, {
293
+ query: language ? { language } : void 0,
319
294
  signal
320
295
  });
321
- return res.execution_result_id;
322
296
  }
323
297
 
324
- // src/api/execution-status.ts
325
- async function getStatus(http, executionResultId, signal) {
326
- return http.requestJson("GET", `/flows/execution-status/${executionResultId}`, { signal });
298
+ // src/sdk/api/list-flows.ts
299
+ async function listFlows(http) {
300
+ const flows = await http.requestJson("GET", "/flows");
301
+ return Array.isArray(flows) ? flows.map(toFlowListItem) : [];
302
+ }
303
+ function toFlowListItem(flow) {
304
+ const value = flow && typeof flow === "object" ? flow : {};
305
+ return {
306
+ id: toStringValue(value.id),
307
+ name: toStringValue(value.name),
308
+ state: toStateValue(value.state)
309
+ };
310
+ }
311
+ function toStringValue(value) {
312
+ return typeof value === "string" ? value : "";
313
+ }
314
+ function toStateValue(value) {
315
+ return typeof value === "string" && value.trim() ? value : "unknown";
327
316
  }
328
317
 
329
- // src/api/execution-result.ts
330
- async function getResult(http, executionResultId, signal) {
331
- return http.requestJson("GET", `/flows/execution-result/${executionResultId}`, { signal });
318
+ // src/sdk/api/list-organizations.ts
319
+ async function listOrganizations(http) {
320
+ const organizations = await http.requestJson("GET", "/organizations");
321
+ return Array.isArray(organizations) ? organizations.map(toAccessibleOrganization).filter(isAccessibleOrganization) : [];
322
+ }
323
+ function toAccessibleOrganization(organization) {
324
+ const value = organization && typeof organization === "object" ? organization : {};
325
+ const id = toStringValue2(value.id).trim();
326
+ if (!id) return void 0;
327
+ const name = toStringValue2(value.name).trim() || id;
328
+ return {
329
+ id,
330
+ name
331
+ };
332
+ }
333
+ function isAccessibleOrganization(organization) {
334
+ return organization !== void 0;
335
+ }
336
+ function toStringValue2(value) {
337
+ return typeof value === "string" ? value : "";
338
+ }
339
+
340
+ // src/sdk/api/flow-metadata.ts
341
+ async function getFlowMetadata(http, flowId) {
342
+ return http.requestJson("GET", `/flows/${encodeURIComponent(flowId)}/metadata`);
332
343
  }
333
344
 
334
- // src/polling/poll.ts
345
+ // src/sdk/api/flow-definition.ts
346
+ async function getFlowDefinition(http, flowId) {
347
+ return http.requestJson("GET", `/flows/${encodeURIComponent(flowId)}/definition`);
348
+ }
349
+
350
+ // src/sdk/api/organization-documents.ts
351
+ async function listOrganizationDocuments(http, options = {}) {
352
+ const result = await http.requestJson("GET", "/organization-documents", {
353
+ query: options.prefix ? { prefix: options.prefix } : void 0
354
+ });
355
+ const value = result && typeof result === "object" ? result : {};
356
+ return {
357
+ configured: value.configured === true,
358
+ documents: Array.isArray(value.documents) ? value.documents.map(toOrganizationDocument).filter(isOrganizationDocument) : []
359
+ };
360
+ }
361
+ async function downloadOrganizationDocument(http, path) {
362
+ const result = await http.requestBytes("GET", "/organization-documents/download", {
363
+ query: { path }
364
+ });
365
+ return {
366
+ bytes: result.bytes,
367
+ filename: filenameFromHeaders(result.headers) ?? filenameFromPath(path),
368
+ contentType: result.headers.get("content-type") || void 0
369
+ };
370
+ }
371
+ async function downloadOrganizationDocuments(http, paths) {
372
+ return Promise.all(paths.map((path) => downloadOrganizationDocument(http, path)));
373
+ }
374
+ function toOrganizationDocument(document) {
375
+ const value = document && typeof document === "object" ? document : {};
376
+ const path = toStringValue3(value.path).trim();
377
+ if (!path) return void 0;
378
+ const contentType = toStringValue3(value.contentType).trim();
379
+ return {
380
+ path,
381
+ name: toStringValue3(value.name).trim() || filenameFromPath(path),
382
+ size: toNumberValue(value.size),
383
+ lastModified: toStringValue3(value.lastModified).trim(),
384
+ ...contentType ? { contentType } : {}
385
+ };
386
+ }
387
+ function isOrganizationDocument(document) {
388
+ return document !== void 0;
389
+ }
390
+ function filenameFromHeaders(headers) {
391
+ const disposition = headers.get("content-disposition");
392
+ if (!disposition) return void 0;
393
+ const utf8 = /filename\*=UTF-8''([^;]+)/i.exec(disposition);
394
+ if (utf8?.[1]) return decodeURIComponent(utf8[1]);
395
+ const quoted = /filename="([^"]+)"/i.exec(disposition);
396
+ if (quoted?.[1]) return quoted[1];
397
+ const plain = /filename=([^;]+)/i.exec(disposition);
398
+ return plain?.[1]?.trim();
399
+ }
400
+ function filenameFromPath(path) {
401
+ return path.replace(/\\/g, "/").split("/").filter(Boolean).pop() || path;
402
+ }
403
+ function toStringValue3(value) {
404
+ return typeof value === "string" ? value : "";
405
+ }
406
+ function toNumberValue(value) {
407
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
408
+ }
409
+
410
+ // src/sdk/polling/poll.ts
335
411
  var TERMINAL = /* @__PURE__ */ new Set(["Completed", "Failed"]);
336
412
  var defaultSleep = (ms) => new Promise((r) => setTimeout(r, ms));
337
413
  async function pollUntilTerminal(opts) {
338
414
  const sleep = opts.sleep ?? defaultSleep;
339
415
  const start = Date.now();
340
416
  const elapsed = opts.elapsed ?? (() => Date.now() - start);
417
+ const random = opts.random ?? Math.random;
418
+ let fallbackAttempt = 0;
341
419
  for (; ; ) {
342
420
  if (opts.signal?.aborted) throw new DocmanaAbortError("Polling aborted");
343
- const status = await opts.check();
421
+ let result;
422
+ try {
423
+ result = normalizePollResult(await opts.check());
424
+ } catch (err) {
425
+ if (!(err instanceof DocmanaRateLimitError)) throw err;
426
+ if (elapsed() >= opts.timeoutMs) {
427
+ throw new DocmanaTimeoutError(`Execution did not finish within ${opts.timeoutMs}ms`);
428
+ }
429
+ await sleep(err.retryAfterMs ?? opts.intervalMs);
430
+ continue;
431
+ }
432
+ const { status } = result;
344
433
  if (TERMINAL.has(status)) return status;
345
434
  if (elapsed() >= opts.timeoutMs) {
346
435
  throw new DocmanaTimeoutError(`Execution did not finish within ${opts.timeoutMs}ms`);
347
436
  }
348
- await sleep(opts.intervalMs);
437
+ const fallbackDelay = fullJitterDelay(
438
+ opts.intervalMs,
439
+ opts.maxIntervalMs,
440
+ fallbackAttempt,
441
+ random
442
+ );
443
+ fallbackAttempt += 1;
444
+ await sleep(result.nextDelayMs ?? fallbackDelay);
349
445
  }
350
446
  }
351
-
352
- // src/mappers/mapper.ts
353
- function extractNodeFields(rawNode, targetLanguage) {
354
- const metadataFields = /* @__PURE__ */ new Set([
355
- "node_id",
356
- "node_name",
357
- "node_type",
358
- "feature_name",
359
- "running_time",
360
- "status",
361
- "score",
362
- "feedback",
363
- "translations",
364
- "tokens",
365
- "input_tokens",
366
- "output_tokens",
367
- "errors",
368
- "mana",
369
- "model",
370
- "tool_calls",
371
- "prompt_id",
372
- "prompt",
373
- "prompt_name",
374
- "label",
375
- "scoreDescription",
376
- "score_description",
377
- "result",
378
- "documentType"
379
- ]);
380
- let sourceObj = rawNode;
381
- if (targetLanguage) {
382
- const translations = rawNode.translations;
383
- if (!translations || !translations[targetLanguage]) {
384
- throw new DocmanaError(
385
- `Translation for language '${targetLanguage}' is missing on node '${rawNode.node_name || rawNode.node_id}'`,
386
- { code: "translation_missing", status: 400 }
387
- );
388
- }
389
- sourceObj = translations[targetLanguage];
390
- }
391
- const dynamicFields = {};
392
- for (const [key, value] of Object.entries(sourceObj)) {
393
- if (!metadataFields.has(key)) {
394
- dynamicFields[key] = value;
395
- }
396
- }
397
- const sdkNode = {
398
- status: rawNode.status || "Unknown"
399
- };
400
- if (rawNode.errors && rawNode.errors.length > 0) {
401
- sdkNode.errors = rawNode.errors;
402
- }
403
- const nodeType = rawNode.node_type || "";
404
- const isScoreNode = /^(Validation|Classification|CrossValidation|Conclusion|Finish)$/i.test(
405
- nodeType
406
- );
407
- if (isScoreNode && rawNode.score !== void 0 && rawNode.score !== null) {
408
- sdkNode.score = rawNode.score;
409
- }
410
- const feedback = sourceObj.feedback ?? rawNode.feedback;
411
- if (feedback !== void 0 && feedback !== null) {
412
- sdkNode.feedback = feedback;
413
- }
414
- if (Object.keys(dynamicFields).length > 0) {
415
- sdkNode.data = dynamicFields;
416
- }
417
- return sdkNode;
447
+ function normalizePollResult(result) {
448
+ return typeof result === "string" ? { status: result } : result;
418
449
  }
419
- function mapExecutionResult(raw, targetLanguage) {
420
- const sdkResult = {
421
- status: raw.status || "Unknown",
422
- executionId: String(raw.execution_id || raw.executionResultId || raw.execution_result_id || ""),
423
- documents: {
424
- mappings: []
425
- }
426
- };
427
- if (raw.errors && raw.errors.length > 0) {
428
- sdkResult.errors = raw.errors;
429
- }
430
- const results = raw.results || [];
431
- for (const item of results) {
432
- const isDocSpecific = !!item.document;
433
- if (isDocSpecific && item.document) {
434
- const docReq = item.document;
435
- const docKey = docReq.doc_key || "unknown_doc";
436
- const docName = docReq.name || "";
437
- sdkResult.documents.mappings.push({
438
- name: docName,
439
- reference: docKey
440
- });
441
- const docResult = {
442
- status: item.status || docReq.status || "Unknown",
443
- extractions: {},
444
- validations: {}
445
- };
446
- const nodeResults = item.node_results || [];
447
- for (const node of nodeResults) {
448
- const nodeType = node.node_type || "";
449
- const nodeName = node.node_name || node.node_id || "unknown_node";
450
- if (/^Classification$/i.test(nodeType)) {
451
- const mappedNode = extractNodeFields(node, targetLanguage);
452
- if (!docResult.classifications) {
453
- docResult.classifications = {};
454
- }
455
- docResult.classifications[nodeName] = mappedNode;
456
- } else if (/^MetadataExtraction$/i.test(nodeType)) {
457
- docResult.metadataExtraction = extractNodeFields(node, targetLanguage);
458
- } else if (/^Extraction$/i.test(nodeType)) {
459
- if (docResult.extractions) {
460
- docResult.extractions[nodeName] = extractNodeFields(node, targetLanguage);
461
- }
462
- } else if (/^Validation$/i.test(nodeType)) {
463
- if (docResult.validations) {
464
- docResult.validations[nodeName] = extractNodeFields(node, targetLanguage);
465
- }
466
- }
467
- }
468
- if (docResult.extractions && Object.keys(docResult.extractions).length === 0) {
469
- delete docResult.extractions;
470
- }
471
- if (docResult.validations && Object.keys(docResult.validations).length === 0) {
472
- delete docResult.validations;
473
- }
474
- sdkResult.documents[docKey] = docResult;
475
- } else {
476
- const nodeResults = item.node_results || [];
477
- for (const node of nodeResults) {
478
- const nodeType = node.node_type || "";
479
- const nodeName = node.node_name || node.node_id || "unknown_node";
480
- if (/^CrossValidation$/i.test(nodeType)) {
481
- if (!sdkResult.crossValidations) {
482
- sdkResult.crossValidations = {};
483
- }
484
- sdkResult.crossValidations[nodeName] = extractNodeFields(
485
- node,
486
- targetLanguage
487
- );
488
- } else if (/^(Conclusion|Finish)$/i.test(nodeType)) {
489
- if (!sdkResult.conclusions) {
490
- sdkResult.conclusions = {};
491
- }
492
- sdkResult.conclusions[nodeName] = extractNodeFields(
493
- node,
494
- targetLanguage
495
- );
496
- }
497
- }
498
- }
499
- }
500
- return sdkResult;
450
+ function fullJitterDelay(intervalMs, maxIntervalMs, attempt, random) {
451
+ const capDelay = Math.min(maxIntervalMs, intervalMs * 2 ** attempt);
452
+ return Math.round(intervalMs + random() * (capDelay - intervalMs));
501
453
  }
502
454
 
503
- // src/client.ts
455
+ // src/sdk/client.ts
504
456
  var Docmana = class {
505
457
  config;
506
458
  http;
507
459
  constructor(config) {
508
460
  this.config = resolveConfig(config);
509
- const tokenManager = new TokenManager({
510
- clientId: this.config.clientId,
511
- clientSecret: this.config.clientSecret,
512
- tokenEndpoint: this.config.tokenEndpoint,
513
- scope: this.config.scope,
514
- fetchImpl: this.config.fetchImpl,
515
- tokenCache: this.config.tokenCache
516
- });
517
461
  this.http = new HttpClient({
518
462
  apiBaseUrl: this.config.apiBaseUrl,
519
463
  fetchImpl: this.config.fetchImpl,
520
- tokenManager,
464
+ accessToken: this.config.accessToken,
465
+ getAccessToken: this.config.getAccessToken,
521
466
  headers: this.config.headers
522
467
  });
523
468
  }
524
- async runFlowAsync(flowId, input) {
525
- const parts = await resolveInputs(input);
526
- const fileIds = await uploadFiles(this.http, parts, input.signal);
527
- const executionResultId = await runFlow(
469
+ async runFlowAsync(flowId, params, options = {}) {
470
+ const parts = await resolveInputs(params);
471
+ return runFlow(
528
472
  this.http,
529
473
  flowId,
530
- fileIds,
531
- input.signal,
532
- input.once ?? false,
533
- input.context
474
+ parts,
475
+ options.signal,
476
+ params.useDraftVersion ?? false,
477
+ params.context,
478
+ params.callbackUrl,
479
+ params.language
534
480
  );
535
- return { executionResultId, fileIds };
536
- }
537
- async runFlowWithCallback(flowId, input) {
538
- const parts = await resolveInputs(input);
539
- const fileIds = await uploadFiles(this.http, parts, input.signal);
540
- const executionResultId = await runFlow(
541
- this.http,
542
- flowId,
543
- fileIds,
544
- input.signal,
545
- input.once ?? false,
546
- input.context,
547
- input.callbackUrl
548
- );
549
- return { executionResultId, fileIds };
550
481
  }
551
482
  async getStatus(executionResultId, signal) {
552
483
  return getStatus(this.http, executionResultId, signal);
553
484
  }
554
485
  async getResult(executionResultId, signal, language) {
555
- const rawResult = await getResult(this.http, executionResultId, signal);
556
- return mapExecutionResult(rawResult, language);
486
+ return getResult(this.http, executionResultId, signal, language);
557
487
  }
558
- async runFlow(flowId, input) {
559
- const { executionResultId } = await this.runFlowAsync(flowId, input);
488
+ async runFlow(flowId, params, options = {}) {
489
+ const { executionResultId } = await this.runFlowAsync(flowId, params, {
490
+ signal: options.signal
491
+ });
492
+ let attempt = 0;
493
+ const startedAt = Date.now();
560
494
  await pollUntilTerminal({
561
- check: async () => (await this.getStatus(executionResultId, input.signal)).status,
562
- intervalMs: input.pollIntervalMs ?? this.config.pollIntervalMs,
563
- timeoutMs: input.timeoutMs ?? this.config.timeoutMs,
564
- signal: input.signal
495
+ check: async () => {
496
+ attempt += 1;
497
+ try {
498
+ const statusResult = await this.getStatus(executionResultId, options.signal);
499
+ const status = statusResult.status;
500
+ const nextPollAfterMs = this.isTerminalStatus(status) ? void 0 : statusResult.pollAfterMs;
501
+ options.onPoll?.({
502
+ executionResultId,
503
+ status,
504
+ attempt,
505
+ elapsedMs: Date.now() - startedAt,
506
+ nextPollAfterMs
507
+ });
508
+ return { status, nextDelayMs: nextPollAfterMs };
509
+ } catch (err) {
510
+ if (err instanceof DocmanaRateLimitError) {
511
+ options.onRateLimit?.({
512
+ executionResultId,
513
+ attempt,
514
+ elapsedMs: Date.now() - startedAt,
515
+ retryAfterMs: err.retryAfterMs ?? options.pollIntervalMs ?? this.config.pollIntervalMs
516
+ });
517
+ }
518
+ throw err;
519
+ }
520
+ },
521
+ intervalMs: options.pollIntervalMs ?? this.config.pollIntervalMs,
522
+ maxIntervalMs: options.pollMaxIntervalMs ?? this.config.pollMaxIntervalMs,
523
+ timeoutMs: options.timeoutMs ?? this.config.timeoutMs,
524
+ signal: options.signal
565
525
  });
566
- const result = await this.getResult(executionResultId, input.signal, input.language);
526
+ const result = await this.getResult(executionResultId, options.signal, params.language);
567
527
  if (result.status === "Failed") {
568
528
  throw new DocmanaExecutionError(
569
529
  `Flow ${flowId} execution failed`,
@@ -573,6 +533,30 @@ var Docmana = class {
573
533
  }
574
534
  return result;
575
535
  }
536
+ async listFlows() {
537
+ return listFlows(this.http);
538
+ }
539
+ async listOrganizations() {
540
+ return listOrganizations(this.http);
541
+ }
542
+ async getFlowMetadata(flowId) {
543
+ return getFlowMetadata(this.http, flowId);
544
+ }
545
+ async getFlowDefinition(flowId) {
546
+ return getFlowDefinition(this.http, flowId);
547
+ }
548
+ async listOrganizationDocuments(options = {}) {
549
+ return listOrganizationDocuments(this.http, options);
550
+ }
551
+ async downloadOrganizationDocument(path) {
552
+ return downloadOrganizationDocument(this.http, path);
553
+ }
554
+ async downloadOrganizationDocuments(paths) {
555
+ return downloadOrganizationDocuments(this.http, paths);
556
+ }
557
+ isTerminalStatus(status) {
558
+ return status === "Completed" || status === "Failed";
559
+ }
576
560
  };
577
561
  export {
578
562
  Docmana,