@docmana/sdk 0.19.0 → 0.21.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 +29 -9
- package/README.md +61 -19
- package/dist/api/fraud-detection.d.ts +5 -0
- package/dist/cli.mjs +130 -2
- package/dist/cli.mjs.map +1 -1
- package/dist/client.d.ts +9 -1
- package/dist/errors.d.ts +7 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +132 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +131 -2
- package/dist/index.mjs.map +1 -1
- package/dist/polling/poll.d.ts +1 -0
- package/dist/types.d.ts +114 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,12 +1,32 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
## 0.
|
|
4
|
-
|
|
5
|
-
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.21.0 - 2026-08-05
|
|
4
|
+
|
|
5
|
+
- Relay the native Fraud job identifier through Consumer Edge while preserving
|
|
6
|
+
the existing high-level polling workflow.
|
|
7
|
+
- Add local file, named byte, and readable stream inputs to `detectFraud` and
|
|
8
|
+
`detectFraudAsync` through the existing `FileInput` contract.
|
|
9
|
+
- Stop sending organization correlation on Fraud requests. The deprecated
|
|
10
|
+
organization arguments remain accepted and ignored for source compatibility.
|
|
11
|
+
- Expose Consumer Edge's BI-friendly Fraud result contract: aggregate `summary`,
|
|
12
|
+
referenced `findings`, and detection `layers` keyed by their unique names.
|
|
13
|
+
|
|
14
|
+
## 0.20.0 - 2026-08-04
|
|
15
|
+
|
|
16
|
+
- Add `detectFraud` to start a Consumer Edge fraud-detection job, poll its
|
|
17
|
+
status, and return the normalized verdict and findings.
|
|
18
|
+
- Add `detectFraudAsync`, `getFraudDetectionStatus`, and
|
|
19
|
+
`getFraudDetectionResult` for callers that manage polling themselves.
|
|
20
|
+
- Respect `X-Poll-After` and `Retry-After`, retry transient status failures,
|
|
21
|
+
and expose typed failure, timeout, and cancellation errors.
|
|
22
|
+
|
|
23
|
+
## 0.19.0 - 2026-08-04
|
|
24
|
+
|
|
25
|
+
- Add synchronous and asynchronous V2 partial flow execution through Consumer Edge.
|
|
26
|
+
- Use `executionScope`, `flowVersion`, and `classificationNodeNames` in the
|
|
27
|
+
multipart body, leaving only `organization` in the query string.
|
|
28
|
+
- Return the standard `DocmanaExecutionResult` shape with optional
|
|
29
|
+
`partialExecution` metadata.
|
|
10
30
|
|
|
11
31
|
## 0.18.0
|
|
12
32
|
- Expose the optional aggregate `verdict` on `DocmanaExecutionResult`, preserving
|
package/README.md
CHANGED
|
@@ -32,6 +32,20 @@ const result = await docmana.runFlow("<flow-id>", {
|
|
|
32
32
|
console.log(result.status, result.executionId);
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
+
Fraud detection follows the same asynchronous Consumer Edge pattern. The
|
|
36
|
+
high-level call starts the job, polls Consumer, and returns the normalized
|
|
37
|
+
result:
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
const fraud = await docmana.detectFraud({
|
|
41
|
+
file: "./claim.pdf",
|
|
42
|
+
analystContext: "Travel claim; verify the invoice total.",
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
console.log(fraud.verdict, fraud.summary.highestSeverity);
|
|
46
|
+
console.log(fraud.layers.pixel?.findingReferences, fraud.findings);
|
|
47
|
+
```
|
|
48
|
+
|
|
35
49
|
## Authentication
|
|
36
50
|
|
|
37
51
|
The public SDK is Bearer-only. It does not acquire OAuth tokens and it never
|
|
@@ -53,16 +67,16 @@ static `accessToken`, `401` is returned as an auth error without retry.
|
|
|
53
67
|
|
|
54
68
|
## Configuration
|
|
55
69
|
|
|
56
|
-
| Field | Type | Required | Description
|
|
57
|
-
| ------------------- | --------------------------------------------------------------------- | -------- |
|
|
58
|
-
| `apiBaseUrl` | `string` | Yes | Base URL of Consumer API, including version when applicable.
|
|
59
|
-
| `accessToken` | `string` | One of | Static Bearer token sent to Consumer.
|
|
60
|
-
| `getAccessToken` | `(options?: { forceRefresh?: boolean }) => string \| Promise<string>` | One of | Token provider for renewable Bearer tokens.
|
|
61
|
-
| `timeoutMs` | `number` | No | Maximum polling time for `runFlow`. Default: 5 minutes.
|
|
62
|
-
| `pollIntervalMs` | `number` | No | Fallback minimum delay between status polls. Default: 2s.
|
|
63
|
-
| `pollMaxIntervalMs` | `number` | No | Fallback maximum polling delay. Default: 10s.
|
|
64
|
-
| `fetch` | `typeof fetch` | No | Custom fetch implementation.
|
|
65
|
-
| `headers` | `Record<string, string>` | No | Extra headers sent on every Consumer request.
|
|
70
|
+
| Field | Type | Required | Description |
|
|
71
|
+
| ------------------- | --------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------- |
|
|
72
|
+
| `apiBaseUrl` | `string` | Yes | Base URL of Consumer API, including version when applicable. |
|
|
73
|
+
| `accessToken` | `string` | One of | Static Bearer token sent to Consumer. |
|
|
74
|
+
| `getAccessToken` | `(options?: { forceRefresh?: boolean }) => string \| Promise<string>` | One of | Token provider for renewable Bearer tokens. |
|
|
75
|
+
| `timeoutMs` | `number` | No | Maximum polling time for `runFlow` and `detectFraud`. Default: 5 minutes. |
|
|
76
|
+
| `pollIntervalMs` | `number` | No | Fallback minimum delay between status polls. Default: 2s. |
|
|
77
|
+
| `pollMaxIntervalMs` | `number` | No | Fallback maximum polling delay. Default: 10s. |
|
|
78
|
+
| `fetch` | `typeof fetch` | No | Custom fetch implementation. |
|
|
79
|
+
| `headers` | `Record<string, string>` | No | Extra headers sent on every Consumer request. |
|
|
66
80
|
|
|
67
81
|
`apiBaseUrl` is required so a migrated SDK cannot accidentally call
|
|
68
82
|
`https://api.docmana.ai`.
|
|
@@ -128,12 +142,39 @@ use `RunFlowParams` for request data:
|
|
|
128
142
|
- `runFlow(flowId, params, options?)` starts async execution, polls status, fetches the
|
|
129
143
|
result, respects `X-Poll-After` and `429 Retry-After` while polling, and throws
|
|
130
144
|
`DocmanaExecutionError` if the result status is `FAILED`.
|
|
145
|
+
- `detectFraud(params, options?)` starts fraud detection from either `documentUrl`
|
|
146
|
+
plus `filename`, or a local `file` using the standard `FileInput` contract. It polls
|
|
147
|
+
Consumer until `COMPLETED` or `FAILED` and returns the normalized verdict, aggregate
|
|
148
|
+
`summary`, `findings` with job-local references, and detection `layers` keyed by name.
|
|
149
|
+
- `detectFraudAsync(params, options?)` starts fraud detection and immediately returns
|
|
150
|
+
`{ jobId, status }` without polling.
|
|
151
|
+
- `getFraudDetectionStatus(jobId, signal?)` and
|
|
152
|
+
`getFraudDetectionResult(jobId, signal?)` provide the lower-level polling workflow.
|
|
131
153
|
|
|
132
154
|
Consumer is the polling authority. When `GET /status` returns `X-Poll-After`,
|
|
133
155
|
the SDK waits that many milliseconds before the next status call. If the header
|
|
134
156
|
is absent, the SDK falls back to capped exponential full jitter between
|
|
135
157
|
`pollIntervalMs` and `pollMaxIntervalMs`. `Retry-After` on `429` always wins.
|
|
136
158
|
|
|
159
|
+
The SDK does not log or return `documentUrl` or uploaded file contents. Signed
|
|
160
|
+
query parameters are sent only in the JSON request body to Consumer Edge. Fraud
|
|
161
|
+
jobs are not organization-scoped; the legacy `organization` inputs are accepted
|
|
162
|
+
but ignored for compatibility with SDK 0.20 callers.
|
|
163
|
+
|
|
164
|
+
URL and local-file inputs use the same methods:
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
const fromUrl = await docmana.detectFraudAsync({
|
|
168
|
+
documentUrl: "https://downloads.example.com/claim.pdf?token=...",
|
|
169
|
+
filename: "claim.pdf",
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
const fromFile = await docmana.detectFraudAsync({
|
|
173
|
+
file: { path: "./claim.pdf" },
|
|
174
|
+
analystContext: "Check the invoice total.",
|
|
175
|
+
});
|
|
176
|
+
```
|
|
177
|
+
|
|
137
178
|
```ts
|
|
138
179
|
const partial = await docmana.runPartialFlow("<flow-id>", {
|
|
139
180
|
files: ["./contract.pdf"],
|
|
@@ -277,15 +318,16 @@ from the Bearer token.
|
|
|
277
318
|
Every SDK error extends `DocmanaError`, which carries `.status`, `.code`, and an
|
|
278
319
|
optional `.requestId` when available.
|
|
279
320
|
|
|
280
|
-
| Error
|
|
281
|
-
|
|
|
282
|
-
| `DocmanaRequestError`
|
|
283
|
-
| `DocmanaAuthError`
|
|
284
|
-
| `DocmanaPermissionError`
|
|
285
|
-
| `DocmanaNotFoundError`
|
|
286
|
-
| `DocmanaExecutionError`
|
|
287
|
-
| `
|
|
288
|
-
| `
|
|
321
|
+
| Error | Thrown when |
|
|
322
|
+
| ---------------------------- | ------------------------------------------------------------------------------------------- |
|
|
323
|
+
| `DocmanaRequestError` | `400` bad request. |
|
|
324
|
+
| `DocmanaAuthError` | `401` rejected or expired Bearer token. |
|
|
325
|
+
| `DocmanaPermissionError` | `403` missing permission. |
|
|
326
|
+
| `DocmanaNotFoundError` | `404` flow or execution not found. |
|
|
327
|
+
| `DocmanaExecutionError` | The flow finished with status `"FAILED"`. Carries `.errors` and `.result`. |
|
|
328
|
+
| `DocmanaFraudDetectionError` | Fraud detection finished with status `"FAILED"`. Carries `.jobId`, `.error`, and `.result`. |
|
|
329
|
+
| `DocmanaTimeoutError` | Polling exceeded `timeoutMs`. |
|
|
330
|
+
| `DocmanaAbortError` | The provided `AbortSignal` fired. |
|
|
289
331
|
|
|
290
332
|
## Local development with yalc
|
|
291
333
|
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { HttpClient } from "../http/http-client.js";
|
|
2
|
+
import type { FraudDetectionAccepted, FraudDetectionParams, FraudDetectionResult, FraudDetectionStatusResult } from "../types.js";
|
|
3
|
+
export declare function startFraudDetection(http: HttpClient, params: FraudDetectionParams, signal?: AbortSignal): Promise<FraudDetectionAccepted>;
|
|
4
|
+
export declare function getFraudDetectionStatus(http: HttpClient, jobId: string, signal?: AbortSignal, organization?: string): Promise<FraudDetectionStatusResult>;
|
|
5
|
+
export declare function getFraudDetectionResult(http: HttpClient, jobId: string, signal?: AbortSignal, organization?: string): Promise<FraudDetectionResult>;
|
package/dist/cli.mjs
CHANGED
|
@@ -79,6 +79,17 @@ var DocmanaExecutionError = class extends DocmanaError {
|
|
|
79
79
|
this.result = result;
|
|
80
80
|
}
|
|
81
81
|
};
|
|
82
|
+
var DocmanaFraudDetectionError = class extends DocmanaError {
|
|
83
|
+
jobId;
|
|
84
|
+
error;
|
|
85
|
+
result;
|
|
86
|
+
constructor(message, result) {
|
|
87
|
+
super(message, { code: "fraud_detection_failed" });
|
|
88
|
+
this.jobId = result.jobId;
|
|
89
|
+
this.error = result.error;
|
|
90
|
+
this.result = result;
|
|
91
|
+
}
|
|
92
|
+
};
|
|
82
93
|
var DocmanaTimeoutError = class extends DocmanaError {
|
|
83
94
|
constructor(message) {
|
|
84
95
|
super(message, { code: "timeout" });
|
|
@@ -366,6 +377,60 @@ async function getResult(http, executionResultId, signal, language, organization
|
|
|
366
377
|
});
|
|
367
378
|
}
|
|
368
379
|
|
|
380
|
+
// src/sdk/api/fraud-detection.ts
|
|
381
|
+
async function startFraudDetection(http, params, signal) {
|
|
382
|
+
if (params.file !== void 0) {
|
|
383
|
+
const [part] = await resolveInputs({ files: [params.file] });
|
|
384
|
+
const form = new FormData();
|
|
385
|
+
form.append("file", new File([part.data], part.filename, { type: part.contentType }));
|
|
386
|
+
if (params.analystContext !== void 0) {
|
|
387
|
+
form.append("analystContext", params.analystContext);
|
|
388
|
+
}
|
|
389
|
+
return http.requestJson("POST", "/fraud-detections", {
|
|
390
|
+
body: form,
|
|
391
|
+
signal
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
return http.requestJson("POST", "/fraud-detections", {
|
|
395
|
+
headers: { "content-type": "application/json" },
|
|
396
|
+
body: JSON.stringify({
|
|
397
|
+
documentUrl: params.documentUrl,
|
|
398
|
+
filename: params.filename,
|
|
399
|
+
...params.analystContext === void 0 ? {} : { analystContext: params.analystContext }
|
|
400
|
+
}),
|
|
401
|
+
signal
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
async function getFraudDetectionStatus(http, jobId, signal, organization) {
|
|
405
|
+
void organization;
|
|
406
|
+
const res = await http.requestRaw(
|
|
407
|
+
"GET",
|
|
408
|
+
`/fraud-detections/${encodeURIComponent(jobId)}/status`,
|
|
409
|
+
{
|
|
410
|
+
signal
|
|
411
|
+
}
|
|
412
|
+
);
|
|
413
|
+
const text = await res.text();
|
|
414
|
+
const body = text ? JSON.parse(text) : { jobId, status: "" };
|
|
415
|
+
const normalized = {
|
|
416
|
+
...body,
|
|
417
|
+
status: normalizeStatus(body.status)
|
|
418
|
+
};
|
|
419
|
+
const pollAfterMs = parsePositiveInt2(res.headers.get("x-poll-after"));
|
|
420
|
+
return pollAfterMs === void 0 ? normalized : { ...normalized, pollAfterMs };
|
|
421
|
+
}
|
|
422
|
+
async function getFraudDetectionResult(http, jobId, signal, organization) {
|
|
423
|
+
void organization;
|
|
424
|
+
return http.requestJson("GET", `/fraud-detections/${encodeURIComponent(jobId)}/result`, {
|
|
425
|
+
signal
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
function parsePositiveInt2(value) {
|
|
429
|
+
if (!value) return void 0;
|
|
430
|
+
const parsed = Number(value);
|
|
431
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
|
|
432
|
+
}
|
|
433
|
+
|
|
369
434
|
// src/sdk/api/list-flows.ts
|
|
370
435
|
async function listFlows(http, organization) {
|
|
371
436
|
const path = organization ? `/flows?organization=${encodeURIComponent(organization)}` : "/flows";
|
|
@@ -510,11 +575,13 @@ async function pollUntilTerminal(opts) {
|
|
|
510
575
|
try {
|
|
511
576
|
result = normalizePollResult(await opts.check());
|
|
512
577
|
} catch (err) {
|
|
513
|
-
|
|
578
|
+
const retryAfterMs = err instanceof DocmanaRateLimitError ? err.retryAfterMs ?? opts.intervalMs : opts.shouldRetryError?.(err) ? fullJitterDelay(opts.intervalMs, opts.maxIntervalMs, fallbackAttempt, random) : void 0;
|
|
579
|
+
if (retryAfterMs === void 0) throw err;
|
|
514
580
|
if (elapsed() >= opts.timeoutMs) {
|
|
515
581
|
throw new DocmanaTimeoutError(`Execution did not finish within ${opts.timeoutMs}ms`);
|
|
516
582
|
}
|
|
517
|
-
|
|
583
|
+
fallbackAttempt += 1;
|
|
584
|
+
await sleep(retryAfterMs);
|
|
518
585
|
continue;
|
|
519
586
|
}
|
|
520
587
|
const status = normalizeStatus(result.status);
|
|
@@ -568,6 +635,60 @@ var Docmana = class {
|
|
|
568
635
|
params.organization
|
|
569
636
|
);
|
|
570
637
|
}
|
|
638
|
+
async detectFraudAsync(params, options = {}) {
|
|
639
|
+
return startFraudDetection(this.http, params, options.signal);
|
|
640
|
+
}
|
|
641
|
+
async getFraudDetectionStatus(jobId, signal, organization) {
|
|
642
|
+
return getFraudDetectionStatus(this.http, jobId, signal, organization);
|
|
643
|
+
}
|
|
644
|
+
async getFraudDetectionResult(jobId, signal, organization) {
|
|
645
|
+
return getFraudDetectionResult(this.http, jobId, signal, organization);
|
|
646
|
+
}
|
|
647
|
+
async detectFraud(params, options = {}) {
|
|
648
|
+
const { jobId } = await this.detectFraudAsync(params, { signal: options.signal });
|
|
649
|
+
let attempt = 0;
|
|
650
|
+
const startedAt = Date.now();
|
|
651
|
+
await pollUntilTerminal({
|
|
652
|
+
check: async () => {
|
|
653
|
+
attempt += 1;
|
|
654
|
+
try {
|
|
655
|
+
const statusResult = await this.getFraudDetectionStatus(jobId, options.signal);
|
|
656
|
+
const nextPollAfterMs = isTerminalStatus(statusResult.status) ? void 0 : statusResult.pollAfterMs;
|
|
657
|
+
options.onPoll?.({
|
|
658
|
+
jobId,
|
|
659
|
+
status: statusResult.status,
|
|
660
|
+
attempt,
|
|
661
|
+
elapsedMs: Date.now() - startedAt,
|
|
662
|
+
nextPollAfterMs
|
|
663
|
+
});
|
|
664
|
+
return { status: statusResult.status, nextDelayMs: nextPollAfterMs };
|
|
665
|
+
} catch (err) {
|
|
666
|
+
if (err instanceof DocmanaRateLimitError) {
|
|
667
|
+
options.onRateLimit?.({
|
|
668
|
+
jobId,
|
|
669
|
+
attempt,
|
|
670
|
+
elapsedMs: Date.now() - startedAt,
|
|
671
|
+
retryAfterMs: err.retryAfterMs ?? options.pollIntervalMs ?? this.config.pollIntervalMs
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
throw err;
|
|
675
|
+
}
|
|
676
|
+
},
|
|
677
|
+
intervalMs: options.pollIntervalMs ?? this.config.pollIntervalMs,
|
|
678
|
+
maxIntervalMs: options.pollMaxIntervalMs ?? this.config.pollMaxIntervalMs,
|
|
679
|
+
timeoutMs: options.timeoutMs ?? this.config.timeoutMs,
|
|
680
|
+
signal: options.signal,
|
|
681
|
+
shouldRetryError: isTransientPollingError
|
|
682
|
+
});
|
|
683
|
+
const result = await this.getFraudDetectionResult(jobId, options.signal);
|
|
684
|
+
if (isFailedStatus(result.status)) {
|
|
685
|
+
throw new DocmanaFraudDetectionError(
|
|
686
|
+
result.error?.message ?? `Fraud detection ${jobId} failed`,
|
|
687
|
+
result
|
|
688
|
+
);
|
|
689
|
+
}
|
|
690
|
+
return result;
|
|
691
|
+
}
|
|
571
692
|
async runPartialFlow(flowId, params, options = {}) {
|
|
572
693
|
const parts = await resolveInputs(params);
|
|
573
694
|
return runPartialFlow(this.http, flowId, parts, params, options.signal);
|
|
@@ -664,6 +785,13 @@ var Docmana = class {
|
|
|
664
785
|
return isTerminalStatus(status);
|
|
665
786
|
}
|
|
666
787
|
};
|
|
788
|
+
function isTransientPollingError(error) {
|
|
789
|
+
if (error instanceof DocmanaRateLimitError) return false;
|
|
790
|
+
if (error instanceof DocmanaError) {
|
|
791
|
+
return error.status !== void 0 && error.status >= 500;
|
|
792
|
+
}
|
|
793
|
+
return error instanceof TypeError;
|
|
794
|
+
}
|
|
667
795
|
|
|
668
796
|
// src/cli/errors.ts
|
|
669
797
|
var CliUsageError = class extends Error {
|