@docmana/sdk 0.10.0 → 0.12.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 CHANGED
@@ -1,8 +1,22 @@
1
- # Changelog
2
-
3
- ## 0.10.0
4
- - Add an optional `organization` to flow selection, emitted as a
5
- `?organization=<org>` query param on the request to
1
+ # Changelog
2
+
3
+ ## 0.12.0
4
+ - Add `--context <json>` to `docmana flow run`, parsed as a JSON object and
5
+ passed through to `runFlow` as `RunFlowParams.context`.
6
+ - Return CLI usage errors when `--context` is invalid JSON or is not a JSON
7
+ object.
8
+
9
+ ## 0.11.0
10
+ - Normalize execution statuses to uppercase SDK values:
11
+ `PENDING`, `RUNNING`, `INCOMPLETED`, `COMPLETED`, and `FAILED`.
12
+ - Normalize raw API and polling statuses before checking terminal or failed
13
+ states, so legacy mixed-case values still map consistently.
14
+ - Rename flow metadata `contextSchema` to `jsonContextSchema` in the public
15
+ `FlowMetadata` shape and README examples.
16
+
17
+ ## 0.10.0
18
+ - Add an optional `organization` to flow selection, emitted as a
19
+ `?organization=<org>` query param on the request to
6
20
  `docmana-consumer-edge-api`. Purely additive — existing calls are unchanged.
7
21
  - `listFlows({ organization })`, `getFlowMetadata(flowId, { organization })`,
8
22
  and `getFlowDefinition(flowId, { organization })` accept the option.
package/README.md CHANGED
@@ -103,7 +103,7 @@ use `RunFlowParams` for request data:
103
103
  - `listOrganizations()` calls `GET /v1/organizations` and returns
104
104
  `{ id, name }[]` for the organizations available to the Bearer token.
105
105
  - `getFlowMetadata(flowId)` calls `GET /v1/flows/:flowId/metadata` and returns
106
- `{ flowId, name?, contextSchema, languages, minScore?, documents }`.
106
+ `{ flowId, name?, jsonContextSchema, languages, minScore?, documents }`.
107
107
  - `getFlowDefinition(flowId)` calls `GET /v1/flows/:flowId/definition` and
108
108
  returns the normalized flow definition `{ flowId, name?, languages, minScore?, nodes }`.
109
109
  - `listOrganizationDocuments({ prefix? })` calls
@@ -121,7 +121,7 @@ use `RunFlowParams` for request data:
121
121
  `GET /v1/executions/:executionResultId/result`.
122
122
  - `runFlow(flowId, params, options?)` starts async execution, polls status, fetches the
123
123
  result, respects `X-Poll-After` and `429 Retry-After` while polling, and throws
124
- `DocmanaExecutionError` if the result status is `Failed`.
124
+ `DocmanaExecutionError` if the result status is `FAILED`.
125
125
 
126
126
  Consumer is the polling authority. When `GET /status` returns `X-Poll-After`,
127
127
  the SDK waits that many milliseconds before the next status call. If the header
@@ -140,14 +140,14 @@ console.log(organizations.map((org) => `${org.id}: ${org.name}`).join("\n"));
140
140
 
141
141
  ```ts
142
142
  const metadata = await docmana.getFlowMetadata("<flow-id>");
143
- console.log(metadata.contextSchema);
143
+ console.log(metadata.jsonContextSchema);
144
144
 
145
145
  const definition = await docmana.getFlowDefinition("<flow-id>");
146
146
  console.log(definition.nodes.map((node) => node.label).join("\n"));
147
147
  ```
148
148
 
149
149
  ```ts
150
- const depot = await docmana.listOrganizationDocuments({ prefix: "dossier/" });
150
+ const depot = await docmana.listOrganizationDocuments({ prefix: "folder/" });
151
151
  if (depot.configured) {
152
152
  const file = await docmana.downloadOrganizationDocument(depot.documents[0].path);
153
153
  console.log(file.filename, file.bytes.byteLength);
@@ -159,8 +159,8 @@ const { executionResultId } = await docmana.runFlowAsync("<flow-id>", {
159
159
  files: ["./contract.pdf"],
160
160
  });
161
161
 
162
- let status = "Pending";
163
- while (status !== "Completed" && status !== "Failed") {
162
+ let status = "PENDING";
163
+ while (status !== "COMPLETED" && status !== "FAILED") {
164
164
  await new Promise((r) => setTimeout(r, 2000));
165
165
  ({ status } = await docmana.getStatus(executionResultId));
166
166
  }
@@ -217,9 +217,9 @@ docmana organization list
217
217
 
218
218
  ```bash
