@docmana/sdk 0.2.2 → 0.3.1

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,11 +1,22 @@
1
- # Changelog
2
-
3
- ## 0.2.2
4
- - Fix `package-lock.json`.
5
-
6
- ## 0.2.1
7
- - 400 responses now throw `DocmanaRequestError` (was `DocmanaAuthError`); add `Incompleted` to `ExecutionStatus`.
8
-
1
+ # Changelog
2
+
3
+ ## 0.3.1
4
+ - Fix: upload all files in a single `/upload` call (one folder UUID) instead of one call per file. Sending each file separately made Docmana treat them as distinct documents, which breaks multi-document flows ("Node already present" when the same flow node is built per document).
5
+
6
+ ## 0.3.0
7
+ - Relocated mapper logic and tests to `src/mappers/`.
8
+ - Renamed SDK models to `DocmanaX` and API models to `DocmanaApiX` to distinguish raw backend types from clean public SDK types.
9
+ - Pluralized `classification` results into a `classifications` key-value record on document results.
10
+ - Nested dynamic extraction fields inside a dedicated `data` record on node results to prevent property name collisions.
11
+ - Restructured node result interfaces to remove `score` and `feedback` from `DocmanaExtractionResult` and `DocmanaMetadataExtractionResult` (via new `DocmanaScoreNodeResult` interface).
12
+ - Excluded internal fields `result` and `documentType` from mapped SDK results.
13
+
14
+ ## 0.2.2
15
+ - Fix `package-lock.json`.
16
+
17
+ ## 0.2.1
18
+ - 400 responses now throw `DocmanaRequestError` (was `DocmanaAuthError`); add `Incompleted` to `ExecutionStatus`.
19
+
9
20
  ## 0.2.0
10
21
  - `RunFlowInput.once` selects the ad-hoc `run_once_flow` endpoint (current/draft flow) instead of the published `run_flow`.
11
22
  - `DocmanaConfig.headers` are sent on every API request (e.g. `X-Selected-Organization`), without overriding the bearer token or per-request headers.
package/dist/cli.mjs CHANGED
@@ -44,9 +44,11 @@ var DocmanaRequestError = class extends DocmanaError {
44
44
  };
