@docmana/sdk 0.32.0 → 0.36.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 +60 -0
- package/LICENSE +21 -21
- package/README.md +183 -0
- package/dist/cli.mjs +265 -1
- package/dist/cli.mjs.map +1 -1
- package/dist/client.d.ts +4 -0
- package/dist/errors.d.ts +17 -0
- package/dist/execution-events.d.ts +47 -0
- package/dist/http/http-client.d.ts +2 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +327 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +317 -2
- package/dist/index.mjs.map +1 -1
- package/dist/models/execution-status.enum.d.ts +1 -1
- package/dist/review-events.d.ts +36 -0
- package/dist/reviews.d.ts +188 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,61 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.36.0 - 2026-10-30
|
|
4
|
+
|
|
5
|
+
- Promote the additive review and execution-event client APIs to a stable release.
|
|
6
|
+
Unused review APIs require no additional backend configuration.
|
|
7
|
+
- Align native pending-list envelopes (documents are read on task detail), and
|
|
8
|
+
support confirmations, correction reasons and human review duration.
|
|
9
|
+
- Preserve DOC-2558 review conflict metadata through Consumer Edge: `decidedBy`,
|
|
10
|
+
`decidedAt` and `currentVersion`, with the exported `ReviewConflictDetails` type.
|
|
11
|
+
Keep deprecated legacy aliases and bounded metadata; decision conflicts are
|
|
12
|
+
never retried automatically. Normal executions and final callbacks are unchanged.
|
|
13
|
+
|
|
14
|
+
## 0.36.0-hitl.2 - 2026-10-23 (HITL prerelease)
|
|
15
|
+
|
|
16
|
+
- Add `executionEvents.list()` and typed, non-consuming sequence-paged node
|
|
17
|
+
history for formulas with or without human review. Add the expanded
|
|
18
|
+
`verifyExecutionEventCallback` / `parseExecutionEventCallback` while retaining
|
|
19
|
+
the existing strict review-only helpers and final callback behavior.
|
|
20
|
+
- Publish pilot versions under the explicit `hitl` npm distribution tag;
|
|
21
|
+
other prereleases use `next` and stable releases use `latest`.
|
|
22
|
+
|
|
23
|
+
## 0.36.0-hitl.1 - 2026-10-23 (local POC)
|
|
24
|
+
|
|
25
|
+
- Add `reviews.calculation()` and exported `ReviewCalculation` for non-consuming
|
|
26
|
+
provisional calculation state, coherent/target revisions, review counts and
|
|
27
|
+
per-revision node reuse. Preserve legacy null metadata, organization scope,
|
|
28
|
+
versioned decisions, assignment and final callback behavior.
|
|
29
|
+
|
|
30
|
+
## 0.35.0-hitl.3 - 2026-10-22 (local POC)
|
|
31
|
+
|
|
32
|
+
- Add optional `ReviewTask.findings` and exported `ReviewFinding` for explicit,
|
|
33
|
+
review-scoped field signals. Preserve missing/empty findings and existing
|
|
34
|
+
corrections, assignment, global confirmation and final callbacks.
|
|
35
|
+
|
|
36
|
+
## 0.35.0-hitl.2 - 2026-10-22 (local POC)
|
|
37
|
+
|
|
38
|
+
- Add optional read-only frozen review assessments (node score, description, feedback, explanation and reasoning). No per-field findings or decision/callback change.
|
|
39
|
+
|
|
40
|
+
## 0.35.0-hitl.1 - 2026-10-22 (local POC)
|
|
41
|
+
|
|
42
|
+
- Preserve optional native review lifecycle timestamps for Live's human-wait and activity display. No final result fetch, pipeline or callback contract change.
|
|
43
|
+
|
|
44
|
+
## 0.34.0 - 2026-10-22
|
|
45
|
+
|
|
46
|
+
- Add `reviews.document(id, docKey, "preview", options)` and exported
|
|
47
|
+
`DocumentPreviewManifest` for progressive import-time WebP preparation.
|
|
48
|
+
Existing document streams, review decisions and final callbacks are unchanged.
|
|
49
|
+
|
|
50
|
+
## 0.33.0 - 2026-10-21
|
|
51
|
+
|
|
52
|
+
- Add typed review event callbacks and raw-body HMAC signature verification for
|
|
53
|
+
Consumer's separate review event relay. Existing execution callbacks are unchanged.
|
|
54
|
+
- Add `Docmana.reviews` for organization-scoped queues, task details, versioned
|
|
55
|
+
decisions and streaming page/content/location reads. Preserve range/cache
|
|
56
|
+
statuses and safe conflict metadata; never automatically retry decisions.
|
|
57
|
+
- Expose `AWAITING_REVIEW` as a non-terminal execution status.
|
|
58
|
+
|
|
3
59
|
## Unreleased
|
|
4
60
|
|
|
5
61
|
## 0.32.0 - 2026-10-19
|
|
@@ -257,3 +313,7 @@ consu## 0.16.0
|
|
|
257
313
|
- Typed error hierarchy (`DocmanaError`, `DocmanaExecutionError`, …).
|
|
258
314
|
- Flexible file inputs: path, `Buffer`, `Stream`, and multi-file uploads.
|
|
259
315
|
- Dual ESM + CJS distribution with bundled type definitions.
|
|
316
|
+
|
|
317
|
+
### Local HITL outline addition
|
|
318
|
+
|
|
319
|
+
- Add reviews.outline with typed published formula steps, edges and review marks. Existing execution, decision and final callback contracts are unchanged.
|
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Docmana
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Docmana
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,5 +1,68 @@
|
|
|
1
1
|
# @docmana/sdk
|
|
2
2
|
|
|
3
|
+
The HITL pilot uses the explicit prerelease `0.36.0-hitl.2` and the npm `hitl`
|
|
4
|
+
distribution tag. Install the exact version for reproducible pilot builds:
|
|
5
|
+
|
|
6
|
+
```sh
|
|
7
|
+
npm install --save-exact @docmana/sdk@0.36.0-hitl.2
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
The stable default remains on `latest`; the prerelease does not select a native
|
|
11
|
+
API. Configure the SDK's Consumer base URL on the application server.
|
|
12
|
+
|
|
13
|
+
## Real formula node progress (local HITL pilot)
|
|
14
|
+
|
|
15
|
+
`sdk.executionEvents.list(executionId, { organization, after: 0, limit: 100, signal })`
|
|
16
|
+
reads a non-consuming metadata journal, including formulas without human review.
|
|
17
|
+
Follow `nextAfter` while non-null; order by `sequence`, deduplicate `eventId`, and
|
|
18
|
+
honor `truncated` when the cursor predates retained history. The native journal
|
|
19
|
+
keeps at most 2,000 events with the execution. No PDF or final-result retention
|
|
20
|
+
is added. Older APIs can return 404; show confirmed status without inventing
|
|
21
|
+
progress. Never turn a journal failure into a call to another backend.
|
|
22
|
+
|
|
23
|
+
The four new names are `execution.node.started`, `execution.node.completed`,
|
|
24
|
+
`execution.node.failed`, and `execution.node.reused`. Events identify a node,
|
|
25
|
+
document keys, revision and attempt. A reused output does not imply a new LLM
|
|
26
|
+
call. Completion of a node is technical completion, not a positive business
|
|
27
|
+
verdict or final execution completion. `occurredAt` is the real action time;
|
|
28
|
+
`publishedAt` is optional broker time, present on signed callbacks.
|
|
29
|
+
|
|
30
|
+
`verifyExecutionEventCallback` and `parseExecutionEventCallback` accept both the
|
|
31
|
+
new node metadata and the four existing review events. Existing review-only
|
|
32
|
+
verification helpers remain strict and unchanged. Final execution callbacks and
|
|
33
|
+
result ownership keep their existing contract; there are no pipeline events.
|
|
34
|
+
|
|
35
|
+
## Review event callbacks
|
|
36
|
+
|
|
37
|
+
SDK 0.33.0 adds server-side `verifyReviewEventCallback`, `parseReviewEventCallback`
|
|
38
|
+
and the `ReviewEventCallback` type for Consumer's opt-in review event relay.
|
|
39
|
+
This is independent from `runFlowAsync({ callbackUrl })`, which still receives only
|
|
40
|
+
the final result. No Service Bus connection is required in your application.
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { verifyReviewEventCallback } from "@docmana/sdk";
|
|
44
|
+
const event = verifyReviewEventCallback({
|
|
45
|
+
body: rawUtf8Body,
|
|
46
|
+
signature: request.headers.get("x-docmana-event-signature"),
|
|
47
|
+
timestamp: request.headers.get("x-docmana-event-timestamp"),
|
|
48
|
+
secret: serverConfiguredSecret, // at least 32 characters; never sent to a browser
|
|
49
|
+
organization: expectedOrganization,
|
|
50
|
+
});
|
|
51
|
+
await persistOnce(event.organization, event.eventId, event);
|
|
52
|
+
// Only now return 2xx. Never finalize an execution from a review notification.
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Supported types are `execution.awaiting_review`, `execution.review_done`,
|
|
56
|
+
`execution.review_closed` and `execution.review_expired`. `review_done` means the
|
|
57
|
+
decision is recorded, not that the execution is finished. `review_closed` also
|
|
58
|
+
describes the review rather than the execution. `publishedAt` is the broker's
|
|
59
|
+
publication time. The metadata excludes document names, values, notes and results.
|
|
60
|
+
Signatures use HMAC-SHA256 over `timestamp + "." + rawBody` with header `v1=<hex>`;
|
|
61
|
+
verification accepts a five-minute clock difference. Broker retries are re-signed
|
|
62
|
+
but retain the event ID; store it durably for deduplication. An event notification
|
|
63
|
+
never consumes the final result. Source publication is best-effort: reconcile
|
|
64
|
+
current review state separately after a missed notification.
|
|
65
|
+
|
|
3
66
|
Official Docmana SDK for calling `docmana-consumer-edge-api`.
|
|
4
67
|
|
|
5
68
|
SDK 0.32.0 adds native formula authoring, assisted conversations and revision-pinned
|
|
@@ -18,6 +81,17 @@ status, and returns the mapped Consumer execution DTO.
|
|
|
18
81
|
npm install @docmana/sdk
|
|
19
82
|
```
|
|
20
83
|
|
|
84
|
+
### GitHub release recovery
|
|
85
|
+
|
|
86
|
+
The Release workflow publishes an existing `v<version>` tag. GitHub Actions are
|
|
87
|
+
pinned to full commit SHAs to satisfy the enterprise policy. If a tag-triggered
|
|
88
|
+
run fails before starting, merge the workflow correction first, then manually
|
|
89
|
+
run **Release** from `main` with that existing tag. The workflow checks out the
|
|
90
|
+
tag and verifies its package version before publishing; do not move a release
|
|
91
|
+
tag to repair a pipeline. Publication requires the existing npm publisher
|
|
92
|
+
credential or trusted-publishing configuration. Verify the version is available
|
|
93
|
+
in npm before updating a consumer's lockfile.
|
|
94
|
+
|
|
21
95
|
Requires Node.js 20+.
|
|
22
96
|
|
|
23
97
|
## Quickstart
|
|
@@ -450,6 +524,98 @@ user confirmation before submitting the same file with `confirmOverwrite: true`.
|
|
|
450
524
|
`remove(id, options)` permanently deletes the formula and its authoring data;
|
|
451
525
|
confirm that action with the user. Import and deletion do not use operation IDs.
|
|
452
526
|
|
|
527
|
+
## Human review of formula executions
|
|
528
|
+
|
|
529
|
+
The native pending queue omits documents: load the selected task with `reviews.get()`
|
|
530
|
+
before displaying its values and documents. Decided tasks leave this queue; resume
|
|
531
|
+
progress belongs to the execution. `ReviewDecision` also supports `confirmations`,
|
|
532
|
+
per-correction `reason`, and `durationSeconds`. Conflict details identify an already
|
|
533
|
+
submitted decision or the current version; no decision is retried automatically.
|
|
534
|
+
|
|
535
|
+
Read calculation metadata repeatedly with
|
|
536
|
+
`client.reviews.calculation(executionId, { organization })`. It returns
|
|
537
|
+
`executionResultId`, normalized `executionStatus` and `calculation` (null for
|
|
538
|
+
legacy/unmarked runs). Calculation includes `status`, coherent `revision`
|
|
539
|
+
(0 before the first calculation), optional `targetRevision`, `provisional`,
|
|
540
|
+
review counts and computed/reused node counts for that revision. Node counts
|
|
541
|
+
are not individual model-call counts. Calculation revision is distinct from
|
|
542
|
+
`ReviewTask.version`, which remains required for decisions. A completed
|
|
543
|
+
calculation stays provisional until the persisted execution is terminal.
|
|
544
|
+
This read is available before the first review and after final callback delivery;
|
|
545
|
+
it never consumes the single-use final result or triggers pipelines/callbacks.
|
|
546
|
+
|
|
547
|
+
Requires Consumer's review routes and the compatible HITL Document API branch.
|
|
548
|
+
No pipelines are required. Start published manual formulas with `runFlowAsync`;
|
|
549
|
+
`AWAITING_REVIEW` is non-terminal. `runFlow` still waits for final completion and
|
|
550
|
+
retains its configured timeout, so do not hold an HTTP request open for human work.
|
|
551
|
+
|
|
552
|
+
```ts
|
|
553
|
+
const scope = { organization: "your-organization", signal: controller.signal };
|
|
554
|
+
const { reviews, nextCursor } = await docmana.reviews.list({
|
|
555
|
+
...scope,
|
|
556
|
+
status: "pending",
|
|
557
|
+
limit: 50,
|
|
558
|
+
});
|
|
559
|
+
const task = await docmana.reviews.get(reviews[0].executionResultId, scope);
|
|
560
|
+
await docmana.reviews.decide(
|
|
561
|
+
task.executionResultId,
|
|
562
|
+
{
|
|
563
|
+
decision: "continue",
|
|
564
|
+
version: task.version,
|
|
565
|
+
reviewer: authenticatedServerActor, // Never trust an identity from the browser.
|
|
566
|
+
corrections: [{ itemId: task.documents[0].items![0].itemId, value: false }],
|
|
567
|
+
},
|
|
568
|
+
scope,
|
|
569
|
+
);
|
|
570
|
+
// Preparation starts at import. Poll queued/processing; other states are terminal.
|
|
571
|
+
const preparation = await docmana.reviews.document(
|
|
572
|
+
task.executionResultId,
|
|
573
|
+
task.documents[0].docKey,
|
|
574
|
+
"preview",
|
|
575
|
+
{
|
|
576
|
+
...scope,
|
|
577
|
+
version: task.reviewId,
|
|
578
|
+
},
|
|
579
|
+
);
|
|
580
|
+
const manifest = await preparation.json(); // DocumentPreviewManifest
|
|
581
|
+
// Only fetch numbers present in manifest.pages; page 1 precedes later batches.
|
|
582
|
+
// Original content remains available as the PDF fallback.
|
|
583
|
+
const page = await docmana.reviews.document(
|
|
584
|
+
task.executionResultId,
|
|
585
|
+
"doc01",
|
|
586
|
+
{ page: 1 },
|
|
587
|
+
{
|
|
588
|
+
...scope,
|
|
589
|
+
version: task.reviewId,
|
|
590
|
+
size: "thumb",
|
|
591
|
+
},
|
|
592
|
+
);
|
|
593
|
+
// page is a streaming Fetch Response. Consume or cancel its body; do not buffer a large PDF.
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
`list` accepts `pending`, `submitted`, `resuming` and an opaque `cursor`. It lists
|
|
597
|
+
open tasks only. The `"*"` dossier entry is not a file. Use the returned item IDs
|
|
598
|
+
unchanged; JSON values preserve `null`, `false`, `0`, objects and arrays.
|
|
599
|
+
`document` also accepts `"content"` or `"locations"`, Range/If-Range and ETag
|
|
600
|
+
options. It returns 200/206/304/416 without consuming the body.
|
|
601
|
+
|
|
602
|
+
A decision receipt means **recorded and resume queued**, never final completion.
|
|
603
|
+
On 409 inspect `DocmanaError.reviewConflict` and `apiCode`, then re-read the task.
|
|
604
|
+
`REVIEW_ALREADY_SUBMITTED` can include `reviewConflict.decidedBy` (`id`, `name`,
|
|
605
|
+
`source: "integrator" | "token"`) and `decidedAt`.
|
|
606
|
+
`REVIEW_VERSION_STALE` can include `reviewConflict.currentVersion`.
|
|
607
|
+
Missing metadata remains `null`; only bounded, whitelisted fields are retained.
|
|
608
|
+
The previous `version`, `submittedBy` and `submittedAt` properties remain as
|
|
609
|
+
deprecated compatibility aliases. Never replace the submitted version with the
|
|
610
|
+
new version and retry automatically: let the user inspect the refreshed task.
|
|
611
|
+
On 503 re-read first: the decision may already be recorded. The SDK does not retry
|
|
612
|
+
a decision after a conflict, 503 or uncertain transport outcome. The standard one-time
|
|
613
|
+
401 token-refresh retry still applies. Keep the existing final callback/polling result owner;
|
|
614
|
+
opening a review must not consume the single-use execution result.
|
|
615
|
+
|
|
616
|
+
Release order: compatible Document API, Consumer, published SDK, then Arcana's
|
|
617
|
+
exact pinned dependency. Preparing this change does not publish or deploy it.
|
|
618
|
+
|
|
453
619
|
## License
|
|
454
620
|
|
|
455
621
|
MIT
|
|
@@ -476,3 +642,20 @@ Use `authoring.trialDocument(fileBlob, filename, spec, { organization, signal })
|
|
|
476
642
|
For assisted Mongo nodes, call `authoring.documentTrialDefinitions({ flowId, expectedRevisions, node | allNodes }, { organization, signal })`. This read-only preparation pins active node-owned rules and settings to the coherent native snapshot, checking revisions again after resolution. Pass the returned definition and uploaded file to the server-side `runAssistedDocumentTrial` library in `@docmana/docmana-flows`. Git callers use Flows' `prepareDocumentTrial` directly. Neither path requires `/extract/test-assisted` or a modified DocMana API. Calling the old native trial method with assisted nodes now fails before upload with HTTP 422.
|
|
477
643
|
|
|
478
644
|
Flows owns assisted IDP/model calls and requires its server-side provider configuration. It analyzes the file once and tries up to 100 compatible classification/extraction nodes independently, retaining evidence envelopes. Files are nonempty PDF/images up to 20 MiB. Definitions and uploads use disposable local storage, cleaned after completion; no formula, rule, reference expectation or publication is saved. These trials do not execute production routing, translate results, compare expectations or produce release approval evidence. Missing upstream context is rejected. Native API accounting and PromptShield are not implied by the Flows executor. No new CLI command is introduced.
|
|
645
|
+
|
|
646
|
+
### Formula steps during human review
|
|
647
|
+
|
|
648
|
+
Task detail also preserves optional native `createdAt`, `submittedAt`, `resumingAt` and `closedAt` timestamps. Human waiting is bounded by task creation and native decision acceptance; review closure does not prove final execution completion. Older backends can omit these fields; clients must show an unavailable duration rather than infer it from callback delivery time.
|
|
649
|
+
|
|
650
|
+
Use client.reviews.outline(flowId, { organization }) to read the published nodes, edges, ordering and human-review marks through Consumer. Compare outline.version with the review flowVersion before displaying progress. This read never consumes an execution result and does not introduce pipelines.
|
|
651
|
+
|
|
652
|
+
### Frozen review evidence (local HITL pilot)
|
|
653
|
+
|
|
654
|
+
`reviews.get()` optionally returns `documents[].assessments`: the stored node score (0–100), `scoreDescription`, `feedback`, `explanation` and `reasoning` from the same frozen snapshot as the items. These are read-only node-level evidence, not uncertain-field findings. Older backends may omit assessments or scores; do not infer missing scores. Decisions still send only editable item corrections through Consumer.
|
|
655
|
+
|
|
656
|
+
`ReviewTask.findings?: ReviewFinding[]` separately carries explicit field signals:
|
|
657
|
+
stable review-scoped `id`, `code`, `message`, resolved `itemIds`, and optional
|
|
658
|
+
`source` (`model` or `rule`). Consumers must not infer fields from node scores or
|
|
659
|
+
explanation text. Missing or empty findings preserve the existing presentation.
|
|
660
|
+
These are frozen evidence, not per-field decisions; editing a value does not
|
|
661
|
+
erase its finding. Continue using the same global, versioned decision call.
|
package/dist/cli.mjs
CHANGED
|
@@ -171,6 +171,234 @@ var FormulaAuthoring = class {
|
|
|
171
171
|
}
|
|
172
172
|
};
|
|
173
173
|
|
|
174
|
+
// src/sdk/reviews.ts
|
|
175
|
+
function segment(value) {
|
|
176
|
+
if (!value || value === "." || value === ".." || /[\\/]/.test(value) || [...value].some((char) => char.charCodeAt(0) < 32))
|
|
177
|
+
throw new Error("Invalid review identifier");
|
|
178
|
+
return encodeURIComponent(value);
|
|
179
|
+
}
|
|
180
|
+
var Reviews = class {
|
|
181
|
+
constructor(http) {
|
|
182
|
+
this.http = http;
|
|
183
|
+
}
|
|
184
|
+
http;
|
|
185
|
+
/** Repeatable metadata read: never consumes the final execution result. */
|
|
186
|
+
calculation(executionId, options) {
|
|
187
|
+
return this.http.requestJson("GET", `/executions/${segment(executionId)}/review/calculation`, {
|
|
188
|
+
signal: options.signal,
|
|
189
|
+
query: { organization: options.organization }
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
/** Published formula structure only. Compare its version with the review's flowVersion. */
|
|
193
|
+
outline(flowId, options) {
|
|
194
|
+
return this.http.requestJson("GET", `/flows/${segment(flowId)}/outline`, {
|
|
195
|
+
signal: options.signal,
|
|
196
|
+
query: { organization: options.organization }
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
list(options) {
|
|
200
|
+
return this.http.requestJson("GET", "/reviews", {
|
|
201
|
+
signal: options.signal,
|
|
202
|
+
query: {
|
|
203
|
+
organization: options.organization,
|
|
204
|
+
...options.status ? { status: options.status } : {},
|
|
205
|
+
...options.cursor ? { cursor: options.cursor } : {},
|
|
206
|
+
...options.limit ? { limit: String(options.limit) } : {}
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
get(executionId, options) {
|
|
211
|
+
return this.http.requestJson("GET", `/executions/${segment(executionId)}/review`, {
|
|
212
|
+
signal: options.signal,
|
|
213
|
+
query: { organization: options.organization }
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
decide(executionId, decision, options) {
|
|
217
|
+
return this.http.requestJson("POST", `/executions/${segment(executionId)}/review`, {
|
|
218
|
+
signal: options.signal,
|
|
219
|
+
query: { organization: options.organization },
|
|
220
|
+
headers: { "content-type": "application/json" },
|
|
221
|
+
body: JSON.stringify(decision)
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
/** Streaming Response: callers own consumption/cancellation. Never buffers the PDF. */
|
|
225
|
+
document(executionId, docKey, resource, options) {
|
|
226
|
+
if (typeof resource === "object" && (!Number.isSafeInteger(resource.page) || resource.page < 1 || resource.page > 999999))
|
|
227
|
+
throw new Error("Invalid page number");
|
|
228
|
+
const path = typeof resource === "object" ? `pages/${resource.page}` : resource;
|
|
229
|
+
const headers = Object.fromEntries(
|
|
230
|
+
Object.entries({
|
|
231
|
+
range: options.range,
|
|
232
|
+
"if-range": options.ifRange,
|
|
233
|
+
"if-none-match": options.ifNoneMatch,
|
|
234
|
+
"if-modified-since": options.ifModifiedSince
|
|
235
|
+
}).filter((entry) => typeof entry[1] === "string")
|
|
236
|
+
);
|
|
237
|
+
return this.http.requestRaw(
|
|
238
|
+
"GET",
|
|
239
|
+
`/executions/${segment(executionId)}/review/documents/${segment(docKey)}/${path}`,
|
|
240
|
+
{
|
|
241
|
+
signal: options.signal,
|
|
242
|
+
headers,
|
|
243
|
+
acceptStatuses: [304, 416],
|
|
244
|
+
query: {
|
|
245
|
+
organization: options.organization,
|
|
246
|
+
...options.version ? { v: options.version } : {},
|
|
247
|
+
...options.size ? { size: options.size } : {}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
// src/sdk/execution-events.ts
|
|
255
|
+
import { createHmac as createHmac2, timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
256
|
+
|
|
257
|
+
// src/sdk/review-events.ts
|
|
258
|
+
import { createHmac, timingSafeEqual } from "crypto";
|
|
259
|
+
var REVIEW_EVENT_TYPES = [
|
|
260
|
+
"execution.awaiting_review",
|
|
261
|
+
"execution.review_done",
|
|
262
|
+
"execution.review_closed",
|
|
263
|
+
"execution.review_expired"
|
|
264
|
+
];
|
|
265
|
+
var ReviewEventVerificationError = class extends Error {
|
|
266
|
+
constructor(reason) {
|
|
267
|
+
super(`Invalid review event ${reason}`);
|
|
268
|
+
this.reason = reason;
|
|
269
|
+
this.name = "ReviewEventVerificationError";
|
|
270
|
+
}
|
|
271
|
+
reason;
|
|
272
|
+
};
|
|
273
|
+
function parseReviewEventCallback(value) {
|
|
274
|
+
const fail = () => {
|
|
275
|
+
throw new ReviewEventVerificationError("payload");
|
|
276
|
+
};
|
|
277
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return fail();
|
|
278
|
+
const raw = value;
|
|
279
|
+
const text = (key) => {
|
|
280
|
+
const v = raw[key];
|
|
281
|
+
if (typeof v !== "string" || !v.trim() || v.length > 512 || [...v].some((char) => char.charCodeAt(0) < 32))
|
|
282
|
+
return fail();
|
|
283
|
+
return v;
|
|
284
|
+
};
|
|
285
|
+
if (raw.schemaVersion !== 1 || !REVIEW_EVENT_TYPES.includes(raw.type))
|
|
286
|
+
return fail();
|
|
287
|
+
const event = {
|
|
288
|
+
schemaVersion: 1,
|
|
289
|
+
eventId: text("eventId"),
|
|
290
|
+
type: raw.type,
|
|
291
|
+
organization: text("organization"),
|
|
292
|
+
executionResultId: text("executionResultId"),
|
|
293
|
+
reviewId: text("reviewId"),
|
|
294
|
+
publishedAt: text("publishedAt")
|
|
295
|
+
};
|
|
296
|
+
if (!Number.isFinite(Date.parse(event.publishedAt))) return fail();
|
|
297
|
+
for (const key of ["flowId", "nodeId"])
|
|
298
|
+
if (raw[key] !== void 0) event[key] = text(key);
|
|
299
|
+
for (const key of ["flowVersion", "reviewVersion"]) {
|
|
300
|
+
if (raw[key] === void 0) continue;
|
|
301
|
+
if (!Number.isSafeInteger(raw[key]) || Number(raw[key]) < 1) return fail();
|
|
302
|
+
event[key] = Number(raw[key]);
|
|
303
|
+
}
|
|
304
|
+
if (raw.documentKeys !== void 0) {
|
|
305
|
+
if (!Array.isArray(raw.documentKeys) || raw.documentKeys.length > 1e3 || raw.documentKeys.some((v) => typeof v !== "string" || !v || v.length > 512))
|
|
306
|
+
return fail();
|
|
307
|
+
event.documentKeys = raw.documentKeys;
|
|
308
|
+
}
|
|
309
|
+
if (raw.decision !== void 0) {
|
|
310
|
+
if (!["continue", "exclude", "rerun", "reject"].includes(String(raw.decision))) return fail();
|
|
311
|
+
event.decision = raw.decision;
|
|
312
|
+
}
|
|
313
|
+
if (raw.reviewStatus !== void 0) {
|
|
314
|
+
if (!["completed", "rejected", "failed"].includes(String(raw.reviewStatus))) return fail();
|
|
315
|
+
event.reviewStatus = raw.reviewStatus;
|
|
316
|
+
}
|
|
317
|
+
if (event.type === "execution.review_done" && (!event.decision || !event.reviewVersion))
|
|
318
|
+
return fail();
|
|
319
|
+
if (event.type === "execution.review_closed" && !event.reviewStatus) return fail();
|
|
320
|
+
return event;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// src/sdk/execution-events.ts
|
|
324
|
+
var EXECUTION_NODE_EVENT_TYPES = [
|
|
325
|
+
"execution.node.started",
|
|
326
|
+
"execution.node.completed",
|
|
327
|
+
"execution.node.failed",
|
|
328
|
+
"execution.node.reused"
|
|
329
|
+
];
|
|
330
|
+
function parseExecutionEventCallback(value) {
|
|
331
|
+
const raw = value;
|
|
332
|
+
if (!raw || !EXECUTION_NODE_EVENT_TYPES.includes(raw.type))
|
|
333
|
+
return parseReviewEventCallback(value);
|
|
334
|
+
const fail = () => {
|
|
335
|
+
throw new ReviewEventVerificationError("payload");
|
|
336
|
+
};
|
|
337
|
+
const text = (key) => {
|
|
338
|
+
const v = raw[key];
|
|
339
|
+
return typeof v === "string" && v.length > 0 && v.length <= 512 && [...v].every((c) => c.charCodeAt(0) >= 32) ? v : fail();
|
|
340
|
+
};
|
|
341
|
+
const integer = (key) => Number.isSafeInteger(raw[key]) && Number(raw[key]) >= 1 ? Number(raw[key]) : fail();
|
|
342
|
+
if (raw.schemaVersion !== 1) return fail();
|
|
343
|
+
const event = {
|
|
344
|
+
schemaVersion: 1,
|
|
345
|
+
eventId: text("eventId"),
|
|
346
|
+
type: raw.type,
|
|
347
|
+
organization: text("organization"),
|
|
348
|
+
executionResultId: text("executionResultId"),
|
|
349
|
+
sequence: integer("sequence"),
|
|
350
|
+
occurredAt: text("occurredAt"),
|
|
351
|
+
nodeId: text("nodeId"),
|
|
352
|
+
nodeType: text("nodeType"),
|
|
353
|
+
revision: integer("revision"),
|
|
354
|
+
attemptId: text("attemptId"),
|
|
355
|
+
documentKeys: []
|
|
356
|
+
};
|
|
357
|
+
if (!Number.isFinite(Date.parse(event.occurredAt))) return fail();
|
|
358
|
+
if (!Array.isArray(raw.documentKeys) || !raw.documentKeys.length || raw.documentKeys.length > 1e3 || raw.documentKeys.some(
|
|
359
|
+
(v) => typeof v !== "string" || !v || v.length > 512 || [...v].some((c) => c.charCodeAt(0) < 32)
|
|
360
|
+
))
|
|
361
|
+
return fail();
|
|
362
|
+
event.documentKeys = raw.documentKeys;
|
|
363
|
+
if (raw.flowId !== void 0) event.flowId = text("flowId");
|
|
364
|
+
if (raw.flowVersion !== void 0) event.flowVersion = integer("flowVersion");
|
|
365
|
+
if (raw.publishedAt !== void 0) {
|
|
366
|
+
event.publishedAt = text("publishedAt");
|
|
367
|
+
if (!Number.isFinite(Date.parse(event.publishedAt))) return fail();
|
|
368
|
+
}
|
|
369
|
+
return event;
|
|
370
|
+
}
|
|
371
|
+
var ExecutionEvents = class {
|
|
372
|
+
constructor(http) {
|
|
373
|
+
this.http = http;
|
|
374
|
+
}
|
|
375
|
+
http;
|
|
376
|
+
/** One bounded page; never polls for completion or consumes a final result. */
|
|
377
|
+
async list(id, options) {
|
|
378
|
+
const after = options.after ?? 0, limit = options.limit ?? 100;
|
|
379
|
+
if (!id || id.length > 512 || id === "." || id === ".." || /[\\/]/.test(id) || [...id].some((c) => c.charCodeAt(0) < 32) || !options.organization?.trim() || !Number.isSafeInteger(after) || after < 0 || !Number.isSafeInteger(limit) || limit < 1 || limit > 100)
|
|
380
|
+
throw new Error("Invalid execution event query");
|
|
381
|
+
const page = await this.http.requestJson(
|
|
382
|
+
"GET",
|
|
383
|
+
`/executions/${encodeURIComponent(id)}/events`,
|
|
384
|
+
{
|
|
385
|
+
signal: options.signal,
|
|
386
|
+
query: { organization: options.organization, after: String(after), limit: String(limit) }
|
|
387
|
+
}
|
|
388
|
+
);
|
|
389
|
+
if (page.executionResultId !== id || !Array.isArray(page.events) || page.events.length > limit || !Number.isSafeInteger(page.lastSequence) || page.lastSequence < 0 || typeof page.truncated !== "boolean")
|
|
390
|
+
throw new ReviewEventVerificationError("payload");
|
|
391
|
+
const events = page.events.map((value) => parseExecutionEventCallback(value));
|
|
392
|
+
if (events.some(
|
|
393
|
+
(event, i) => !("sequence" in event) || event.executionResultId !== id || event.organization !== options.organization || event.sequence <= (i ? events[i - 1].sequence : after) || event.sequence > page.lastSequence
|
|
394
|
+
))
|
|
395
|
+
throw new ReviewEventVerificationError("payload");
|
|
396
|
+
if (page.nextAfter !== null && (!Number.isSafeInteger(page.nextAfter) || page.nextAfter <= after || page.nextAfter !== events.at(-1)?.sequence))
|
|
397
|
+
throw new ReviewEventVerificationError("payload");
|
|
398
|
+
return { ...page, events };
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
|
|
174
402
|
// src/sdk/errors.ts
|
|
175
403
|
var DocmanaError = class extends Error {
|
|
176
404
|
status;
|
|
@@ -178,6 +406,7 @@ var DocmanaError = class extends Error {
|
|
|
178
406
|
code;
|
|
179
407
|
/** Public API business code; `code` retains the SDK's existing error category. */
|
|
180
408
|
apiCode;
|
|
409
|
+
reviewConflict;
|
|
181
410
|
constructor(message, opts = {}) {
|
|
182
411
|
super(message);
|
|
183
412
|
this.name = new.target.name;
|
|
@@ -303,7 +532,7 @@ var HttpClient = class {
|
|
|
303
532
|
if (res.status === 401 && this.opts.getAccessToken) {
|
|
304
533
|
res = await this.send(method, path, options, true);
|
|
305
534
|
}
|
|
306
|
-
if (!res.ok) {
|
|
535
|
+
if (!res.ok && !options.acceptStatuses?.includes(res.status)) {
|
|
307
536
|
const requestId = res.headers.get("x-request-id") ?? void 0;
|
|
308
537
|
const message = await this.extractMessage(res);
|
|
309
538
|
const error = errorFromResponse(
|
|
@@ -315,6 +544,11 @@ var HttpClient = class {
|
|
|
315
544
|
const data = await res.clone().json().catch(() => null);
|
|
316
545
|
if (typeof data?.code === "string" && /^[A-Za-z][A-Za-z0-9_]{0,99}$/.test(data.code))
|
|
317
546
|
Object.defineProperty(error, "apiCode", { value: data.code, enumerable: true });
|
|
547
|
+
if (res.status === 409 && isRecord(data?.details))
|
|
548
|
+
Object.defineProperty(error, "reviewConflict", {
|
|
549
|
+
value: reviewConflictDetails(data.details),
|
|
550
|
+
enumerable: true
|
|
551
|
+
});
|
|
318
552
|
throw error;
|
|
319
553
|
}
|
|
320
554
|
return res;
|
|
@@ -358,6 +592,32 @@ var HttpClient = class {
|
|
|
358
592
|
}
|
|
359
593
|
}
|
|
360
594
|
};
|
|
595
|
+
function isRecord(value) {
|
|
596
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
597
|
+
}
|
|
598
|
+
function reviewConflictDetails(raw) {
|
|
599
|
+
const text = (value, limit) => typeof value === "string" ? value.slice(0, limit) : null;
|
|
600
|
+
const version = (value) => Number.isSafeInteger(value) && Number(value) >= 1 ? value : null;
|
|
601
|
+
const actor = isRecord(raw.decidedBy) ? raw.decidedBy : {};
|
|
602
|
+
const id = text(actor.id, 200);
|
|
603
|
+
const name = text(actor.name, 200);
|
|
604
|
+
const source = actor.source === "integrator" || actor.source === "token" ? actor.source : null;
|
|
605
|
+
const decidedBy = id !== null || name !== null || source !== null ? {
|
|
606
|
+
...id !== null ? { id } : {},
|
|
607
|
+
...name !== null ? { name } : {},
|
|
608
|
+
...source ? { source } : {}
|
|
609
|
+
} : null;
|
|
610
|
+
const currentVersion = version(raw.currentVersion) ?? version(raw.version);
|
|
611
|
+
const decidedAt = text(raw.decidedAt, 40) ?? text(raw.submittedAt, 40);
|
|
612
|
+
return {
|
|
613
|
+
currentVersion,
|
|
614
|
+
decidedBy,
|
|
615
|
+
decidedAt,
|
|
616
|
+
version: currentVersion,
|
|
617
|
+
submittedBy: id ?? name ?? text(raw.submittedBy, 200),
|
|
618
|
+
submittedAt: decidedAt
|
|
619
|
+
};
|
|
620
|
+
}
|
|
361
621
|
function parseRetryAfterMs(value) {
|
|
362
622
|
if (!value) return void 0;
|
|
363
623
|
const seconds = Number(value);
|
|
@@ -816,6 +1076,8 @@ function fullJitterDelay(intervalMs, maxIntervalMs, attempt, random) {
|
|
|
816
1076
|
// src/sdk/client.ts
|
|
817
1077
|
var Docmana = class {
|
|
818
1078
|
authoring;
|
|
1079
|
+
reviews;
|
|
1080
|
+
executionEvents;
|
|
819
1081
|
config;
|
|
820
1082
|
http;
|
|
821
1083
|
constructor(config) {
|
|
@@ -828,6 +1090,8 @@ var Docmana = class {
|
|
|
828
1090
|
headers: this.config.headers
|
|
829
1091
|
});
|
|
830
1092
|
this.authoring = new FormulaAuthoring(this.http);
|
|
1093
|
+
this.reviews = new Reviews(this.http);
|
|
1094
|
+
this.executionEvents = new ExecutionEvents(this.http);
|
|
831
1095
|
}
|
|
832
1096
|
async runFlowAsync(flowId, params, options = {}) {
|
|
833
1097
|
const parts = await resolveInputs(params);
|