@docmana/sdk 0.28.1 → 0.36.0-hitl.2

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/dist/client.d.ts CHANGED
@@ -1,5 +1,11 @@
1
+ import { FormulaAuthoring } from "./authoring.js";
2
+ import { Reviews } from "./reviews.js";
3
+ import { ExecutionEvents } from "./execution-events.js";
1
4
  import type { AccessibleOrganization, DocmanaConfig, DocmanaExecutionResult, DownloadedOrganizationDocument, ExecutionStatus, FraudDetectionAccepted, FraudDetectionAsyncOptions, FraudDetectionOptions, FraudDetectionParams, FraudDetectionResult, FraudDetectionStatusResult, FlowDefinition, FlowListItem, FlowMetadata, OrganizationDocumentsPayload, RunFlowAsyncParams, RunFlowAsyncOptions, RunFlowOptions, RunFlowParams, RunPartialFlowAsyncParams, RunPartialFlowOptions, RunPartialFlowParams } from "./types.js";
2
5
  export declare class Docmana {
6
+ readonly authoring: FormulaAuthoring;
7
+ readonly reviews: Reviews;
8
+ readonly executionEvents: ExecutionEvents;
3
9
  private readonly config;
4
10
  private readonly http;
5
11
  constructor(config: DocmanaConfig);
package/dist/errors.d.ts CHANGED
@@ -4,6 +4,13 @@ export declare class DocmanaError extends Error {
4
4
  readonly status?: number;
5
5
  readonly requestId?: string;
6
6
  readonly code: string;
7
+ /** Public API business code; `code` retains the SDK's existing error category. */
8
+ readonly apiCode?: string;
9
+ readonly reviewConflict?: {
10
+ version: number | null;
11
+ submittedBy: string | null;
12
+ submittedAt: string | null;
13
+ };
7
14
  constructor(message: string, opts?: {
8
15
  status?: number;
9
16
  requestId?: string;
@@ -0,0 +1,47 @@
1
+ import type { HttpClient } from "./http/http-client.js";
2
+ import type { ExecutionStatus } from "./models/execution-status.enum.js";
3
+ import { type ReviewEventCallback, type verifyReviewEventCallback } from "./review-events.js";
4
+ export declare const EXECUTION_NODE_EVENT_TYPES: readonly ["execution.node.started", "execution.node.completed", "execution.node.failed", "execution.node.reused"];
5
+ export interface ExecutionNodeEvent {
6
+ schemaVersion: 1;
7
+ eventId: string;
8
+ type: (typeof EXECUTION_NODE_EVENT_TYPES)[number];
9
+ organization: string;
10
+ executionResultId: string;
11
+ sequence: number;
12
+ occurredAt: string;
13
+ /** Broker publication instant, present only on event callbacks. */
14
+ publishedAt?: string;
15
+ flowId?: string;
16
+ flowVersion?: number;
17
+ nodeId: string;
18
+ nodeType: string;
19
+ documentKeys: string[];
20
+ revision: number;
21
+ attemptId: string;
22
+ }
23
+ export type ExecutionEventCallback = ExecutionNodeEvent | ReviewEventCallback;
24
+ export interface ExecutionEventsPage {
25
+ executionResultId: string;
26
+ executionStatus: ExecutionStatus;
27
+ flowId?: string;
28
+ flowVersion?: number | null;
29
+ events: ExecutionNodeEvent[];
30
+ nextAfter: number | null;
31
+ lastSequence: number;
32
+ truncated: boolean;
33
+ }
34
+ export declare function parseExecutionEventCallback(value: unknown): ExecutionEventCallback;
35
+ /** Expanded signature verifier. Existing review-only parser/verifier keep their contract. */
36
+ export declare function verifyExecutionEventCallback(options: Parameters<typeof verifyReviewEventCallback>[0]): ExecutionEventCallback;
37
+ export declare class ExecutionEvents {
38
+ private readonly http;
39
+ constructor(http: HttpClient);
40
+ /** One bounded page; never polls for completion or consumes a final result. */
41
+ list(id: string, options: {
42
+ organization: string;
43
+ after?: number;
44
+ limit?: number;
45
+ signal?: AbortSignal;
46
+ }): Promise<ExecutionEventsPage>;
47
+ }
@@ -9,6 +9,8 @@ export interface HttpClientOptions {
9
9
  }
10
10
  type FetchBody = NonNullable<Parameters<typeof fetch>[1]>["body"];
11
11
  export interface RequestOptions {
12
+ /** Conditional document reads can legitimately return 304 or 416. */
13
+ acceptStatuses?: readonly number[];
12
14
  query?: Record<string, string>;
13
15
  body?: FetchBody;
14
16
  headers?: Record<string, string>;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,12 @@
1
1
  export { Docmana } from "./client.js";
2
+ export { REVIEW_EVENT_TYPES, parseReviewEventCallback, verifyReviewEventCallback, ReviewEventVerificationError, } from "./review-events.js";
3
+ export type { ReviewEventCallback, ReviewEventType } from "./review-events.js";
4
+ export { FormulaAuthoring } from "./authoring.js";
5
+ export type { AuthoringDocumentTrialNode, AuthoringDocumentTrialSpec, AuthoringDocumentTrialReport, AuthoringSource, AuthoringGitPublication, AuthoringGitProjection, AuthoringPublication, AuthoringConversation, AuthoringConversationInput, AuthoringConversationContext, AuthoringConversationTurn, AuthoringData, AuthoringNode, AuthoringEdge, AuthoringRevisions, AuthoringFlowSummary, AuthoringSnapshot, AuthoringChanges, AuthoringDraft, AuthoringCreate, AuthoringReceipt, AuthoringOptions, AuthoringEditorCatalog, AuthoringPageOptions, } from "./authoring.js";
2
6
  export { DocmanaError, DocmanaAuthError, DocmanaRequestError, DocmanaPayloadTooLargeError, DocmanaPermissionError, DocmanaNotFoundError, DocmanaExecutionError, DocmanaFraudDetectionError, DocmanaTimeoutError, DocmanaAbortError, DocmanaServiceUnavailableError, } from "./errors.js";
3
7
  export type { AccessibleOrganization, DownloadedOrganizationDocument, DocmanaConfig, FlowDefinition, FlowDefinitionField, FlowDefinitionNode, FlowDefinitionNodeMetadata, FlowDocumentRequirement, FlowListItem, FlowMetadata, FraudDetectionAccepted, FraudDetectionAsyncOptions, FraudDetectionBaseParams, FraudDetectionError, FraudDetectionFileParams, FraudDetectionOptions, FraudDetectionParams, FraudDetectionProcessor, FraudDetectionResult, FraudDetectionStatus, FraudDetectionStatusResult, FraudDetectionSummary, FraudDetectionUrlParams, FraudDetectionVerdict, FraudFinding, FraudFindingLocation, FraudFindingSeverity, FraudSeverityCounts, OrganizationDocument, OrganizationDocumentsPayload, PartialExecutionFlowVersion, PartialExecutionMetadata, PartialExecutionScope, RunFlowAsyncParams, RunFlowAsyncOptions, RunFlowOptions, RunFlowParams, RunPartialFlowAsyncParams, RunPartialFlowOptions, RunPartialFlowParams, FileInput, NamedFileInput, PathFileInput, DocmanaExecutionResult, DocmanaExecutionVerdict, ExecutionStatus, DocmanaDocumentMapping, DocmanaSourceDocument, DocmanaDocumentResult, DocmanaNodeResult, DocmanaScoreNodeResult, DocmanaCrossValidationResult, DocmanaConclusionResult, DocmanaClassificationResult, DocmanaMetadataExtractionResult, DocmanaExtractionResult, DocmanaValidationResult, DocmanaPreprocessingResult, } from "./types.js";
4
8
  export type { ExecutionProgress, VirtualDocument, PhysicalDocument, DocumentContent, FlowNodeType, FlowConfigs, FlowNode, FlowEdge, Flow, NodeTranslationResult, DocmanaApiExecutionResult, DocmanaApiNodeResult, DocmanaApiDocumentExecutionResult, DocmanaApiDocumentRequest, DocmanaApiPreprocessDocumentResult, } from "./models/index.js";
9
+ export { Reviews } from "./reviews.js";
10
+ export { ExecutionEvents, EXECUTION_NODE_EVENT_TYPES, parseExecutionEventCallback, verifyExecutionEventCallback, } from "./execution-events.js";
11
+ export type { ExecutionNodeEvent, ExecutionEventCallback, ExecutionEventsPage, } from "./execution-events.js";
12
+ export type { ReviewCalculation, ReviewFlowOutline, ReviewItem, DocumentPreviewManifest, ReviewDocument, ReviewAssessment, ReviewFinding, ReviewTask, ReviewSummary, ReviewDecision, ReviewReadOptions, ReviewDocumentOptions, } from "./reviews.js";
package/dist/index.js CHANGED
@@ -31,7 +31,17 @@ __export(sdk_exports, {
31
31
  DocmanaPermissionError: () => DocmanaPermissionError,
32
32
  DocmanaRequestError: () => DocmanaRequestError,
33
33
  DocmanaServiceUnavailableError: () => DocmanaServiceUnavailableError,
34
- DocmanaTimeoutError: () => DocmanaTimeoutError
34
+ DocmanaTimeoutError: () => DocmanaTimeoutError,
35
+ EXECUTION_NODE_EVENT_TYPES: () => EXECUTION_NODE_EVENT_TYPES,
36
+ ExecutionEvents: () => ExecutionEvents,
37
+ FormulaAuthoring: () => FormulaAuthoring,
38
+ REVIEW_EVENT_TYPES: () => REVIEW_EVENT_TYPES,
39
+ ReviewEventVerificationError: () => ReviewEventVerificationError,
40
+ Reviews: () => Reviews,
41
+ parseExecutionEventCallback: () => parseExecutionEventCallback,
42
+ parseReviewEventCallback: () => parseReviewEventCallback,
43
+ verifyExecutionEventCallback: () => verifyExecutionEventCallback,
44
+ verifyReviewEventCallback: () => verifyReviewEventCallback
35
45
  });
36
46
  module.exports = __toCommonJS(sdk_exports);
37
47
 
@@ -65,11 +75,419 @@ function normalizeApiBaseUrl(value) {
65
75
  return /\/v\d+$/i.test(trimmed) ? trimmed : `${trimmed}/v1`;
66
76
  }
67
77
 
78
+ // src/sdk/authoring.ts
79
+ function identity(id) {
80
+ if (!/^[A-Za-z0-9_-]{1,200}$/.test(id)) throw new Error("Invalid formula identifier");
81
+ return encodeURIComponent(id);
82
+ }
83
+ var FormulaAuthoring = class {
84
+ constructor(http) {
85
+ this.http = http;
86
+ }
87
+ http;
88
+ /** Read-only, revision-pinned definitions. Assisted execution belongs to the server-side Flows POC. */
89
+ documentTrialDefinitions(spec, options) {
90
+ identity(spec.flowId);
91
+ if (Boolean(spec.node) === (spec.allNodes === true))
92
+ throw new Error("Select one node or all nodes");
93
+ if (spec.node) identity(spec.node);
94
+ return this.request("POST", "document-trial/definitions", options, spec);
95
+ }
96
+ /** Native manual execution through Consumer Edge; assisted trials use definitions plus server-side Flows. */
97
+ async trialDocument(file, filename, spec, options) {
98
+ if (!options.organization.trim()) throw new Error("Organization is required");
99
+ if (!file.size || file.size > 20 * 1024 * 1024 || !/\.(pdf|jpeg|jpg|png|bmp|tiff|tif|heic)$/i.test(filename))
100
+ throw new Error("Invalid trial document");
101
+ if ("flowId" in spec) {
102
+ identity(spec.flowId);
103
+ if (Boolean(spec.node) === (spec.allNodes === true))
104
+ throw new Error("Select one node or all nodes");
105
+ if (spec.node) identity(spec.node);
106
+ } else if (!Array.isArray(spec.nodes) || !spec.nodes.length || spec.nodes.length > 100)
107
+ throw new Error("Invalid trial nodes");
108
+ const body = new FormData();
109
+ body.set("file", file, filename);
110
+ body.set("spec", JSON.stringify(spec));
111
+ return this.http.requestJson("POST", "/authoring/document-trial", {
112
+ query: { organization: options.organization },
113
+ signal: options.signal,
114
+ body
115
+ });
116
+ }
117
+ editorCatalog(options) {
118
+ return this.request("GET", "editor-catalog", options);
119
+ }
120
+ generateMapping(body, options) {
121
+ return this.request("POST", "generate-mapping", options, body);
122
+ }
123
+ openConversation(body, options) {
124
+ identity(body.flow_id);
125
+ if (body.node_id) identity(body.node_id);
126
+ return this.request("POST", "conversations", options, body);
127
+ }
128
+ sendConversationMessage(id, content, options) {
129
+ return this.request("POST", `conversations/${identity(id)}/messages`, options, { content });
130
+ }
131
+ confirmConversation(id, body, options) {
132
+ return this.request("POST", `conversations/${identity(id)}/confirm`, options, body);
133
+ }
134
+ request(method, path, options, body) {
135
+ if (!options.organization.trim()) throw new Error("Organization is required");
136
+ return this.http.requestJson(method, `/authoring/${path}`, {
137
+ query: { organization: options.organization },
138
+ signal: options.signal,
139
+ ...body === void 0 ? {} : {
140
+ body: JSON.stringify(body),
141
+ headers: { "content-type": "application/json" }
142
+ }
143
+ });
144
+ }
145
+ list(options) {
146
+ const offset = options.offset ?? 0, limit = options.limit ?? 50;
147
+ if (!Number.isInteger(offset) || offset < 0 || !Number.isInteger(limit) || limit < 1 || limit > 200)
148
+ throw new Error("Invalid authoring pagination");
149
+ return this.request(
150
+ "GET",
151
+ `flows?offset=${offset}&limit=${limit}&sort=${encodeURIComponent(options.sort ?? "id")}`,
152
+ options
153
+ );
154
+ }
155
+ remove(id, options) {
156
+ return this.request("DELETE", `flows/${identity(id)}`, options);
157
+ }
158
+ async exportArchive(id, options) {
159
+ if (!options.organization.trim()) throw new Error("Organization is required");
160
+ return (await this.http.requestBytes("GET", `/authoring/flows/${identity(id)}/export`, {
161
+ query: { organization: options.organization },
162
+ signal: options.signal
163
+ })).bytes;
164
+ }
165
+ importArchive(file, filename, options) {
166
+ if (!options.organization.trim()) throw new Error("Organization is required");
167
+ if (!/\.(zip|docmana)$/i.test(filename) || file.size > 10 * 1024 * 1024)
168
+ throw new Error("Invalid formula archive");
169
+ const body = new FormData();
170
+ body.append("file", file, filename);
171
+ return this.http.requestJson("POST", "/authoring/flows/import", {
172
+ query: {
173
+ organization: options.organization,
174
+ confirmOverwrite: String(options.confirmOverwrite === true)
175
+ },
176
+ body,
177
+ signal: options.signal
178
+ });
179
+ }
180
+ get(id, options) {
181
+ return this.request("GET", `flows/${identity(id)}`, options);
182
+ }
183
+ create(body, options) {
184
+ return this.request("POST", "flows", options, body);
185
+ }
186
+ save(id, body, options) {
187
+ return this.request("PATCH", `flows/${identity(id)}/draft`, options, body);
188
+ }
189
+ validate(id, revisions, options) {
190
+ return this.request("POST", `flows/${identity(id)}/validate`, options, {
191
+ expectedRevisions: revisions
192
+ });
193
+ }
194
+ publish(id, body, options) {
195
+ return this.request("POST", `flows/${identity(id)}/publish`, options, body);
196
+ }
197
+ history(id, kind, options) {
198
+ if (kind !== "snapshots" && kind !== "published") throw new Error("Invalid history kind");
199
+ const offset = options.offset ?? 0, limit = options.limit ?? 20;
200
+ if (!Number.isInteger(offset) || offset < 0 || !Number.isInteger(limit) || limit < 1 || limit > 100)
201
+ throw new Error("Invalid history pagination");
202
+ return this.request(
203
+ "GET",
204
+ `flows/${identity(id)}/history/${kind}?offset=${offset}&limit=${limit}`,
205
+ options
206
+ );
207
+ }
208
+ flags(options) {
209
+ return this.request("GET", "feature-flags", options);
210
+ }
211
+ };
212
+
213
+ // src/sdk/reviews.ts
214
+ function segment(value) {
215
+ if (!value || value === "." || value === ".." || /[\\/]/.test(value) || [...value].some((char) => char.charCodeAt(0) < 32))
216
+ throw new Error("Invalid review identifier");
217
+ return encodeURIComponent(value);
218
+ }
219
+ var Reviews = class {
220
+ constructor(http) {
221
+ this.http = http;
222
+ }
223
+ http;
224
+ /** Repeatable metadata read: never consumes the final execution result. */
225
+ calculation(executionId, options) {
226
+ return this.http.requestJson("GET", `/executions/${segment(executionId)}/review/calculation`, {
227
+ signal: options.signal,
228
+ query: { organization: options.organization }
229
+ });
230
+ }
231
+ /** Published formula structure only. Compare its version with the review's flowVersion. */
232
+ outline(flowId, options) {
233
+ return this.http.requestJson("GET", `/flows/${segment(flowId)}/outline`, {
234
+ signal: options.signal,
235
+ query: { organization: options.organization }
236
+ });
237
+ }
238
+ list(options) {
239
+ return this.http.requestJson("GET", "/reviews", {
240
+ signal: options.signal,
241
+ query: {
242
+ organization: options.organization,
243
+ ...options.status ? { status: options.status } : {},
244
+ ...options.cursor ? { cursor: options.cursor } : {},
245
+ ...options.limit ? { limit: String(options.limit) } : {}
246
+ }
247
+ });
248
+ }
249
+ get(executionId, options) {
250
+ return this.http.requestJson("GET", `/executions/${segment(executionId)}/review`, {
251
+ signal: options.signal,
252
+ query: { organization: options.organization }
253
+ });
254
+ }
255
+ decide(executionId, decision, options) {
256
+ return this.http.requestJson("POST", `/executions/${segment(executionId)}/review`, {
257
+ signal: options.signal,
258
+ query: { organization: options.organization },
259
+ headers: { "content-type": "application/json" },
260
+ body: JSON.stringify(decision)
261
+ });
262
+ }
263
+ /** Streaming Response: callers own consumption/cancellation. Never buffers the PDF. */
264
+ document(executionId, docKey, resource, options) {
265
+ if (typeof resource === "object" && (!Number.isSafeInteger(resource.page) || resource.page < 1 || resource.page > 999999))
266
+ throw new Error("Invalid page number");
267
+ const path = typeof resource === "object" ? `pages/${resource.page}` : resource;
268
+ const headers = Object.fromEntries(
269
+ Object.entries({
270
+ range: options.range,
271
+ "if-range": options.ifRange,
272
+ "if-none-match": options.ifNoneMatch,
273
+ "if-modified-since": options.ifModifiedSince
274
+ }).filter((entry) => typeof entry[1] === "string")
275
+ );
276
+ return this.http.requestRaw(
277
+ "GET",
278
+ `/executions/${segment(executionId)}/review/documents/${segment(docKey)}/${path}`,
279
+ {
280
+ signal: options.signal,
281
+ headers,
282
+ acceptStatuses: [304, 416],
283
+ query: {
284
+ organization: options.organization,
285
+ ...options.version ? { v: options.version } : {},
286
+ ...options.size ? { size: options.size } : {}
287
+ }
288
+ }
289
+ );
290
+ }
291
+ };
292
+
293
+ // src/sdk/execution-events.ts
294
+ var import_node_crypto2 = require("crypto");
295
+
296
+ // src/sdk/review-events.ts
297
+ var import_node_crypto = require("crypto");
298
+ var REVIEW_EVENT_TYPES = [
299
+ "execution.awaiting_review",
300
+ "execution.review_done",
301
+ "execution.review_closed",
302
+ "execution.review_expired"
303
+ ];
304
+ var ReviewEventVerificationError = class extends Error {
305
+ constructor(reason) {
306
+ super(`Invalid review event ${reason}`);
307
+ this.reason = reason;
308
+ this.name = "ReviewEventVerificationError";
309
+ }
310
+ reason;
311
+ };
312
+ function parseReviewEventCallback(value) {
313
+ const fail = () => {
314
+ throw new ReviewEventVerificationError("payload");
315
+ };
316
+ if (!value || typeof value !== "object" || Array.isArray(value)) return fail();
317
+ const raw = value;
318
+ const text = (key) => {
319
+ const v = raw[key];
320
+ if (typeof v !== "string" || !v.trim() || v.length > 512 || [...v].some((char) => char.charCodeAt(0) < 32))
321
+ return fail();
322
+ return v;
323
+ };
324
+ if (raw.schemaVersion !== 1 || !REVIEW_EVENT_TYPES.includes(raw.type))
325
+ return fail();
326
+ const event = {
327
+ schemaVersion: 1,
328
+ eventId: text("eventId"),
329
+ type: raw.type,
330
+ organization: text("organization"),
331
+ executionResultId: text("executionResultId"),
332
+ reviewId: text("reviewId"),
333
+ publishedAt: text("publishedAt")
334
+ };
335
+ if (!Number.isFinite(Date.parse(event.publishedAt))) return fail();
336
+ for (const key of ["flowId", "nodeId"])
337
+ if (raw[key] !== void 0) event[key] = text(key);
338
+ for (const key of ["flowVersion", "reviewVersion"]) {
339
+ if (raw[key] === void 0) continue;
340
+ if (!Number.isSafeInteger(raw[key]) || Number(raw[key]) < 1) return fail();
341
+ event[key] = Number(raw[key]);
342
+ }
343
+ if (raw.documentKeys !== void 0) {
344
+ if (!Array.isArray(raw.documentKeys) || raw.documentKeys.length > 1e3 || raw.documentKeys.some((v) => typeof v !== "string" || !v || v.length > 512))
345
+ return fail();
346
+ event.documentKeys = raw.documentKeys;
347
+ }
348
+ if (raw.decision !== void 0) {
349
+ if (!["continue", "exclude", "rerun", "reject"].includes(String(raw.decision))) return fail();
350
+ event.decision = raw.decision;
351
+ }
352
+ if (raw.reviewStatus !== void 0) {
353
+ if (!["completed", "rejected", "failed"].includes(String(raw.reviewStatus))) return fail();
354
+ event.reviewStatus = raw.reviewStatus;
355
+ }
356
+ if (event.type === "execution.review_done" && (!event.decision || !event.reviewVersion))
357
+ return fail();
358
+ if (event.type === "execution.review_closed" && !event.reviewStatus) return fail();
359
+ return event;
360
+ }
361
+ function verifyReviewEventCallback(options) {
362
+ const { body, signature, timestamp, secret } = options;
363
+ if (!timestamp || !/^\d{10,13}$/.test(timestamp))
364
+ throw new ReviewEventVerificationError("timestamp");
365
+ const age = Math.abs((options.nowMs ?? Date.now()) / 1e3 - Number(timestamp));
366
+ if (age > (options.toleranceSeconds ?? 300)) throw new ReviewEventVerificationError("timestamp");
367
+ if (secret.length < 32 || !signature || !/^v1=[a-f0-9]{64}$/.test(signature))
368
+ throw new ReviewEventVerificationError("signature");
369
+ const expected = (0, import_node_crypto.createHmac)("sha256", secret).update(`${timestamp}.${body}`).digest();
370
+ if (!(0, import_node_crypto.timingSafeEqual)(expected, Buffer.from(signature.slice(3), "hex")))
371
+ throw new ReviewEventVerificationError("signature");
372
+ let value;
373
+ try {
374
+ value = JSON.parse(body);
375
+ } catch {
376
+ throw new ReviewEventVerificationError("payload");
377
+ }
378
+ const event = parseReviewEventCallback(value);
379
+ if (options.organization !== void 0 && event.organization !== options.organization)
380
+ throw new ReviewEventVerificationError("organization");
381
+ return event;
382
+ }
383
+
384
+ // src/sdk/execution-events.ts
385
+ var EXECUTION_NODE_EVENT_TYPES = [
386
+ "execution.node.started",
387
+ "execution.node.completed",
388
+ "execution.node.failed",
389
+ "execution.node.reused"
390
+ ];
391
+ function parseExecutionEventCallback(value) {
392
+ const raw = value;
393
+ if (!raw || !EXECUTION_NODE_EVENT_TYPES.includes(raw.type))
394
+ return parseReviewEventCallback(value);
395
+ const fail = () => {
396
+ throw new ReviewEventVerificationError("payload");
397
+ };
398
+ const text = (key) => {
399
+ const v = raw[key];
400
+ return typeof v === "string" && v.length > 0 && v.length <= 512 && [...v].every((c) => c.charCodeAt(0) >= 32) ? v : fail();
401
+ };
402
+ const integer = (key) => Number.isSafeInteger(raw[key]) && Number(raw[key]) >= 1 ? Number(raw[key]) : fail();
403
+ if (raw.schemaVersion !== 1) return fail();
404
+ const event = {
405
+ schemaVersion: 1,
406
+ eventId: text("eventId"),
407
+ type: raw.type,
408
+ organization: text("organization"),
409
+ executionResultId: text("executionResultId"),
410
+ sequence: integer("sequence"),
411
+ occurredAt: text("occurredAt"),
412
+ nodeId: text("nodeId"),
413
+ nodeType: text("nodeType"),
414
+ revision: integer("revision"),
415
+ attemptId: text("attemptId"),
416
+ documentKeys: []
417
+ };
418
+ if (!Number.isFinite(Date.parse(event.occurredAt))) return fail();
419
+ if (!Array.isArray(raw.documentKeys) || !raw.documentKeys.length || raw.documentKeys.length > 1e3 || raw.documentKeys.some(
420
+ (v) => typeof v !== "string" || !v || v.length > 512 || [...v].some((c) => c.charCodeAt(0) < 32)
421
+ ))
422
+ return fail();
423
+ event.documentKeys = raw.documentKeys;
424
+ if (raw.flowId !== void 0) event.flowId = text("flowId");
425
+ if (raw.flowVersion !== void 0) event.flowVersion = integer("flowVersion");
426
+ if (raw.publishedAt !== void 0) {
427
+ event.publishedAt = text("publishedAt");
428
+ if (!Number.isFinite(Date.parse(event.publishedAt))) return fail();
429
+ }
430
+ return event;
431
+ }
432
+ function verifyExecutionEventCallback(options) {
433
+ const { body, signature, timestamp, secret } = options;
434
+ if (!timestamp || !/^\d{10,13}$/.test(timestamp) || Math.abs((options.nowMs ?? Date.now()) / 1e3 - Number(timestamp)) > (options.toleranceSeconds ?? 300))
435
+ throw new ReviewEventVerificationError("timestamp");
436
+ if (secret.length < 32 || !signature || !/^v1=[a-f0-9]{64}$/.test(signature))
437
+ throw new ReviewEventVerificationError("signature");
438
+ const expected = (0, import_node_crypto2.createHmac)("sha256", secret).update(`${timestamp}.${body}`).digest();
439
+ if (!(0, import_node_crypto2.timingSafeEqual)(expected, Buffer.from(signature.slice(3), "hex")))
440
+ throw new ReviewEventVerificationError("signature");
441
+ let value;
442
+ try {
443
+ value = JSON.parse(body);
444
+ } catch {
445
+ throw new ReviewEventVerificationError("payload");
446
+ }
447
+ const event = parseExecutionEventCallback(value);
448
+ if (options.organization !== void 0 && event.organization !== options.organization)
449
+ throw new ReviewEventVerificationError("organization");
450
+ return event;
451
+ }
452
+ var ExecutionEvents = class {
453
+ constructor(http) {
454
+ this.http = http;
455
+ }
456
+ http;
457
+ /** One bounded page; never polls for completion or consumes a final result. */
458
+ async list(id, options) {
459
+ const after = options.after ?? 0, limit = options.limit ?? 100;
460
+ 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)
461
+ throw new Error("Invalid execution event query");
462
+ const page = await this.http.requestJson(
463
+ "GET",
464
+ `/executions/${encodeURIComponent(id)}/events`,
465
+ {
466
+ signal: options.signal,
467
+ query: { organization: options.organization, after: String(after), limit: String(limit) }
468
+ }
469
+ );
470
+ if (page.executionResultId !== id || !Array.isArray(page.events) || page.events.length > limit || !Number.isSafeInteger(page.lastSequence) || page.lastSequence < 0 || typeof page.truncated !== "boolean")
471
+ throw new ReviewEventVerificationError("payload");
472
+ const events = page.events.map((value) => parseExecutionEventCallback(value));
473
+ if (events.some(
474
+ (event, i) => !("sequence" in event) || event.executionResultId !== id || event.organization !== options.organization || event.sequence <= (i ? events[i - 1].sequence : after) || event.sequence > page.lastSequence
475
+ ))
476
+ throw new ReviewEventVerificationError("payload");
477
+ if (page.nextAfter !== null && (!Number.isSafeInteger(page.nextAfter) || page.nextAfter <= after || page.nextAfter !== events.at(-1)?.sequence))
478
+ throw new ReviewEventVerificationError("payload");
479
+ return { ...page, events };
480
+ }
481
+ };
482
+
68
483
  // src/sdk/errors.ts
69
484
  var DocmanaError = class extends Error {
70
485
  status;
71
486
  requestId;
72
487
  code;
488
+ /** Public API business code; `code` retains the SDK's existing error category. */
489
+ apiCode;
490
+ reviewConflict;
73
491
  constructor(message, opts = {}) {
74
492
  super(message);
75
493
  this.name = new.target.name;
@@ -195,15 +613,28 @@ var HttpClient = class {
195
613
  if (res.status === 401 && this.opts.getAccessToken) {
196
614
  res = await this.send(method, path, options, true);
197
615
  }
198
- if (!res.ok) {
616
+ if (!res.ok && !options.acceptStatuses?.includes(res.status)) {
199
617
  const requestId = res.headers.get("x-request-id") ?? void 0;
200
618
  const message = await this.extractMessage(res);
201
- throw errorFromResponse(
619
+ const error = errorFromResponse(
202
620
  res.status,
203
621
  message,
204
622
  requestId,
205
623
  parseRetryAfterMs(res.headers.get("retry-after"))
206
624
  );
625
+ const data = await res.clone().json().catch(() => null);
626
+ if (typeof data?.code === "string" && /^[A-Za-z][A-Za-z0-9_]{0,99}$/.test(data.code))
627
+ Object.defineProperty(error, "apiCode", { value: data.code, enumerable: true });
628
+ if (res.status === 409 && data?.details)
629
+ Object.defineProperty(error, "reviewConflict", {
630
+ value: {
631
+ version: Number.isInteger(data.details.version) ? data.details.version : null,
632
+ submittedBy: typeof data.details.submittedBy === "string" ? data.details.submittedBy.slice(0, 200) : null,
633
+ submittedAt: typeof data.details.submittedAt === "string" ? data.details.submittedAt.slice(0, 40) : null
634
+ },
635
+ enumerable: true
636
+ });
637
+ throw error;
207
638
  }
208
639
  return res;
209
640
  }
@@ -703,6 +1134,9 @@ function fullJitterDelay(intervalMs, maxIntervalMs, attempt, random) {
703
1134
 
704
1135
  // src/sdk/client.ts
705
1136
  var Docmana = class {
1137
+ authoring;
1138
+ reviews;
1139
+ executionEvents;
706
1140
  config;
707
1141
  http;
708
1142
  constructor(config) {
@@ -714,6 +1148,9 @@ var Docmana = class {
714
1148
  getAccessToken: this.config.getAccessToken,
715
1149
  headers: this.config.headers
716
1150
  });
1151
+ this.authoring = new FormulaAuthoring(this.http);
1152
+ this.reviews = new Reviews(this.http);
1153
+ this.executionEvents = new ExecutionEvents(this.http);
717
1154
  }
718
1155
  async runFlowAsync(flowId, params, options = {}) {
719
1156
  const parts = await resolveInputs(params);
@@ -899,6 +1336,16 @@ function isTransientPollingError(error) {
899
1336
  DocmanaPermissionError,
900
1337
  DocmanaRequestError,
901
1338
  DocmanaServiceUnavailableError,
902
- DocmanaTimeoutError
1339
+ DocmanaTimeoutError,
1340
+ EXECUTION_NODE_EVENT_TYPES,
1341
+ ExecutionEvents,
1342
+ FormulaAuthoring,
1343
+ REVIEW_EVENT_TYPES,
1344
+ ReviewEventVerificationError,
1345
+ Reviews,
1346
+ parseExecutionEventCallback,
1347
+ parseReviewEventCallback,
1348
+ verifyExecutionEventCallback,
1349
+ verifyReviewEventCallback
903
1350
  });
904
1351
  //# sourceMappingURL=index.js.map