45
45
  var DocmanaExecutionError = class extends DocmanaError {
46
46
  errors;
47
- constructor(message, errors = []) {
47
+ result;
48
+ constructor(message, errors = [], result) {
48
49
  super(message, { code: "execution_failed" });
49
50
  this.errors = errors;
51
+ this.result = result;
50
52
  }
51
53
  };
52
54
  var DocmanaTimeoutError = class extends DocmanaError {
@@ -306,14 +308,12 @@ async function resolveInputs(input) {
306
308
 
307
309
  // src/api/upload.ts
308
310
  async function uploadFiles(http, parts, signal) {
309
- const ids = [];
311
+ const form = new FormData();
310
312
  for (const part of parts) {
311
- const form = new FormData();
312
313
  form.append("files", new File([part.data], part.filename, { type: part.contentType }));
313
- const res = await http.requestJson("POST", "/upload", { body: form, signal });
314
- ids.push(res.id);
315
314
  }
316
- return ids;
315
+ const res = await http.requestJson("POST", "/upload", { body: form, signal });
316
+ return [res.id];
317
317
  }
318
318
 
319
319
  // src/api/run-flow.ts
@@ -356,6 +356,156 @@ async function pollUntilTerminal(opts) {
356
356
  }
357
357
  }
358
358
 
359
+ // src/mappers/mapper.ts
360
+ function extractNodeFields(rawNode, targetLanguage) {
361
+ const metadataFields = /* @__PURE__ */ new Set([
362
+ "node_id",
363
+ "node_name",
364
+ "node_type",
365
+ "feature_name",
366
+ "running_time",
367
+ "status",
368
+ "score",
369
+ "feedback",
370
+ "translations",
371
+ "tokens",
372
+ "input_tokens",
373
+ "output_tokens",
374
+ "errors",
375
+ "mana",
376
+ "model",
377
+ "tool_calls",
378
+ "prompt_id",
379
+ "prompt",
380
+ "prompt_name",
381
+ "label",
382
+ "scoreDescription",
383
+ "score_description",
384
+ "result",
385
+ "documentType"
386
+ ]);
387
+ let sourceObj = rawNode;
388
+ if (targetLanguage) {
389
+ const translations = rawNode.translations;
390
+ if (!translations || !translations[targetLanguage]) {
391
+ throw new DocmanaError(
392
+ `Translation for language '${targetLanguage}' is missing on node '${rawNode.node_name || rawNode.node_id}'`,
393
+ { code: "translation_missing", status: 400 }
394
+ );
395
+ }
396
+ sourceObj = translations[targetLanguage];
397
+ }
398
+ const dynamicFields = {};
399
+ for (const [key, value] of Object.entries(sourceObj)) {
400
+ if (!metadataFields.has(key)) {
401
+ dynamicFields[key] = value;
402
+ }
403
+ }
404
+ const sdkNode = {
405
+ status: rawNode.status || "Unknown"
406
+ };
407
+ if (rawNode.errors && rawNode.errors.length > 0) {
408
+ sdkNode.errors = rawNode.errors;
409
+ }
410
+ const nodeType = rawNode.node_type || "";
411
+ const isScoreNode = /^(Validation|Classification|CrossValidation|Conclusion)$/i.test(nodeType);
412
+ if (isScoreNode) {
413
+ if (rawNode.score !== void 0 && rawNode.score !== null) {
414
+ sdkNode.score = rawNode.score;
415
+ }
416
+ if (rawNode.feedback !== void 0 && rawNode.feedback !== null) {
417
+ sdkNode.feedback = rawNode.feedback;
418
+ }
419
+ }
420
+ if (Object.keys(dynamicFields).length > 0) {
421
+ sdkNode.data = dynamicFields;
422
+ }
423
+ return sdkNode;
424
+ }
425
+ function mapExecutionResult(raw, targetLanguage) {
426
+ const sdkResult = {
427
+ status: raw.status || "Unknown",
428
+ executionId: String(raw.execution_id || raw.executionResultId || raw.execution_result_id || ""),
429
+ documents: {
430
+ mappings: []
431
+ }
432
+ };
433
+ if (raw.errors && raw.errors.length > 0) {
434
+ sdkResult.errors = raw.errors;
435
+ }
436
+ const results = raw.results || [];
437
+ for (const item of results) {
438
+ const isDocSpecific = !!item.document;
439
+ if (isDocSpecific && item.document) {
440
+ const docReq = item.document;
441
+ const docKey = docReq.doc_key || "unknown_doc";
442
+ const docName = docReq.name || "";
443
+ sdkResult.documents.mappings.push({
444
+ name: docName,
445
+ reference: docKey
446
+ });
447
+ const docResult = {
448
+ status: item.status || docReq.status || "Unknown",
449
+ extractions: {},
450
+ validations: {}
451
+ };
452
+ const nodeResults = item.node_results || [];
453
+ for (const node of nodeResults) {
454
+ const nodeType = node.node_type || "";
455
+ const nodeName = node.node_name || node.node_id || "unknown_node";
456
+ if (/^Classification$/i.test(nodeType)) {
457
+ const mappedNode = extractNodeFields(node, targetLanguage);
458
+ if (!docResult.classifications) {
459
+ docResult.classifications = {};
460
+ }
461
+ docResult.classifications[nodeName] = mappedNode;
462
+ } else if (/^MetadataExtraction$/i.test(nodeType)) {
463
+ docResult.metadataExtraction = extractNodeFields(node, targetLanguage);
464
+ } else if (/^Extraction$/i.test(nodeType)) {
465
+ if (docResult.extractions) {
466
+ docResult.extractions[nodeName] = extractNodeFields(node, targetLanguage);
467
+ }
468
+ } else if (/^Validation$/i.test(nodeType)) {
469
+ if (docResult.validations) {
470
+ docResult.validations[nodeName] = extractNodeFields(node, targetLanguage);
471
+ }
472
+ }
473
+ }
474
+ if (docResult.extractions && Object.keys(docResult.extractions).length === 0) {
475
+ delete docResult.extractions;
476
+ }
477
+ if (docResult.validations && Object.keys(docResult.validations).length === 0) {
478
+ delete docResult.validations;
479
+ }
480
+ sdkResult.documents[docKey] = docResult;
481
+ } else {
482
+ const nodeResults = item.node_results || [];
483
+ for (const node of nodeResults) {
484
+ const nodeType = node.node_type || "";
485
+ const nodeName = node.node_name || node.node_id || "unknown_node";
486
+ if (/^CrossValidation$/i.test(nodeType)) {
487
+ if (!sdkResult.crossValidations) {
488
+ sdkResult.crossValidations = {};
489
+ }
490
+ sdkResult.crossValidations[nodeName] = extractNodeFields(
491
+ node,
492
+ targetLanguage
493
+ );
494
+ } else if (/^(Conclusion|Finish)$/i.test(nodeType)) {
495
+ if (!sdkResult.conclusions) {
496
+ sdkResult.conclusions = {};
497
+ }
498
+ sdkResult.conclusions[nodeName] = extractNodeFields(
499
+ node,
500
+ targetLanguage
501
+ );
502
+ }
503
+ }
504
+ }
505
+ }
506
+ return sdkResult;
507
+ }
508
+
359
509
  // src/client.ts
360
510
  var Docmana = class {
361
511
  config;
@@ -392,8 +542,9 @@ var Docmana = class {
392
542
  async getStatus(executionResultId, signal) {
393
543
  return getStatus(this.http, executionResultId, signal);
394
544
  }
395
- async getResult(executionResultId, signal) {
396
- return getResult(this.http, executionResultId, signal);
545
+ async getResult(executionResultId, signal, language) {
546
+ const rawResult = await getResult(this.http, executionResultId, signal);
547
+ return mapExecutionResult(rawResult, language);
397
548
  }
398
549
  async runFlow(flowId, input) {
399
550
  const { executionResultId } = await this.runFlowAsync(flowId, input);
@@ -403,9 +554,13 @@ var Docmana = class {
403
554
  timeoutMs: input.timeoutMs ?? this.config.timeoutMs,
404
555
  signal: input.signal
405
556
  });
406
- const result = await this.getResult(executionResultId, input.signal);
557
+ const result = await this.getResult(executionResultId, input.signal, input.language);
407
558
  if (result.status === "Failed") {
408
- throw new DocmanaExecutionError(`Flow ${flowId} execution failed`, result.errors ?? []);
559
+ throw new DocmanaExecutionError(
560
+ `Flow ${flowId} execution failed`,
561
+ result.errors ?? [],
562
+ result
563
+ );
409
564
  }
410
565
  return result;
411
566
  }
@@ -418,6 +573,15 @@ var CliUsageError = class extends Error {
418
573
  this.name = "CliUsageError";
419
574
  }
420
575
  };
576
+ var CliSilentError = class extends Error {
577
+ exitCode;
578
+ constructor(exitCode = 1) {
579
+ super();
580
+ this.name = "CliSilentError";
581
+ this.exitCode = exitCode;
582
+ Object.setPrototypeOf(this, new.target.prototype);
583
+ }
584
+ };
421
585
  var HELP = {
422
586
  root: "Run Docmana document-analysis flows.",
423
587
  flow: "Manage and run Docmana flows.",
@@ -441,6 +605,9 @@ async function runCli(argv = process.argv.slice(2), env = process.env, io = {
441
605
  await program.parseAsync(argv, { from: "user" });
442
606
  return 0;
443
607
  } catch (err) {
608
+ if (err instanceof CliSilentError) {
609
+ return err.exitCode;
610
+ }
444
611
  if (err instanceof CliUsageError) {
445
612
  io.stderr(`Error: ${err.message}
446
613
  `);
@@ -467,7 +634,7 @@ function buildProgram(env, io, clientFactory, deps) {
467
634
  const flow = program.command("flow").description(HELP.flow).action(() => {
468
635
  io.stdout(flow.helpInformation());
469
636
  });
470
- flow.command("run").description(HELP.run).argument("<flow-id>", "Docmana flow id").requiredOption("--files <csv>", "Comma-separated file paths to upload").option("--client-id <id>", "OAuth2 client id; defaults to DOCMANA_CLIENT_ID").option("--client-secret <secret>", "OAuth2 client secret; defaults to DOCMANA_CLIENT_SECRET").option("--api-url <url>", "Docmana API base URL; defaults to DOCMANA_API_URL").option("--token-url <url>", "OAuth2 token endpoint; defaults to DOCMANA_TOKEN_URL").option("--scope <scope>", "OAuth2 scope; defaults to DOCMANA_SCOPE").option("--organisation <id>", "Docmana organisation id; defaults to DOCMANA_ORGANISATION").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").action(async (flowId, options) => {
637
+ flow.command("run").description(HELP.run).argument("<flow-id>", "Docmana flow id").requiredOption("--files <csv>", "Comma-separated file paths to upload").option("--client-id <id>", "OAuth2 client id; defaults to DOCMANA_CLIENT_ID").option("--client-secret <secret>", "OAuth2 client secret; defaults to DOCMANA_CLIENT_SECRET").option("--api-url <url>", "Docmana API base URL; defaults to DOCMANA_API_URL").option("--token-url <url>", "OAuth2 token endpoint; defaults to DOCMANA_TOKEN_URL").option("--scope <scope>", "OAuth2 scope; defaults to DOCMANA_SCOPE").option("--organisation <id>", "Docmana organisation id; defaults to DOCMANA_ORGANISATION").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").action(async (flowId, options) => {
471
638
  await runFlowCommand(flowId, options, env, io, clientFactory, deps);
472
639
  });
473
640
  const config = program.command("config").description(HELP.config).action(() => {
@@ -479,50 +646,77 @@ function buildProgram(env, io, clientFactory, deps) {
479
646
  return program;
480
647
  }
481
648
  async function runFlowCommand(flowId, options, env, io, clientFactory, deps) {
482
- const files = parseFiles(options.files);
483
- const cliConfig = await loadCliConfig(options.config, getCwd(deps), Boolean(options.config));
484
- const clientId = firstNonEmpty(options.clientId, env.DOCMANA_CLIENT_ID, cliConfig.clientId);
485
- const clientSecret = firstNonEmpty(
486
- options.clientSecret,
487
- env.DOCMANA_CLIENT_SECRET,
488
- cliConfig.clientSecret
489
- );
490
- const organisation = firstNonEmpty(
491
- options.organisation,
492
- env.DOCMANA_ORGANISATION,
493
- cliConfig.organisation
494
- );
495
- const apiBaseUrl = firstNonEmpty(options.apiUrl, env.DOCMANA_API_URL, cliConfig.apiBaseUrl);
496
- const tokenEndpoint = firstNonEmpty(
497
- options.tokenUrl,
498
- env.DOCMANA_TOKEN_URL,
499
- cliConfig.tokenEndpoint
500
- );
501
- const scope = firstNonEmpty(options.scope, env.DOCMANA_SCOPE, cliConfig.scope);
502
- if (!clientId)
503
- throw new CliUsageError("Missing client id. Use --client-id or DOCMANA_CLIENT_ID.");
504
- if (!clientSecret) {
505
- throw new CliUsageError("Missing client secret. Use --client-secret or DOCMANA_CLIENT_SECRET.");
506
- }
507
- if (!organisation) {
508
- throw new CliUsageError("Missing organisation. Use --organisation or DOCMANA_ORGANISATION.");
509
- }
510
- const client = clientFactory({
511
- clientId,
512
- clientSecret,
513
- apiBaseUrl,
514
- tokenEndpoint,
515
- scope,
516
- headers: { "X-Selected-Organization": organisation },
517
- tokenCache: createFileTokenCache(resolveTokenCachePath(options.tokenCache, getCwd(deps)))
518
- });
519
- const result = await client.runFlow(flowId, { files: files.map((path) => ({ path })) });
520
- if (options.json) {
521
- io.stdout(`${JSON.stringify(result, null, 2)}
649
+ try {
650
+ const files = parseFiles(options.files);
651
+ const cliConfig = await loadCliConfig(options.config, getCwd(deps), Boolean(options.config));
652
+ const clientId = firstNonEmpty(options.clientId, env.DOCMANA_CLIENT_ID, cliConfig.clientId);
653
+ const clientSecret = firstNonEmpty(
654
+ options.clientSecret,
655
+ env.DOCMANA_CLIENT_SECRET,
656
+ cliConfig.clientSecret
657
+ );
658
+ const organisation = firstNonEmpty(
659
+ options.organisation,
660
+ env.DOCMANA_ORGANISATION,
661
+ cliConfig.organisation
662
+ );
663
+ const apiBaseUrl = firstNonEmpty(options.apiUrl, env.DOCMANA_API_URL, cliConfig.apiBaseUrl);
664
+ const tokenEndpoint = firstNonEmpty(
665
+ options.tokenUrl,
666
+ env.DOCMANA_TOKEN_URL,
667
+ cliConfig.tokenEndpoint
668
+ );
669
+ const scope = firstNonEmpty(options.scope, env.DOCMANA_SCOPE, cliConfig.scope);
670
+ if (!clientId)
671
+ throw new CliUsageError("Missing client id. Use --client-id or DOCMANA_CLIENT_ID.");
672
+ if (!clientSecret) {
673
+ throw new CliUsageError(
674
+ "Missing client secret. Use --client-secret or DOCMANA_CLIENT_SECRET."
675
+ );
676
+ }
677
+ if (!organisation) {
678
+ throw new CliUsageError("Missing organisation. Use --organisation or DOCMANA_ORGANISATION.");
679
+ }
680
+ const client = clientFactory({
681
+ clientId,
682
+ clientSecret,
683
+ apiBaseUrl,
684
+ tokenEndpoint,
685
+ scope,
686
+ headers: { "X-Selected-Organization": organisation },
687
+ tokenCache: createFileTokenCache(resolveTokenCachePath(options.tokenCache, getCwd(deps)))
688
+ });
689
+ const result = await client.runFlow(flowId, {
690
+ files: files.map((path) => ({ path })),
691
+ language: options.language
692
+ });
693
+ if (options.json) {
694
+ io.stdout(`${JSON.stringify(result, null, 2)}
695
+ `);
696
+ return;
697
+ }
698
+ io.stdout(formatHumanResult(result));
699
+ } catch (err) {
700
+ if (options.json) {
701
+ if (err instanceof DocmanaExecutionError && err.result) {
702
+ io.stdout(`${JSON.stringify(err.result, null, 2)}
703
+ `);
704
+ } else {
705
+ const errorPayload = {
706
+ error: err instanceof Error ? err.message : String(err)
707
+ };
708
+ if (err instanceof DocmanaError) {
709
+ errorPayload.code = err.code;
710
+ if (err.status !== void 0) errorPayload.status = err.status;
711
+ if (err.requestId !== void 0) errorPayload.requestId = err.requestId;
712
+ }
713
+ io.stdout(`${JSON.stringify(errorPayload, null, 2)}
522
714
  `);
523
- return;
715
+ }
716
+ throw new CliSilentError(err instanceof CliUsageError ? 2 : 1);
717
+ }
718
+ throw err;
524
719
  }
525
- io.stdout(formatHumanResult(result));
526
720
  }
527
721
  async function loginCommand(options, env, io, deps) {
528
722
  const auth = await resolveAuthConfig(options, env, deps);
@@ -726,31 +920,141 @@ function firstNonEmpty(...values) {
726
920
  }
727
921
  function formatHumanResult(result) {
728
922
  const lines = ["Docmana flow completed", `Status: ${String(result.status ?? "Unknown")}`];
729
- const executionId = result.execution_id ?? result.executionResultId ?? result.execution_result_id;
923
+ const executionId = result.executionId;
730
924
  if (executionId) lines.push(`Execution id: ${String(executionId)}`);
731
- if (Array.isArray(result.results)) lines.push(`Results: ${result.results.length}`);
732
- if (Array.isArray(result.errors)) lines.push(`Errors: ${result.errors.length}`);
733
- const preview = formatResultsPreview(result.results);
925
+ if (result.documents && result.documents.mappings) {
926
+ lines.push(`Results: ${result.documents.mappings.length}`);
927
+ }
928
+ if (result.errors && result.errors.length > 0) {
929
+ lines.push(`Errors: ${result.errors.length}`);
930
+ }
931
+ const previewData = {};
932
+ if (result.documents && result.documents.mappings) {
933
+ for (const mapping of result.documents.mappings.slice(0, 3)) {
934
+ const docKey = mapping.reference;
935
+ previewData[mapping.name] = result.documents[docKey];
936
+ }
937
+ }
938
+ const preview = formatResultsPreview(previewData);
734
939
  if (preview) lines.push("", "Result preview:", preview);
735
940
  lines.push("", "Use --json to print the complete result payload.");
736
941
  return `${lines.join("\n")}
737
942
  `;
738
943
  }
739
944
  function formatResultsPreview(results) {
740
- if (!Array.isArray(results) || results.length === 0) return void 0;
741
- const preview = JSON.stringify(results.slice(0, 3), null, 2);
945
+ if (!results) return void 0;
946
+ const preview = JSON.stringify(results, null, 2);
742
947
  if (!preview) return void 0;
743
948
  return preview.length > 2e3 ? `${preview.slice(0, 2e3)}
744
949
  ...` : preview;
745
950
  }
746
951
  function formatRuntimeError(err) {
952
+ return formatHumanError(err);
953
+ }
954
+ function formatHumanError(err) {
747
955
  if (err instanceof DocmanaExecutionError) {
748
- const details = err.errors.length > 0 ? ` (${err.errors.length} error(s))` : "";
749
- return `${err.message}${details}`;
956
+ const lines = [`Error: ${err.message}`];
957
+ if (err.errors && err.errors.length > 0) {
958
+ lines.push("Global errors:");
959
+ for (const e of err.errors) {
960
+ lines.push(` - ${formatSingleError(e)}`);
961
+ }
962
+ }
963
+ const result = err.result;
964
+ if (result) {
965
+ if (result.documents && result.documents.mappings && result.documents.mappings.length > 0) {
966
+ lines.push("Document execution results:");
967
+ for (const mapping of result.documents.mappings) {
968
+ const docKey = mapping.reference;
969
+ const docName = mapping.name;
970
+ const docResult = result.documents[docKey];
971
+ if (docResult) {
972
+ const status = docResult.status || "Unknown";
973
+ const nodes = [];
974
+ if (docResult.classifications) nodes.push(...Object.values(docResult.classifications));
975
+ if (docResult.metadataExtraction) nodes.push(docResult.metadataExtraction);
976
+ if (docResult.extractions) nodes.push(...Object.values(docResult.extractions));
977
+ if (docResult.validations) nodes.push(...Object.values(docResult.validations));
978
+ const hasNodeFailures = nodes.some(
979
+ (n) => n.status === "Failed" || n.errors && n.errors.length > 0
980
+ );
981
+ if (docResult.status === "Failed" || hasNodeFailures) {
982
+ lines.push(` - Document: ${docName} (Status: ${status})`);
983
+ if (docResult.classifications) {
984
+ for (const [nodeName, node] of Object.entries(docResult.classifications)) {
985
+ if (node.errors && node.errors.length > 0) {
986
+ lines.push(` Classification: ${nodeName} (Status: ${node.status})`);
987
+ for (const nodeErr of node.errors) {
988
+ lines.push(` - ${nodeErr}`);
989
+ }
990
+ }
991
+ }
992
+ }
993
+ if (docResult.metadataExtraction?.errors && docResult.metadataExtraction.errors.length > 0) {
994
+ lines.push(
995
+ ` MetadataExtraction: (Status: ${docResult.metadataExtraction.status})`
996
+ );
997
+ for (const nodeErr of docResult.metadataExtraction.errors) {
998
+ lines.push(` - ${nodeErr}`);
999
+ }
1000
+ }
1001
+ if (docResult.extractions) {
1002
+ for (const [nodeName, node] of Object.entries(docResult.extractions)) {
1003
+ if (node.status === "Failed" || node.errors && node.errors.length > 0) {
1004
+ lines.push(` Extraction: ${nodeName} (Status: ${node.status})`);
1005
+ if (node.errors) {
1006
+ for (const nodeErr of node.errors) {
1007
+ lines.push(` - ${nodeErr}`);
1008
+ }
1009
+ }
1010
+ }
1011
+ }
1012
+ }
1013
+ if (docResult.validations) {
1014
+ for (const [nodeName, node] of Object.entries(docResult.validations)) {
1015
+ if (node.status === "Failed" || node.errors && node.errors.length > 0) {
1016
+ lines.push(` Validation: ${nodeName} (Status: ${node.status})`);
1017
+ if (node.errors) {
1018
+ for (const nodeErr of node.errors) {
1019
+ lines.push(` - ${nodeErr}`);
1020
+ }
1021
+ }
1022
+ }
1023
+ }
1024
+ }
1025
+ } else {
1026
+ lines.push(` - Document: ${docName} (Status: ${status})`);
1027
+ }
1028
+ } else {
1029
+ lines.push(` - Document: ${docName} (Status: Unknown)`);
1030
+ }
1031
+ }
1032
+ }
1033
+ }
1034
+ return lines.join("\n");
1035
+ }
1036
+ if (err instanceof DocmanaError) {
1037
+ return `Error: ${err.message} (Code: ${err.code})`;
1038
+ }
1039
+ if (err instanceof Error) {
1040
+ return `Error: ${err.message}`;
1041
+ }
1042
+ return `Error: ${String(err)}`;
1043
+ }
1044
+ function formatSingleError(e) {
1045
+ if (typeof e === "string") return e;
1046
+ if (e && typeof e === "object") {
1047
+ const obj = e;
1048
+ if (typeof obj.msg === "string") return obj.msg;
1049
+ if (typeof obj.message === "string") return obj.message;
1050
+ if (typeof obj.error === "string") return obj.error;
1051
+ try {
1052
+ return JSON.stringify(e);
1053
+ } catch {
1054
+ return String(e);
1055
+ }
750
1056
  }
751
- if (err instanceof DocmanaError) return err.message;
752
- if (err instanceof Error) return err.message;
753
- return "Unexpected error";
1057
+ return String(e);
754
1058
  }
755
1059
  function isDirectRun() {
756
1060
  const entry = process.argv[1];