219
219
  DOCMANA_API_URL="https://consumer.example.com/v1"
220
- docmana organization document list --prefix "dossier/"
221
- docmana organization document download "dossier/facture.pdf"
222
- docmana organization document download "dossier/facture.pdf" --output "./facture.pdf"
220
+ docmana organization document list --prefix "folder/"
221
+ docmana organization document download "folder/invoice.pdf"
222
+ docmana organization document download "folder/invoice.pdf" --output "./invoice.pdf"
223
223
  ```
224
224
 
225
225
  ```bash
@@ -239,12 +239,14 @@ You can also run without the cache:
239
239
  docmana flow run "<flow-id>" \
240
240
  --api-url "https://consumer.example.com/v1" \
241
241
  --access-token "$DOCMANA_ACCESS_TOKEN" \
242
- --files "./contract.pdf,./annexe.pdf" \
242
+ --files "./contract.pdf,./appendix.pdf" \
243
+ --context '{"contract":{"destination":"Canada"}}' \
243
244
  --draft \
244
245
  --json
245
246
  ```
246
247
 
247
248
  Use `--draft` to run the current draft flow version.
249
+ Use `--context` to pass business context as a JSON object.
248
250
  Use `docmana flow list --json` to print the raw flow list returned by Consumer.
249
251
  Use `docmana organization list --json` to print the organization list as JSON.
250
252
  Use `docmana organization document list --json` for raw organization document JSON.
@@ -264,7 +266,7 @@ optional `.requestId` when available.
264
266
  | `DocmanaAuthError` | `401` rejected or expired Bearer token. |
265
267
  | `DocmanaPermissionError` | `403` missing permission. |
266
268
  | `DocmanaNotFoundError` | `404` flow or execution not found. |
267
- | `DocmanaExecutionError` | The flow finished with status `"Failed"`. Carries `.errors` and `.result`. |
269
+ | `DocmanaExecutionError` | The flow finished with status `"FAILED"`. Carries `.errors` and `.result`. |
268
270
  | `DocmanaTimeoutError` | Polling exceeded `timeoutMs`. |
269
271
  | `DocmanaAbortError` | The provided `AbortSignal` fired. |
270
272
 
package/dist/cli.mjs CHANGED
@@ -282,13 +282,29 @@ async function runFlow(http, flowId, parts, signal, useDraftVersion = false, con
282
282
  );
283
283
  }
284
284
 
285
+ // src/sdk/status.ts
286
+ function normalizeStatus(status, fallback = "UNKNOWN") {
287
+ if (typeof status !== "string") return fallback;
288
+ const trimmed = status.trim();
289
+ if (!trimmed) return fallback;
290
+ return trimmed.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[\s-]+/g, "_").toUpperCase();
291
+ }
292
+ function isFailedStatus(status) {
293
+ return normalizeStatus(status) === "FAILED";
294
+ }
295
+ function isTerminalStatus(status) {
296
+ const normalized = normalizeStatus(status);
297
+ return normalized === "COMPLETED" || normalized === "FAILED";
298
+ }
299
+
285
300
  // src/sdk/api/execution-status.ts
286
301
  async function getStatus(http, executionResultId, signal) {
287
302
  const res = await http.requestRaw("GET", `/executions/${executionResultId}/status`, { signal });
288
303
  const text = await res.text();
289
304
  const body = text ? JSON.parse(text) : { status: "" };
305
+ const normalizedBody = { ...body, status: normalizeStatus(body.status) };
290
306
  const pollAfterMs = parsePositiveInt(res.headers.get("x-poll-after"));
291
- return pollAfterMs === void 0 ? body : { ...body, pollAfterMs };
307
+ return pollAfterMs === void 0 ? normalizedBody : { ...normalizedBody, pollAfterMs };
292
308
  }
293
309
  function parsePositiveInt(value) {
294
310
  if (!value) return void 0;
@@ -430,7 +446,6 @@ function toNumberValue(value) {
430
446
  }
431
447
 
432
448
  // src/sdk/polling/poll.ts
433
- var TERMINAL = /* @__PURE__ */ new Set(["Completed", "Failed"]);
434
449
  var defaultSleep = (ms) => new Promise((r) => setTimeout(r, ms));
435
450
  async function pollUntilTerminal(opts) {
436
451
  const sleep = opts.sleep ?? defaultSleep;
@@ -451,8 +466,8 @@ async function pollUntilTerminal(opts) {
451
466
  await sleep(err.retryAfterMs ?? opts.intervalMs);
452
467
  continue;
453
468
  }
454
- const { status } = result;
455
- if (TERMINAL.has(status)) return status;
469
+ const status = normalizeStatus(result.status);
470
+ if (isTerminalStatus(status)) return status;
456
471
  if (elapsed() >= opts.timeoutMs) {
457
472
  throw new DocmanaTimeoutError(`Execution did not finish within ${opts.timeoutMs}ms`);
458
473
  }
@@ -547,7 +562,7 @@ var Docmana = class {
547
562
  signal: options.signal
548
563
  });
549
564
  const result = await this.getResult(executionResultId, options.signal, params.language);
550
- if (result.status === "Failed") {
565
+ if (isFailedStatus(result.status)) {
551
566
  throw new DocmanaExecutionError(
552
567
  `Flow ${flowId} execution failed`,
553
568
  result.errors ?? [],
@@ -578,7 +593,7 @@ var Docmana = class {
578
593
  return downloadOrganizationDocuments(this.http, paths);
579
594
  }
580
595
  isTerminalStatus(status) {
581
- return status === "Completed" || status === "Failed";
596
+ return isTerminalStatus(status);
582
597
  }
583
598
  };
584
599
 
@@ -686,10 +701,10 @@ function formatHumanError(err) {
686
701
  if (docResult.extractions) nodes.push(...Object.values(docResult.extractions));
687
702
  if (docResult.validations) nodes.push(...Object.values(docResult.validations));
688
703
  const hasNodeFailures = nodes.some(
689
- (n) => n.status === "Failed" || n.errors && n.errors.length > 0
704
+ (n) => isFailedStatus(n.status) || n.errors && n.errors.length > 0
690
705
  );
691
706
  lines.push(` - Document: ${docName} (Status: ${status})`);
692
- if (docResult.status !== "Failed" && !hasNodeFailures) continue;
707
+ if (!isFailedStatus(docResult.status) && !hasNodeFailures) continue;
693
708
  if (docResult.classifications) {
694
709
  for (const [nodeName, node] of Object.entries(docResult.classifications)) {
695
710
  if (node.errors && node.errors.length > 0) {
@@ -706,7 +721,7 @@ function formatHumanError(err) {
706
721
  }
707
722
  if (docResult.extractions) {
708
723
  for (const [nodeName, node] of Object.entries(docResult.extractions)) {
709
- if (node.status === "Failed" || node.errors && node.errors.length > 0) {
724
+ if (isFailedStatus(node.status) || node.errors && node.errors.length > 0) {
710
725
  lines.push(` Extraction: ${nodeName} (Status: ${node.status})`);
711
726
  if (node.errors) {
712
727
  for (const nodeErr of node.errors) lines.push(` - ${nodeErr}`);
@@ -716,7 +731,7 @@ function formatHumanError(err) {
716
731
  }
717
732
  if (docResult.validations) {
718
733
  for (const [nodeName, node] of Object.entries(docResult.validations)) {
719
- if (node.status === "Failed" || node.errors && node.errors.length > 0) {
734
+ if (isFailedStatus(node.status) || node.errors && node.errors.length > 0) {
720
735
  lines.push(` Validation: ${nodeName} (Status: ${node.status})`);
721
736
  if (node.errors) {
722
737
  for (const nodeErr of node.errors) lines.push(` - ${nodeErr}`);
@@ -1255,11 +1270,13 @@ async function listOrganizationsCommand(options, env, io, clientFactory, deps) {
1255
1270
  async function runFlowCommand(flowId, options, env, io, clientFactory, deps) {
1256
1271
  try {
1257
1272
  const files = parseFiles(options.files);
1273
+ const context = parseContext(options.context);
1258
1274
  const client = await resolveApiClient(options, env, io, clientFactory, deps);
1259
1275
  const result = await client.runFlow(
1260
1276
  flowId,
1261
1277
  {
1262
1278
  files: files.map((path) => ({ path })),
1279
+ context,
1263
1280
  language: options.language,
1264
1281
  useDraftVersion: options.draft === true
1265
1282
  },
@@ -1307,6 +1324,19 @@ async function runFlowCommand(flowId, options, env, io, clientFactory, deps) {
1307
1324
  throw err;
1308
1325
  }
1309
1326
  }
1327
+ function parseContext(raw) {
1328
+ if (raw === void 0) return void 0;
1329
+ let parsed;
1330
+ try {
1331
+ parsed = JSON.parse(raw);
1332
+ } catch {
1333
+ throw new CliUsageError("--context must be valid JSON.");
1334
+ }
1335
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
1336
+ throw new CliUsageError("--context must be a JSON object.");
1337
+ }
1338
+ return parsed;
1339
+ }
1310
1340
 
1311
1341
  // src/cli/commands/login.ts
1312
1342
  async function loginCommand(options, env, io, deps) {
@@ -1385,7 +1415,7 @@ function buildProgram(env, io, clientFactory, deps) {
1385
1415
  flow.command("list").description(HELP.list).option("--api-url <url>", "Consumer API base URL; defaults to DOCMANA_API_URL").option("--access-token <token>", "Bearer token; defaults to DOCMANA_ACCESS_TOKEN").option("--config <path>", `Config file path; defaults to ./${DEFAULT_CONFIG_FILE}`).option("--token-cache <path>", `Token cache path; defaults to ./${DEFAULT_TOKEN_CACHE_FILE}`).option("--json", "Print the raw JSON flow list").action(async (options) => {
1386
1416
  await listFlowsCommand(options, env, io, clientFactory, deps);
1387
1417
  });
1388
- flow.command("run").description(HELP.run).argument("<flow-id>", "Docmana flow id").requiredOption("--files <csv>", "Comma-separated file paths to upload").option("--api-url <url>", "Consumer API base URL; defaults to DOCMANA_API_URL").option("--access-token <token>", "Bearer token; defaults to DOCMANA_ACCESS_TOKEN").option("--config <path>", `Config file path; defaults to ./${DEFAULT_CONFIG_FILE}`).option("--token-cache <path>", `Token cache path; defaults to ./${DEFAULT_TOKEN_CACHE_FILE}`).option("--json", "Print the raw JSON result").option("--language <lang>", "Translate output to specified language").option("--draft", "Run the current draft flow version").action(async (flowId, options) => {
1418
+ flow.command("run").description(HELP.run).argument("<flow-id>", "Docmana flow id").requiredOption("--files <csv>", "Comma-separated file paths to upload").option("--api-url <url>", "Consumer API base URL; defaults to DOCMANA_API_URL").option("--access-token <token>", "Bearer token; defaults to DOCMANA_ACCESS_TOKEN").option("--config <path>", `Config file path; defaults to ./${DEFAULT_CONFIG_FILE}`).option("--token-cache <path>", `Token cache path; defaults to ./${DEFAULT_TOKEN_CACHE_FILE}`).option("--json", "Print the raw JSON result").option("--context <json>", "Business context as a JSON object").option("--language <lang>", "Translate output to specified language").option("--draft", "Run the current draft flow version").action(async (flowId, options) => {
1389
1419
  await runFlowCommand(flowId, options, env, io, clientFactory, deps);
1390
1420
  });
1391
1421
  flow.command("metadata").description(HELP.metadata).argument("<flow-id>", "Docmana flow id").option("--api-url <url>", "Consumer API base URL; defaults to DOCMANA_API_URL").option("--access-token <token>", "Bearer token; defaults to DOCMANA_ACCESS_TOKEN").option("--config <path>", `Config file path; defaults to ./${DEFAULT_CONFIG_FILE}`).option("--token-cache <path>", `Token cache path; defaults to ./${DEFAULT_TOKEN_CACHE_FILE}`).action(async (flowId, options) => {