@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.
- package/CHANGELOG.md +33 -4
- package/README.md +175 -232
- package/dist/api/execution-result.d.ts +3 -0
- package/dist/api/execution-status.d.ts +6 -0
- package/dist/api/flow-definition.d.ts +3 -0
- package/dist/api/flow-metadata.d.ts +3 -0
- package/dist/api/list-flows.d.ts +3 -0
- package/dist/api/list-organizations.d.ts +3 -0
- package/dist/api/organization-documents.d.ts +7 -0
- package/dist/api/run-flow.d.ts +6 -0
- package/dist/cli.mjs +1022 -695
- package/dist/cli.mjs.map +1 -1
- package/dist/client.d.ts +26 -0
- package/dist/config.d.ts +21 -0
- package/dist/errors.d.ts +55 -0
- package/dist/files/resolve-input.d.ts +9 -0
- package/dist/http/http-client.d.ts +31 -0
- package/dist/index.d.ts +4 -349
- package/dist/index.js +323 -339
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +319 -335
- package/dist/index.mjs.map +1 -1
- package/dist/models/docmana-api-document-execution-result.model.d.ts +9 -0
- package/dist/models/docmana-api-document-request.model.d.ts +15 -0
- package/dist/models/docmana-api-execution-result.model.d.ts +24 -0
- package/dist/models/docmana-api-node-result.model.d.ts +28 -0
- package/dist/models/docmana-classification-result.model.d.ts +3 -0
- package/dist/models/docmana-conclusion-result.model.d.ts +3 -0
- package/dist/models/docmana-cross-validation-result.model.d.ts +3 -0
- package/dist/models/docmana-document-mapping.model.d.ts +4 -0
- package/dist/models/docmana-document-result.model.d.ts +11 -0
- package/dist/models/docmana-execution-result.model.d.ts +15 -0
- package/dist/models/docmana-extraction-result.model.d.ts +3 -0
- package/dist/models/docmana-metadata-extraction-result.model.d.ts +3 -0
- package/dist/models/docmana-node-result.model.d.ts +7 -0
- package/dist/models/docmana-score-node-result.model.d.ts +5 -0
- package/dist/models/docmana-validation-result.model.d.ts +3 -0
- package/dist/models/document-content.model.d.ts +4 -0
- package/dist/models/execution-progress.model.d.ts +4 -0
- package/dist/models/execution-status.enum.d.ts +1 -0
- package/dist/models/flow-configs.model.d.ts +16 -0
- package/dist/models/flow-edge.model.d.ts +4 -0
- package/dist/models/flow-node-type.enum.d.ts +1 -0
- package/dist/models/flow-node.model.d.ts +33 -0
- package/dist/models/flow.model.d.ts +11 -0
- package/dist/models/index.d.ts +26 -0
- package/dist/models/node-translation-result.model.d.ts +7 -0
- package/dist/models/physical-document.model.d.ts +9 -0
- package/dist/models/virtual-document.model.d.ts +10 -0
- package/dist/polling/poll.d.ts +15 -0
- package/dist/types.d.ts +145 -0
- package/package.json +17 -14
- 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.
|
|
11
|
-
if (!config.
|
|
12
|
-
|
|
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
|
-
|
|
18
|
-
|
|
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
|
-
|
|
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/
|
|
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.
|
|
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(
|
|
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.
|
|
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
|
-
|
|
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 (
|
|
283
|
-
const
|
|
243
|
+
if (isNamedFileInput(input)) {
|
|
244
|
+
const bytes = input.data instanceof Uint8Array ? input.data : await streamToBuffer(input.data);
|
|
284
245
|
return {
|
|
285
|
-
data: new Blob([
|
|
286
|
-
filename:
|
|
287
|
-
contentType: contentTypeFor(
|
|
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
|
|
294
|
-
if (list.length === 0) throw new Error("runFlow requires `
|
|
295
|
-
|
|
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/
|
|
300
|
-
async function
|
|
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
|
-
|
|
306
|
-
|
|
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/
|
|
310
|
-
async function
|
|
311
|
-
|
|
312
|
-
|
|
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/
|
|
325
|
-
async function
|
|
326
|
-
|
|
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/
|
|
330
|
-
async function
|
|
331
|
-
|
|
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/
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
420
|
-
const
|
|
421
|
-
|
|
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
|
-
|
|
464
|
+
accessToken: this.config.accessToken,
|
|
465
|
+
getAccessToken: this.config.getAccessToken,
|
|
521
466
|
headers: this.config.headers
|
|
522
467
|
});
|
|
523
468
|
}
|
|
524
|
-
async runFlowAsync(flowId,
|
|
525
|
-
const parts = await resolveInputs(
|
|
526
|
-
|
|
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
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
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
|
-
|
|
556
|
-
return mapExecutionResult(rawResult, language);
|
|
486
|
+
return getResult(this.http, executionResultId, signal, language);
|
|
557
487
|
}
|
|
558
|
-
async runFlow(flowId,
|
|
559
|
-
const { executionResultId } = await this.runFlowAsync(flowId,
|
|
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 () =>
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
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,
|
|
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,
|