@superdurable/connector-docusign 0.1.0 → 0.2.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # DocuSign TypeScript connector
2
2
 
3
- `@superdurable/connector-docusign` provides five eSignature operations for Dex Flows: `createEnvelopeFromTemplate`, `getEnvelope`, `listEnvelopeRecipients`, `voidEnvelope`, and `downloadCombinedDocument`. The package uses the manifest's production and developer OAuth methods, with automatic refresh through the generated credential wiring. DocuSign Connect trigger delivery remains Go-only.
3
+ Install `@superdurable/connector-docusign@0.2.0` with `@superdurable/connectors-sdk@0.11.0` and `@superdurable/dex` 1.28 or newer. It provides five eSignature operations for Dex Flows: `createEnvelopeFromTemplate`, `getEnvelope`, `listEnvelopeRecipients`, `voidEnvelope`, and `downloadCombinedDocument`, and from 0.2.0 the `envelopeEventReceived` Connect Trigger. The package uses the manifest's production and developer OAuth methods, with automatic refresh through the generated credential wiring.
4
4
 
5
5
  The package exports generated operation-specific Step factories, typed models, mock cases, `DocuSignClient`, and `newProjectConnection`. Applications configure a named DocuSign connection in Dex Web using the [connector authorization guide](../README.md). Never put access tokens, refresh tokens, client secrets, Connect HMAC keys, or signed PDFs in Flow inputs, Attributes, Results, or logs.
6
6
 
@@ -9,3 +9,15 @@ The [envelope status example](examples/envelope-status/README.md) is a runnable
9
9
  The optional `CombinedDocumentStore` supplied to `DocuSignClient` stores a downloaded PDF outside Dex and returns a safe location of at most 1024 bytes. Without a store, `downloadCombinedDocument` returns only its byte count and SHA-256 digest. A store must consume the whole content atomically and be safe to call again after a retry.
10
10
 
11
11
  From this package directory, run `npm ci`, `npm run check`, and `npm run test:integration` with `DEX_FLOW_SERVICE_ADDRESS` pointing to a running `dexcli dev` server.
12
+
13
+ ## Triggers
14
+
15
+ From 0.2.0, `envelopeEventReceived` receives HMAC-verified DocuSign Connect `envelope-completed`, `envelope-declined`, and `envelope-voided` events in the JSON SIM format, at parity with the Go connector. The runnable [envelope outcome example](examples/envelope-outcome/README.md) sends an envelope tagged with a hidden `dexSigningRequestId` custom field and completes when Connect reports its outcome through a typed RPC.
16
+
17
+ - `newProjectEnvelopeEventReceivedEndpointRunner(project, connectionName, routes)` opens the connection and each route's binding from the project configuration and wraps every target in the binding's durable project inbox, so an event is stored before Connect receives `200`. Mount `runner.serveHTTP` at the Connect configuration's URL to Publish and run `runner.run(context)` for the life of the process; it replays pending inbox events first. Wire targets with `newDexRPCTriggerTarget` or `newDexFlowTriggerTarget` from `@superdurable/connectors-sdk`. `runner.runningSourceCount()` serves a readiness check.
18
+ - The endpoint accepts only `POST` up to `connectMaxBodyBytes` (`413` above it, before the signature is checked), and accepts any `X-DocuSign-Signature-<n>` header equal to the base64 HMAC-SHA256 of the raw body under `connect_hmac_key`, with `"` removed from the key, in constant time. A bad signature or a body that is not a JSON SIM envelope event with GUID identities answers `400`. A connection without a key, a moment with no running binding, and a failed inbox write answer `503`, so Connect retries. Another Connect event, such as `recipient-completed`, and an outcome the binding's `events` filter excludes are acknowledged with `200` and not recorded.
19
+ - The event ID is the lowercase envelope ID and the event name, such as `93be49ab-0000-0000-0000-f752070d71ec:envelope-completed`, so a Connect retry or a second configuration delivering the same outcome keeps one ID. `occurredAt` is Connect's `generatedDateTime`, or the receipt time when it is blank or unparsable. The payload carries the lowercase account ID, the envelope summary's lowercase `status`, `voidedReason`, and named text `customFields` when the configuration includes envelope data; `envelopeEventCustomFieldValue` reads one field.
20
+ - The binding configuration is `{"events": [...]}` with distinct values from the three outcomes; empty or omitted records all three. The connector declares no Studio units, so `defineEnvelopeEventReceivedTriggerBinding` needs no `configurationUI` and Dex Web saves the binding as `{}`.
21
+ - `envelopeEventReceivedWebhookEndpoint(connection)` and `newEnvelopeEventReceivedTrigger` build the same endpoint and a standalone runner without the project inbox; every Trigger of one connection shares one endpoint.
22
+
23
+ The unit tests mirror the Go endpoint tests for signatures, forgeries, filters, oversized and XML bodies, a missing key, and the inbox. The integration test runs the example against a real Dex Server with a local DocuSign stand-in: it refuses a forged delivery, consumes an uncorrelated one, resumes the Flow once from a signed outcome and its redelivery, skips a filtered outcome, and replays an inbox event recorded before a restart. Live DocuSign Connect deliveries have not been verified for this package.
@@ -4,13 +4,15 @@
4
4
  "package": {
5
5
  "ecosystem": "npm",
6
6
  "name": "@superdurable/connector-docusign",
7
- "version": "0.1.0"
7
+ "version": "0.2.0"
8
8
  },
9
9
  "factories": [
10
10
  {
11
11
  "name": "newCreateEnvelopeFromTemplateStep",
12
12
  "kind": "mutation",
13
13
  "operationId": "createEnvelopeFromTemplate",
14
+ "inputType": "CreateEnvelopeFromTemplateInput",
15
+ "description": "Create one envelope from a server template, filling its roles in their routing order, and send it or keep it as a draft. DocuSign has no idempotency key, so the call runs with sync durability, tags the envelope with a hidden custom field derived from the Step's idempotency key, and on every later attempt looks for that envelope by the field before sending again; an outcome it cannot confirm selects uncertain.",
14
16
  "fields": {
15
17
  "stepType": "stepType",
16
18
  "annotations": "annotations",
@@ -38,12 +40,107 @@
38
40
  "field": "defect",
39
41
  "optional": true
40
42
  }
41
- ]
43
+ ],
44
+ "inputSchema": {
45
+ "$defs": {
46
+ "EnvelopeCustomField": {
47
+ "additionalProperties": false,
48
+ "description": "EnvelopeCustomField is one envelope text custom field, hidden from recipients, such as a correlation ID that a Connect event carries back.",
49
+ "properties": {
50
+ "name": {
51
+ "description": "Name is the field name, 1 to 50 characters.",
52
+ "type": "string"
53
+ },
54
+ "value": {
55
+ "description": "Value is the field value, at most 100 characters.",
56
+ "type": "string"
57
+ }
58
+ },
59
+ "required": [
60
+ "name",
61
+ "value"
62
+ ],
63
+ "type": "object"
64
+ },
65
+ "TemplateRole": {
66
+ "additionalProperties": false,
67
+ "description": "TemplateRole assigns one person to a template role.",
68
+ "properties": {
69
+ "email": {
70
+ "description": "Email is the recipient's email address, at most 100 characters.",
71
+ "type": "string"
72
+ },
73
+ "name": {
74
+ "description": "Name is the recipient's full name, 1 to 100 characters.",
75
+ "type": "string"
76
+ },
77
+ "roleName": {
78
+ "description": "RoleName is the template's role name, such as Customer, matched as DocuSign matches it.",
79
+ "type": "string"
80
+ },
81
+ "routingOrder": {
82
+ "description": "RoutingOrder overrides the role's routing order, 1 to 999; zero keeps the template's order, which decides who is asked to sign first.",
83
+ "type": "integer"
84
+ }
85
+ },
86
+ "required": [
87
+ "email",
88
+ "name",
89
+ "roleName"
90
+ ],
91
+ "type": "object"
92
+ }
93
+ },
94
+ "additionalProperties": false,
95
+ "description": "CreateEnvelopeFromTemplateInput describes one envelope created from a DocuSign server template.",
96
+ "properties": {
97
+ "customFields": {
98
+ "description": "CustomFields adds hidden text custom fields, at most 10, such as a correlation ID that Connect events return. Names are 1 to 50 characters and unique; dexIdempotencyKey is reserved.",
99
+ "items": {
100
+ "$ref": "#/$defs/EnvelopeCustomField"
101
+ },
102
+ "type": "array"
103
+ },
104
+ "emailBlurb": {
105
+ "description": "EmailBlurb overrides the template's email message, at most 10,000 characters; blank keeps it.",
106
+ "type": "string"
107
+ },
108
+ "emailSubject": {
109
+ "description": "EmailSubject overrides the template's email subject, at most 100 characters; blank keeps it.",
110
+ "type": "string"
111
+ },
112
+ "isDraft": {
113
+ "description": "IsDraft creates the envelope with status created and sends nothing; false sends it at once.",
114
+ "type": "boolean"
115
+ },
116
+ "templateId": {
117
+ "description": "TemplateID is the server template's GUID, shown in DocuSign under Templates > the template > Template ID.",
118
+ "type": "string"
119
+ },
120
+ "templateRoles": {
121
+ "description": "TemplateRoles fills the template's roles by name, 1 to 100 of them. Every role the template requires must be filled.",
122
+ "items": {
123
+ "$ref": "#/$defs/TemplateRole"
124
+ },
125
+ "type": [
126
+ "array",
127
+ "null"
128
+ ]
129
+ }
130
+ },
131
+ "required": [
132
+ "templateId",
133
+ "templateRoles"
134
+ ],
135
+ "type": "object"
136
+ }
42
137
  },
43
138
  {
44
139
  "name": "newGetEnvelopeStep",
45
140
  "kind": "query",
46
141
  "operationId": "getEnvelope",
142
+ "inputType": "GetEnvelopeInput",
143
+ "description": "Read one envelope's status, status timestamps, void reason, and text custom fields. DocuSign allows an app one status read per envelope every 15 minutes, so prefer the envelopeEventReceived Trigger and poll only as a fallback.",
47
144
  "fields": {
48
145
  "stepType": "stepType",
49
146
  "annotations": "annotations",
@@ -76,12 +173,28 @@
76
173
  "field": "defect",
77
174
  "optional": true
78
175
  }
79
- ]
176
+ ],
177
+ "inputSchema": {
178
+ "additionalProperties": false,
179
+ "description": "GetEnvelopeInput names the envelope to read.",
180
+ "properties": {
181
+ "envelopeId": {
182
+ "description": "EnvelopeID is the envelope's GUID, such as a createEnvelopeFromTemplate Result's envelopeId.",
183
+ "type": "string"
184
+ }
185
+ },
186
+ "required": [
187
+ "envelopeId"
188
+ ],
189
+ "type": "object"
190
+ }
80
191
  },
81
192
  {
82
193
  "name": "newListEnvelopeRecipientsStep",
83
194
  "kind": "query",
84
195
  "operationId": "listEnvelopeRecipients",
196
+ "inputType": "ListEnvelopeRecipientsInput",
197
+ "description": "List an envelope's recipients in routing order with each one's type, role, status, and sent, delivered, signed, and declined times, and the routing order DocuSign is currently waiting on.",
85
198
  "fields": {
86
199
  "stepType": "stepType",
87
200
  "annotations": "annotations",
@@ -114,12 +227,28 @@
114
227
  "field": "defect",
115
228
  "optional": true
116
229
  }
117
- ]
230
+ ],
231
+ "inputSchema": {
232
+ "additionalProperties": false,
233
+ "description": "ListEnvelopeRecipientsInput names the envelope whose recipients to list.",
234
+ "properties": {
235
+ "envelopeId": {
236
+ "description": "EnvelopeID is the envelope's GUID.",
237
+ "type": "string"
238
+ }
239
+ },
240
+ "required": [
241
+ "envelopeId"
242
+ ],
243
+ "type": "object"
244
+ }
118
245
  },
119
246
  {
120
247
  "name": "newVoidEnvelopeStep",
121
248
  "kind": "mutation",
122
249
  "operationId": "voidEnvelope",
250
+ "inputType": "VoidEnvelopeInput",
251
+ "description": "Void one sent or delivered envelope with a reason, the compensation for a send that must not complete. An envelope that is already voided also selects voided, so a repeated call is safe; a completed, declined, or draft envelope selects notVoidable with its status.",
123
252
  "fields": {
124
253
  "stepType": "stepType",
125
254
  "annotations": "annotations",
@@ -157,12 +286,33 @@
157
286
  "field": "defect",
158
287
  "optional": true
159
288
  }
160
- ]
289
+ ],
290
+ "inputSchema": {
291
+ "additionalProperties": false,
292
+ "description": "VoidEnvelopeInput names the envelope to void and why.",
293
+ "properties": {
294
+ "envelopeId": {
295
+ "description": "EnvelopeID is the envelope's GUID.",
296
+ "type": "string"
297
+ },
298
+ "voidedReason": {
299
+ "description": "VoidedReason is shown to the recipients, 1 to 200 characters, such as \"Signing deadline passed\".",
300
+ "type": "string"
301
+ }
302
+ },
303
+ "required": [
304
+ "envelopeId",
305
+ "voidedReason"
306
+ ],
307
+ "type": "object"
308
+ }
161
309
  },
162
310
  {
163
311
  "name": "newDownloadCombinedDocumentStep",
164
312
  "kind": "query",
165
313
  "operationId": "downloadCombinedDocument",
314
+ "inputType": "DownloadCombinedDocumentInput",
315
+ "description": "Stream an envelope's documents as one combined PDF, optionally with the certificate of completion, through a SHA-256 digest and into the application's CombinedDocumentStore when one is configured. The Result holds the byte count, digest, and the store's location, never the PDF bytes.",
166
316
  "fields": {
167
317
  "stepType": "stepType",
168
318
  "annotations": "annotations",
@@ -200,7 +350,35 @@
200
350
  "field": "defect",
201
351
  "optional": true
202
352
  }
203
- ]
353
+ ],
354
+ "inputSchema": {
355
+ "additionalProperties": false,
356
+ "description": "DownloadCombinedDocumentInput names the envelope whose documents to download.",
357
+ "properties": {
358
+ "envelopeId": {
359
+ "description": "EnvelopeID is the envelope's GUID, normally of a completed envelope.",
360
+ "type": "string"
361
+ },
362
+ "includeCertificate": {
363
+ "description": "IncludeCertificate appends DocuSign's certificate of completion to the combined PDF.",
364
+ "type": "boolean"
365
+ }
366
+ },
367
+ "required": [
368
+ "envelopeId"
369
+ ],
370
+ "type": "object"
371
+ }
372
+ },
373
+ {
374
+ "name": "defineEnvelopeEventReceivedTriggerBinding",
375
+ "kind": "trigger",
376
+ "triggerName": "envelopeEventReceived",
377
+ "fields": {
378
+ "connectionName": "connectionName",
379
+ "bindingName": "bindingName",
380
+ "configurationUI": "configurationUI"
381
+ }
204
382
  }
205
383
  ]
206
384
  }
@@ -212,7 +212,9 @@ var CreateEnvelopeFromTemplateDefinition = {
212
212
  { id: "uncertain", description: "The create request was sent but DocuSign's answer was lost, a server error, or unreadable, and no envelope with this Step's custom field could be found, so an envelope may exist that this Result does not name; do not create another automatically.", optional: true },
213
213
  { id: "defect", description: "Local input, connection configuration, or connector definition is invalid.", optional: true }
214
214
  ],
215
- stepDefaults: { executeMethodTimeoutMs: 6e4, executeDurability: "sync", executeRetry: { initialIntervalMs: 1e3, backoffCoefficient: 2, maximumIntervalMs: 6e4, maximumAttempts: 6, totalDurationMs: 39e5 } }
215
+ stepDefaults: { executeMethodTimeoutMs: 6e4, executeDurability: "sync", executeRetry: { initialIntervalMs: 1e3, backoffCoefficient: 2, maximumIntervalMs: 6e4, maximumAttempts: 6, totalDurationMs: 39e5 } },
216
+ repeatOnUnknown: "limited",
217
+ maxSends: 3
216
218
  };
217
219
  var CreateEnvelopeFromTemplateMetadata = { kind: "mutation", idempotency: "required", authorization: "required" };
218
220
  var GetEnvelopeBranch = {
@@ -287,7 +289,9 @@ var VoidEnvelopeDefinition = {
287
289
  { id: "invalidResponse", description: "DocuSign returned a malformed or oversized response.", optional: true },
288
290
  { id: "defect", description: "Local input, connection configuration, or connector definition is invalid.", optional: true }
289
291
  ],
290
- stepDefaults: { executeMethodTimeoutMs: 3e4, executeDurability: "async", executeRetry: { initialIntervalMs: 1e3, backoffCoefficient: 2, maximumIntervalMs: 6e4, maximumAttempts: 0, totalDurationMs: 39e5 } }
292
+ stepDefaults: { executeMethodTimeoutMs: 3e4, executeDurability: "async", executeRetry: { initialIntervalMs: 1e3, backoffCoefficient: 2, maximumIntervalMs: 6e4, maximumAttempts: 0, totalDurationMs: 39e5 } },
293
+ repeatOnUnknown: "limited",
294
+ maxSends: 3
291
295
  };
292
296
  var VoidEnvelopeMetadata = { kind: "mutation", idempotency: "required", authorization: "required" };
293
297
  var DownloadCombinedDocumentBranch = {
@@ -360,4 +360,4 @@ declare function newConnection(client: Operations, reference: ConnectionRef): Co
360
360
  /** Bind scripted operations to a named connection. */
361
361
  declare function newConnectionWithOperations(operations: Operations, reference: ConnectionRef): Connection;
362
362
 
363
- export { newConnectionWithOperations as A, type CreateEnvelopeFromTemplateInput as C, type DownloadCombinedDocumentInput as D, type Envelope as E, type GetEnvelopeInput as G, type ListEnvelopeRecipientsInput as L, type Operations as O, type Recipient as R, type TemplateRole as T, type VoidEnvelopeInput as V, type CreatedEnvelope as a, Connection as b, type CombinedDocument as c, type EnvelopeRecipients as d, type VoidedEnvelope as e, type CredentialSource as f, type Config as g, type Credentials as h, CombinedDocumentSchema as i, ConfigSchema as j, CreateEnvelopeFromTemplateInputSchema as k, CreatedEnvelopeSchema as l, DownloadCombinedDocumentInputSchema as m, type EnvelopeCustomField as n, EnvelopeCustomFieldSchema as o, EnvelopeRecipientsSchema as p, EnvelopeSchema as q, GetEnvelopeInputSchema as r, ListEnvelopeRecipientsInputSchema as s, RecipientSchema as t, TemplateRoleSchema as u, VoidEnvelopeInputSchema as v, VoidedEnvelopeSchema as w, decodeCredentials as x, decodeSharedCredentials as y, newConnection as z };
363
+ export { newConnectionWithOperations as A, type CreateEnvelopeFromTemplateInput as C, type DownloadCombinedDocumentInput as D, type Envelope as E, type GetEnvelopeInput as G, type ListEnvelopeRecipientsInput as L, type Operations as O, type Recipient as R, type TemplateRole as T, type VoidEnvelopeInput as V, type CreatedEnvelope as a, Connection as b, type CombinedDocument as c, type EnvelopeRecipients as d, type VoidedEnvelope as e, type CredentialSource as f, type Credentials as g, type Config as h, type EnvelopeCustomField as i, CombinedDocumentSchema as j, ConfigSchema as k, CreateEnvelopeFromTemplateInputSchema as l, CreatedEnvelopeSchema as m, DownloadCombinedDocumentInputSchema as n, EnvelopeCustomFieldSchema as o, EnvelopeRecipientsSchema as p, EnvelopeSchema as q, GetEnvelopeInputSchema as r, ListEnvelopeRecipientsInputSchema as s, RecipientSchema as t, TemplateRoleSchema as u, VoidEnvelopeInputSchema as v, VoidedEnvelopeSchema as w, decodeCredentials as x, decodeSharedCredentials as y, newConnection as z };
package/dist/index.d.ts CHANGED
@@ -1,11 +1,14 @@
1
- import { C as CreateEnvelopeFromTemplateInput, a as CreatedEnvelope, b as Connection, D as DownloadCombinedDocumentInput, c as CombinedDocument, G as GetEnvelopeInput, E as Envelope, L as ListEnvelopeRecipientsInput, d as EnvelopeRecipients, V as VoidEnvelopeInput, e as VoidedEnvelope, f as CredentialSource, g as Config, h as Credentials, O as Operations } from './connection-YEptevxS.js';
2
- export { i as CombinedDocumentSchema, j as ConfigSchema, k as CreateEnvelopeFromTemplateInputSchema, l as CreatedEnvelopeSchema, m as DownloadCombinedDocumentInputSchema, n as EnvelopeCustomField, o as EnvelopeCustomFieldSchema, p as EnvelopeRecipientsSchema, q as EnvelopeSchema, r as GetEnvelopeInputSchema, s as ListEnvelopeRecipientsInputSchema, R as Recipient, t as RecipientSchema, T as TemplateRole, u as TemplateRoleSchema, v as VoidEnvelopeInputSchema, w as VoidedEnvelopeSchema, x as decodeCredentials, y as decodeSharedCredentials, z as newConnection, A as newConnectionWithOperations } from './connection-YEptevxS.js';
1
+ import { C as CreateEnvelopeFromTemplateInput, a as CreatedEnvelope, b as Connection, D as DownloadCombinedDocumentInput, c as CombinedDocument, G as GetEnvelopeInput, E as Envelope, L as ListEnvelopeRecipientsInput, d as EnvelopeRecipients, V as VoidEnvelopeInput, e as VoidedEnvelope, f as CredentialSource, g as Credentials, h as Config, O as Operations, i as EnvelopeCustomField } from './connection-BFvLyRgn.js';
2
+ export { j as CombinedDocumentSchema, k as ConfigSchema, l as CreateEnvelopeFromTemplateInputSchema, m as CreatedEnvelopeSchema, n as DownloadCombinedDocumentInputSchema, o as EnvelopeCustomFieldSchema, p as EnvelopeRecipientsSchema, q as EnvelopeSchema, r as GetEnvelopeInputSchema, s as ListEnvelopeRecipientsInputSchema, R as Recipient, t as RecipientSchema, T as TemplateRole, u as TemplateRoleSchema, v as VoidEnvelopeInputSchema, w as VoidedEnvelopeSchema, x as decodeCredentials, y as decodeSharedCredentials, z as newConnection, A as newConnectionWithOperations } from './connection-BFvLyRgn.js';
3
3
  import * as _superdurable_connectors_sdk from '@superdurable/connectors-sdk';
4
- import { MutationDefinition, QueryDefinition, MutationStepConfig, Mutation, MutationResult, QueryStepConfig, Query, QueryResult, MutationStep, QueryStep, Call, QueryAttempt, MutationAttempt, Failure, ReceiptDraft } from '@superdurable/connectors-sdk';
4
+ import { MutationDefinition, QueryDefinition, MutationStepConfig, Mutation, MutationResult, QueryStepConfig, Query, QueryResult, MutationStep, QueryStep, Call, QueryAttempt, MutationAttempt, TriggerLogger, Failure, ReceiptDraft, TriggerEvent, TriggerTarget, TriggerRunner, TriggerContext, ConnectorConfigurationUI, TriggerDefinition, TriggerBindingDefinition } from '@superdurable/connectors-sdk';
5
5
  import { StepTarget } from '@superdurable/dex';
6
- import { LoadedProject } from '@superdurable/connectors-sdk/projectconfig';
6
+ import { LoadedProject, TriggerInboxKey, Configuration } from '@superdurable/connectors-sdk/projectconfig';
7
7
  import { ProviderRequest, ProviderResponse } from '@superdurable/connectors-sdk/providerhttp';
8
- import 'zod';
8
+ import { ManifestCredentialRefreshDriver } from '@superdurable/connectors-sdk/oauthtoken';
9
+ import { IncomingHttpHeaders, IncomingMessage, ServerResponse } from 'node:http';
10
+ import { z } from 'zod';
11
+ import { WebhookEndpoint } from '@superdurable/connectors-sdk/webhooktrigger';
9
12
 
10
13
  /** Stable connector identity. */
11
14
  declare const ConnectorID = "docusign";
@@ -259,7 +262,7 @@ declare class CreateEnvelopeFromTemplateOperation implements Mutation<CreateEnve
259
262
  /** Bind the authenticated DocuSign client. */
260
263
  constructor(client: DocuSignClient);
261
264
  /** Return manifest routes and Execute defaults. */
262
- definition(): _superdurable_connectors_sdk.QueryDefinition;
265
+ definition(): _superdurable_connectors_sdk.MutationDefinition;
263
266
  /** Use the stable Dex Call ID as the hidden deduplication marker. */
264
267
  idempotencyKey(callId: string): string;
265
268
  /** Invoke one safe-to-classify create attempt. */
@@ -291,7 +294,7 @@ declare class VoidEnvelopeOperation implements Mutation<VoidEnvelopeInput, Voide
291
294
  /** Bind the authenticated DocuSign client. */
292
295
  constructor(client: DocuSignClient);
293
296
  /** Return manifest routes and Execute defaults. */
294
- definition(): _superdurable_connectors_sdk.QueryDefinition;
297
+ definition(): _superdurable_connectors_sdk.MutationDefinition;
295
298
  /** Use the stable Call ID for this safely repeatable PUT. */
296
299
  idempotencyKey(callId: string): string;
297
300
  /** Invoke one void attempt. */
@@ -327,6 +330,8 @@ interface DocuSignClientOptions {
327
330
  now?: () => Date;
328
331
  /** Persist a combined PDF outside Dex payloads. */
329
332
  documentStore?: CombinedDocumentStore;
333
+ /** Receive the envelopeEventReceived Trigger's delivery records; omitted uses the SDK's console logger. */
334
+ logger?: TriggerLogger;
330
335
  }
331
336
  /** One provider answer; oversized applies only to successful JSON bodies. */
332
337
  interface DocuSignResponse {
@@ -363,12 +368,17 @@ declare class DocuSignClient {
363
368
  readonly maxResponseBytes: number;
364
369
  readonly maxDocumentBytes: number;
365
370
  readonly documentStore: CombinedDocumentStore | undefined;
371
+ /** Largest Connect request body the envelopeEventReceived endpoint accepts. */
372
+ readonly connectMaxBodyBytes: number;
373
+ /** Destination of the envelopeEventReceived Trigger's records. */
374
+ readonly triggerLogger: TriggerLogger;
366
375
  private readonly accountId;
367
376
  private readonly send;
368
377
  private readonly hasCustomSend;
369
378
  private readonly now;
370
379
  private readonly accountCache;
371
- private readonly refreshDriver;
380
+ /** @internal Refreshes expiring OAuth credentials for operations and the Connect endpoint. */
381
+ readonly refreshDriver: ManifestCredentialRefreshDriver<Credentials>;
372
382
  /** Validate connection settings and bind host-owned renewable credentials. */
373
383
  constructor(config: Config, credentials: CredentialSource, options?: DocuSignClientOptions);
374
384
  /** Bind the template creation Mutation. */
@@ -399,4 +409,145 @@ declare class DocuSignClient {
399
409
  /** Open a named DocuSign connection with generated private and shared credential decoders. */
400
410
  declare function newProjectConnection(project: LoadedProject, connectionName: string, options?: DocuSignClientOptions): Connection<Operations>;
401
411
 
402
- export { CombinedDocument, type CombinedDocumentReference, type CombinedDocumentStore, Config, Connection, ConnectorID, CreateEnvelopeFromTemplateBranch, CreateEnvelopeFromTemplateBranchCreated, CreateEnvelopeFromTemplateBranchDefect, CreateEnvelopeFromTemplateBranchProviderRejected, CreateEnvelopeFromTemplateBranchUncertain, CreateEnvelopeFromTemplateDefinition, CreateEnvelopeFromTemplateInput, CreateEnvelopeFromTemplateMetadata, CreateEnvelopeFromTemplateOperation, type CreateEnvelopeFromTemplateStepConfig, CreatedEnvelope, CredentialSource, Credentials, DocuSignClient, type DocuSignClientOptions, DownloadCombinedDocumentBranch, DownloadCombinedDocumentBranchDefect, DownloadCombinedDocumentBranchDownloaded, DownloadCombinedDocumentBranchInvalidResponse, DownloadCombinedDocumentBranchNotFound, DownloadCombinedDocumentBranchProviderRejected, DownloadCombinedDocumentBranchTooLarge, DownloadCombinedDocumentDefinition, DownloadCombinedDocumentInput, DownloadCombinedDocumentMetadata, DownloadCombinedDocumentOperation, type DownloadCombinedDocumentStepConfig, Envelope, EnvelopeRecipients, GetEnvelopeBranch, GetEnvelopeBranchDefect, GetEnvelopeBranchFound, GetEnvelopeBranchInvalidResponse, GetEnvelopeBranchNotFound, GetEnvelopeBranchProviderRejected, GetEnvelopeDefinition, GetEnvelopeInput, GetEnvelopeMetadata, GetEnvelopeOperation, type GetEnvelopeStepConfig, ListEnvelopeRecipientsBranch, ListEnvelopeRecipientsBranchDefect, ListEnvelopeRecipientsBranchInvalidResponse, ListEnvelopeRecipientsBranchListed, ListEnvelopeRecipientsBranchNotFound, ListEnvelopeRecipientsBranchProviderRejected, ListEnvelopeRecipientsDefinition, ListEnvelopeRecipientsInput, ListEnvelopeRecipientsMetadata, ListEnvelopeRecipientsOperation, type ListEnvelopeRecipientsStepConfig, ProviderID, VoidEnvelopeBranch, VoidEnvelopeBranchDefect, VoidEnvelopeBranchInvalidResponse, VoidEnvelopeBranchNotFound, VoidEnvelopeBranchNotVoidable, VoidEnvelopeBranchProviderRejected, VoidEnvelopeBranchVoided, VoidEnvelopeDefinition, VoidEnvelopeInput, VoidEnvelopeMetadata, VoidEnvelopeOperation, type VoidEnvelopeStepConfig, VoidedEnvelope, newCreateEnvelopeFromTemplateStep, newDownloadCombinedDocumentStep, newGetEnvelopeStep, newListEnvelopeRecipientsStep, newProjectConnection, newVoidEnvelopeStep };
412
+ /** The Connect event sent when every recipient finished. */
413
+ declare const ConnectEventEnvelopeCompleted = "envelope-completed";
414
+ /** The Connect event sent when a recipient declined. */
415
+ declare const ConnectEventEnvelopeDeclined = "envelope-declined";
416
+ /** The Connect event sent when the sender voided the envelope or it expired. */
417
+ declare const ConnectEventEnvelopeVoided = "envelope-voided";
418
+ /** The Connect events the envelopeEventReceived Trigger records. */
419
+ declare const ConnectEnvelopeEvents: readonly string[];
420
+ /** One envelope Connect event name. */
421
+ type ConnectEnvelopeEvent = typeof ConnectEventEnvelopeCompleted | typeof ConnectEventEnvelopeDeclined | typeof ConnectEventEnvelopeVoided;
422
+ /**
423
+ * Filters the Connect events one binding records. An empty or omitted events list records every
424
+ * envelope-completed, envelope-declined, and envelope-voided event. It reduces what a binding stores; the
425
+ * application's TriggerFilter remains its admission rule.
426
+ */
427
+ declare const EnvelopeEventReceivedTriggerConfigurationSchema: z.ZodObject<{
428
+ events: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
429
+ }, z.core.$strict>;
430
+ /** Event filter of one envelopeEventReceived binding. */
431
+ type EnvelopeEventReceivedTriggerConfiguration = z.infer<typeof EnvelopeEventReceivedTriggerConfigurationSchema>;
432
+ /**
433
+ * One HMAC-verified Connect envelope event in the JSON SIM format; field names and order match the Go
434
+ * connector. Its Trigger event ID is the envelope ID and the event name, such as
435
+ * 93be49ab-0000-0000-0000-f752070d71ec:envelope-completed, so a Connect retry or a second Connect
436
+ * configuration delivering the same outcome keeps one ID.
437
+ */
438
+ interface EnvelopeEvent {
439
+ /** envelope-completed, envelope-declined, or envelope-voided. */ event: string;
440
+ /** The envelope's lowercase GUID. */ envelopeId: string;
441
+ /** The sender's lowercase DocuSign API account GUID. */ accountId: string;
442
+ /** When Connect generated the message, or when the endpoint received it if Connect left it blank, as RFC 3339 UTC. */ generatedAt: string;
443
+ /** The envelope summary's lowercase status; absent unless the Connect configuration includes envelope data. */ status?: Envelope['status'];
444
+ /** The envelope summary's void reason for an envelope-voided event, when included. */ voidedReason?: string;
445
+ /** The envelope's text custom fields; absent unless the Connect configuration's Include Data lists Custom Fields. */ customFields?: EnvelopeCustomField[];
446
+ }
447
+ /** Runtime validator of a replayed EnvelopeEvent payload. */
448
+ declare const EnvelopeEventSchema: z.ZodPipe<z.ZodObject<{
449
+ event: z.ZodString;
450
+ envelopeId: z.ZodString;
451
+ accountId: z.ZodString;
452
+ generatedAt: z.ZodString;
453
+ status: z.ZodOptional<z.ZodString>;
454
+ voidedReason: z.ZodOptional<z.ZodString>;
455
+ customFields: z.ZodOptional<z.ZodArray<z.ZodObject<{
456
+ name: z.ZodString;
457
+ value: z.ZodString;
458
+ }, z.core.$strip>>>;
459
+ }, z.core.$strip>, z.ZodTransform<EnvelopeEvent, {
460
+ event: string;
461
+ envelopeId: string;
462
+ accountId: string;
463
+ generatedAt: string;
464
+ status?: string | undefined;
465
+ voidedReason?: string | undefined;
466
+ customFields?: {
467
+ name: string;
468
+ value: string;
469
+ }[] | undefined;
470
+ }>>;
471
+ /** Return the value of the event's named text custom field, or undefined when it has none. */
472
+ declare function envelopeEventCustomFieldValue(event: EnvelopeEvent, name: string): string | undefined;
473
+ /** A Connect message that is not a JSON SIM envelope event with GUID identities. */
474
+ declare class ConnectMessageMalformedError extends Error {
475
+ constructor();
476
+ }
477
+ /** No X-DocuSign-Signature-N header matches the HMAC-SHA256 of the body. */
478
+ declare class ConnectSignatureInvalidError extends Error {
479
+ constructor();
480
+ }
481
+ /** Accept any X-DocuSign-Signature-N whose standard base64 equals the body's HMAC-SHA256 under hmacKey. */
482
+ declare function verifyConnectSignature(body: Uint8Array, headers: IncomingHttpHeaders, hmacKey: string): void;
483
+ /**
484
+ * Decode a JSON SIM envelope outcome received at receivedAt. Another valid Connect event returns
485
+ * undefined, so the endpoint acknowledges it unrecorded; anything else throws ConnectMessageMalformedError.
486
+ */
487
+ declare function decodeConnectMessage(body: Uint8Array, receivedAt: Date): TriggerEvent<EnvelopeEvent> | undefined;
488
+
489
+ /** Manifest definition of envelopeEventReceived. */
490
+ declare const EnvelopeEventReceivedTriggerDefinition: TriggerDefinition;
491
+ /** One static application binding of envelopeEventReceived. */
492
+ interface EnvelopeEventReceivedTriggerBindingConfig {
493
+ /** Static connection name. */ connectionName: string;
494
+ /** Static binding name. */ bindingName: string;
495
+ /** Optional Connector Studio configuration units; the connector declares none, so Dex Web saves the binding as {}, which records all three outcomes. */ configurationUI?: ConnectorConfigurationUI;
496
+ }
497
+ /** Declare a static envelopeEventReceived binding for Flow Definition tooling and Dex Web. */
498
+ declare function defineEnvelopeEventReceivedTriggerBinding(config: EnvelopeEventReceivedTriggerBindingConfig): TriggerBindingDefinition;
499
+ /** Configuration of one standalone envelopeEventReceived Trigger. */
500
+ interface EnvelopeEventReceivedTriggerConfig {
501
+ /** DocuSign connection from newProjectConnection or newConnection with a DocuSignClient. */ connection: Connection;
502
+ /** Static connection name; must equal the connection's name. */ connectionName: string;
503
+ /** Static binding name. */ bindingName: string;
504
+ /** The binding's event filter. */ configuration: EnvelopeEventReceivedTriggerConfiguration;
505
+ /** Routes accepted events into the application. */ target: TriggerTarget<EnvelopeEvent>;
506
+ }
507
+ /**
508
+ * Create one envelopeEventReceived Trigger fed by the connection's shared Connect endpoint, which
509
+ * envelopeEventReceivedWebhookEndpoint returns for the application to mount. Every Trigger of one
510
+ * connection shares that endpoint. Throws a TypeError or the schema's error on invalid wiring.
511
+ */
512
+ declare function newEnvelopeEventReceivedTrigger(config: EnvelopeEventReceivedTriggerConfig): TriggerRunner;
513
+ /**
514
+ * Return the connection's Connect endpoint, creating it on first use, for an application to mount at the
515
+ * URL to Publish of its Connect configuration. Every envelopeEventReceived Trigger built from this
516
+ * connection feeds from it. It answers 503 while none of them runs and without a connect_hmac_key, so
517
+ * Connect retries; 400 for a bad signature or a body that is not JSON SIM; 413 above connectMaxBodyBytes,
518
+ * before the signature is checked; and 200 for another Connect event, unrecorded.
519
+ */
520
+ declare function envelopeEventReceivedWebhookEndpoint(connection: Connection): WebhookEndpoint<Credentials, EnvelopeEvent>;
521
+ /** One envelopeEventReceived binding from the project configuration and its application target. */
522
+ interface ProjectEnvelopeEventReceivedTriggerRoute {
523
+ /** Configured binding name whose configuration filters the events. */ bindingName: string;
524
+ /** Receives the binding's events through its durable project inbox, such as a newDexRPCTriggerTarget that resumes the waiting Flow. */ target: TriggerTarget<EnvelopeEvent>;
525
+ }
526
+ /**
527
+ * Serves one connection's Connect endpoint while running its bindings. Mount serveHTTP at the Connect
528
+ * configuration's URL to Publish and run it for the life of the process. It is safe for concurrent requests.
529
+ */
530
+ declare class EnvelopeEventReceivedEndpointRunner {
531
+ #private;
532
+ /** @internal Use newProjectEnvelopeEventReceivedEndpointRunner. */
533
+ constructor(endpoint: WebhookEndpoint<Credentials, EnvelopeEvent>, runners: readonly TriggerRunner[]);
534
+ /** Verify, record, and acknowledge one Connect delivery. It answers 503 until run has replayed every binding's pending events and started receiving, so Connect retries. */
535
+ serveHTTP(request: IncomingMessage, response: ServerResponse): Promise<void>;
536
+ /** Replay each binding's durable inbox, then deliver new events until the context ends, rejecting with TriggerDeliveryAbortedError, or a binding fails. */
537
+ run(context: TriggerContext): Promise<void>;
538
+ /** How many bindings are receiving events; a readiness check can wait until it equals the number of routes. */
539
+ runningSourceCount(): number;
540
+ }
541
+ /**
542
+ * Open connectionName and every route's binding from the loaded project and wrap each target in the
543
+ * binding's durable project inbox, so an event is stored before Connect receives 200. A route whose
544
+ * binding is not configured throws ObjectNotFoundError; an invalid configuration, a blank or duplicated
545
+ * binding name, or no routes throws. The client options' logger also applies to the inboxes.
546
+ */
547
+ declare function newProjectEnvelopeEventReceivedEndpointRunner(project: LoadedProject, connectionName: string, routes: readonly ProjectEnvelopeEventReceivedTriggerRoute[], options?: DocuSignClientOptions): EnvelopeEventReceivedEndpointRunner;
548
+ /** Wraps one binding's target so each event is stored before its acknowledgement. */
549
+ type DurableEnvelopeEventReceivedTarget = (key: TriggerInboxKey, target: TriggerTarget<EnvelopeEvent>) => TriggerTarget<EnvelopeEvent>;
550
+ /** @internal Run routes on connection with the bindings that configuration stores, wrapping each target with makeDurable. */
551
+ declare function newEnvelopeEventReceivedEndpointRunner(connection: Connection, configuration: Configuration, routes: readonly ProjectEnvelopeEventReceivedTriggerRoute[], makeDurable: DurableEnvelopeEventReceivedTarget): EnvelopeEventReceivedEndpointRunner;
552
+
553
+ export { CombinedDocument, type CombinedDocumentReference, type CombinedDocumentStore, Config, type ConnectEnvelopeEvent, ConnectEnvelopeEvents, ConnectEventEnvelopeCompleted, ConnectEventEnvelopeDeclined, ConnectEventEnvelopeVoided, ConnectMessageMalformedError, ConnectSignatureInvalidError, Connection, ConnectorID, CreateEnvelopeFromTemplateBranch, CreateEnvelopeFromTemplateBranchCreated, CreateEnvelopeFromTemplateBranchDefect, CreateEnvelopeFromTemplateBranchProviderRejected, CreateEnvelopeFromTemplateBranchUncertain, CreateEnvelopeFromTemplateDefinition, CreateEnvelopeFromTemplateInput, CreateEnvelopeFromTemplateMetadata, CreateEnvelopeFromTemplateOperation, type CreateEnvelopeFromTemplateStepConfig, CreatedEnvelope, CredentialSource, Credentials, DocuSignClient, type DocuSignClientOptions, DownloadCombinedDocumentBranch, DownloadCombinedDocumentBranchDefect, DownloadCombinedDocumentBranchDownloaded, DownloadCombinedDocumentBranchInvalidResponse, DownloadCombinedDocumentBranchNotFound, DownloadCombinedDocumentBranchProviderRejected, DownloadCombinedDocumentBranchTooLarge, DownloadCombinedDocumentDefinition, DownloadCombinedDocumentInput, DownloadCombinedDocumentMetadata, DownloadCombinedDocumentOperation, type DownloadCombinedDocumentStepConfig, type DurableEnvelopeEventReceivedTarget, Envelope, EnvelopeCustomField, type EnvelopeEvent, EnvelopeEventReceivedEndpointRunner, type EnvelopeEventReceivedTriggerBindingConfig, type EnvelopeEventReceivedTriggerConfig, type EnvelopeEventReceivedTriggerConfiguration, EnvelopeEventReceivedTriggerConfigurationSchema, EnvelopeEventReceivedTriggerDefinition, EnvelopeEventSchema, EnvelopeRecipients, GetEnvelopeBranch, GetEnvelopeBranchDefect, GetEnvelopeBranchFound, GetEnvelopeBranchInvalidResponse, GetEnvelopeBranchNotFound, GetEnvelopeBranchProviderRejected, GetEnvelopeDefinition, GetEnvelopeInput, GetEnvelopeMetadata, GetEnvelopeOperation, type GetEnvelopeStepConfig, ListEnvelopeRecipientsBranch, ListEnvelopeRecipientsBranchDefect, ListEnvelopeRecipientsBranchInvalidResponse, ListEnvelopeRecipientsBranchListed, ListEnvelopeRecipientsBranchNotFound, ListEnvelopeRecipientsBranchProviderRejected, ListEnvelopeRecipientsDefinition, ListEnvelopeRecipientsInput, ListEnvelopeRecipientsMetadata, ListEnvelopeRecipientsOperation, type ListEnvelopeRecipientsStepConfig, type ProjectEnvelopeEventReceivedTriggerRoute, ProviderID, VoidEnvelopeBranch, VoidEnvelopeBranchDefect, VoidEnvelopeBranchInvalidResponse, VoidEnvelopeBranchNotFound, VoidEnvelopeBranchNotVoidable, VoidEnvelopeBranchProviderRejected, VoidEnvelopeBranchVoided, VoidEnvelopeDefinition, VoidEnvelopeInput, VoidEnvelopeMetadata, VoidEnvelopeOperation, type VoidEnvelopeStepConfig, VoidedEnvelope, decodeConnectMessage, defineEnvelopeEventReceivedTriggerBinding, envelopeEventCustomFieldValue, envelopeEventReceivedWebhookEndpoint, newCreateEnvelopeFromTemplateStep, newDownloadCombinedDocumentStep, newEnvelopeEventReceivedEndpointRunner, newEnvelopeEventReceivedTrigger, newGetEnvelopeStep, newListEnvelopeRecipientsStep, newProjectConnection, newProjectEnvelopeEventReceivedEndpointRunner, newVoidEnvelopeStep, verifyConnectSignature };
package/dist/index.js CHANGED
@@ -63,7 +63,7 @@ import {
63
63
  newConnectionWithOperations,
64
64
  newProjectConnection,
65
65
  newProjectCredentialProvider
66
- } from "./chunk-42O7OZRV.js";
66
+ } from "./chunk-PSJGBNG4.js";
67
67
 
68
68
  // src/generated/steps.ts
69
69
  import { goToBranch, newMutationStep, newQueryStep } from "@superdurable/connectors-sdk";
@@ -128,7 +128,7 @@ function newDownloadCombinedDocumentStep(configuration) {
128
128
  import { createHash as createHash2 } from "crypto";
129
129
  import { BodyTooLargeError, isHeaderSafeCredential, parseRetryAfter, readErrorTokens, sendProviderRequest } from "@superdurable/connectors-sdk/providerhttp";
130
130
  import { ManifestCredentialRefreshDriver } from "@superdurable/connectors-sdk/oauthtoken";
131
- import { resolveCredential, resolveCredentialAfterRejection } from "@superdurable/connectors-sdk";
131
+ import { defaultTriggerLogger, resolveCredential, resolveCredentialAfterRejection } from "@superdurable/connectors-sdk";
132
132
 
133
133
  // src/generated/refresh.ts
134
134
  var Authorization = {
@@ -185,7 +185,8 @@ var Authorization = {
185
185
  "invalid_client",
186
186
  "invalid_grant",
187
187
  "unauthorized_client"
188
- ]
188
+ ],
189
+ "tokenExchangeTimeout": "25s"
189
190
  },
190
191
  "scopes": [
191
192
  "signature",
@@ -247,7 +248,8 @@ var Authorization = {
247
248
  "invalid_client",
248
249
  "invalid_grant",
249
250
  "unauthorized_client"
250
- ]
251
+ ],
252
+ "tokenExchangeTimeout": "25s"
251
253
  },
252
254
  "scopes": [
253
255
  "signature",
@@ -631,11 +633,11 @@ function buildCreateBody(input, marker) {
631
633
  if ((input.customFields?.length ?? 0) > 10) throw new Error("customFields holds at most 10 fields");
632
634
  const fields = [{ name: "dexIdempotencyKey", value: marker, show: "false", required: "false" }];
633
635
  const fieldNames = /* @__PURE__ */ new Set(["dexidempotencykey"]);
634
- for (const [index, field] of (input.customFields ?? []).entries()) {
635
- if (!field.name || [...field.name].length > 50 || [...field.value].length > 100) throw new Error(`customFields[${index}] needs a 1 to 50 character name and a value of at most 100 characters`);
636
- if (fieldNames.has(field.name.toLowerCase())) throw new Error(`customFields[${index}]: name ${JSON.stringify(field.name)} is reserved or repeated`);
637
- fieldNames.add(field.name.toLowerCase());
638
- fields.push({ name: field.name, value: field.value, show: "false", required: "false" });
636
+ for (const [index, field2] of (input.customFields ?? []).entries()) {
637
+ if (!field2.name || [...field2.name].length > 50 || [...field2.value].length > 100) throw new Error(`customFields[${index}] needs a 1 to 50 character name and a value of at most 100 characters`);
638
+ if (fieldNames.has(field2.name.toLowerCase())) throw new Error(`customFields[${index}]: name ${JSON.stringify(field2.name)} is reserved or repeated`);
639
+ fieldNames.add(field2.name.toLowerCase());
640
+ fields.push({ name: field2.name, value: field2.value, show: "false", required: "false" });
639
641
  }
640
642
  return { templateId: input.templateId.toLowerCase(), status: input.isDraft ? "created" : "sent", ...input.emailSubject ? { emailSubject: input.emailSubject } : {}, ...input.emailBlurb ? { emailBlurb: input.emailBlurb } : {}, templateRoles: roles, customFields: { textCustomFields: fields } };
641
643
  }
@@ -696,11 +698,11 @@ function decodeEnvelope(body) {
696
698
  if (typeof fields !== "object" || Array.isArray(fields)) throw new Error(malformed);
697
699
  const text = fields.textCustomFields;
698
700
  if (text != null && !Array.isArray(text)) throw new Error(malformed);
699
- const customFields = (text ?? []).map((field) => {
700
- if (!field || typeof field !== "object" || Array.isArray(field)) throw new Error(malformed);
701
- const item = field;
701
+ const customFields = (text ?? []).map((field2) => {
702
+ if (!field2 || typeof field2 !== "object" || Array.isArray(field2)) throw new Error(malformed);
703
+ const item = field2;
702
704
  return { name: optionalString(item.name), value: optionalString(item.value) };
703
- }).filter((field) => field.name);
705
+ }).filter((field2) => field2.name);
704
706
  if (customFields.length) result.customFields = customFields;
705
707
  }
706
708
  return result;
@@ -829,7 +831,7 @@ function recoveryRequestTimeout(call) {
829
831
  function findTagged(body, marker) {
830
832
  const raw = parseObject(body);
831
833
  if (raw.envelopes != null && !Array.isArray(raw.envelopes)) throw new Error(malformed);
832
- return (raw.envelopes ?? []).map((entry) => decodeEnvelope(Buffer.from(JSON.stringify(entry)))).filter((envelope) => envelope.customFields?.some((field) => field.name === "dexIdempotencyKey" && field.value === marker)).map((envelope) => ({ envelopeId: envelope.envelopeId, status: envelope.status, ...envelope.statusChangedAt ? { statusChangedAt: envelope.statusChangedAt } : {} }));
834
+ return (raw.envelopes ?? []).map((entry) => decodeEnvelope(Buffer.from(JSON.stringify(entry)))).filter((envelope) => envelope.customFields?.some((field2) => field2.name === "dexIdempotencyKey" && field2.value === marker)).map((envelope) => ({ envelopeId: envelope.envelopeId, status: envelope.status, ...envelope.statusChangedAt ? { statusChangedAt: envelope.statusChangedAt } : {} }));
833
835
  }
834
836
 
835
837
  // src/client.ts
@@ -838,6 +840,7 @@ var productionHostPattern = /^(?!demo\.)[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.do
838
840
  var maximumErrorBodyBytes = 65536;
839
841
  var defaultResponseBytes = 1048576;
840
842
  var defaultDocumentBytes = 67108864;
843
+ var defaultConnectBodyBytes = 2097152;
841
844
  var rateLimitCodes = /* @__PURE__ */ new Set([
842
845
  "HOURLY_APIINVOCATION_LIMIT_EXCEEDED",
843
846
  "BURST_APIINVOCATION_LIMIT_EXCEEDED",
@@ -884,20 +887,27 @@ var DocuSignClient2 = class {
884
887
  if (!Number.isSafeInteger(this.maxResponseBytes) || this.maxResponseBytes <= 0) throw new TypeError("maxResponseBytes must be positive");
885
888
  if (!Number.isSafeInteger(this.maxDocumentBytes) || this.maxDocumentBytes <= 0) throw new TypeError("maxDocumentBytes must be positive");
886
889
  if (config.connectMaxBodyBytes !== void 0 && (!Number.isSafeInteger(config.connectMaxBodyBytes) || config.connectMaxBodyBytes < 0)) throw new TypeError("connectMaxBodyBytes must be non-negative");
890
+ this.connectMaxBodyBytes = config.connectMaxBodyBytes || defaultConnectBodyBytes;
887
891
  this.send = options.send ?? sendProviderRequest;
888
892
  this.hasCustomSend = options.send !== void 0;
889
893
  this.now = options.now ?? (() => /* @__PURE__ */ new Date());
890
894
  this.documentStore = options.documentStore;
895
+ this.triggerLogger = options.logger ?? defaultTriggerLogger;
891
896
  }
892
897
  credentials;
893
898
  maxResponseBytes;
894
899
  maxDocumentBytes;
895
900
  documentStore;
901
+ /** Largest Connect request body the envelopeEventReceived endpoint accepts. */
902
+ connectMaxBodyBytes;
903
+ /** Destination of the envelopeEventReceived Trigger's records. */
904
+ triggerLogger;
896
905
  accountId;
897
906
  send;
898
907
  hasCustomSend;
899
908
  now;
900
909
  accountCache = /* @__PURE__ */ new Map();
910
+ /** @internal Refreshes expiring OAuth credentials for operations and the Connect endpoint. */
901
911
  refreshDriver = new ManifestCredentialRefreshDriver({ providerName: "DocuSign", auth: Authorization, credentialFields: CredentialFields });
902
912
  /** Bind the template creation Mutation. */
903
913
  createEnvelopeFromTemplate() {
@@ -1192,9 +1202,260 @@ function newProjectConnection2(project, connectionName, options = {}) {
1192
1202
  return new DocuSignClient2(config, newProjectCredentialProvider(loadedProject, name, decodeCredentials, encodeCredentials, decodeSharedCredentials), options);
1193
1203
  });
1194
1204
  }
1205
+
1206
+ // src/connect_event.ts
1207
+ import { createHmac, timingSafeEqual } from "crypto";
1208
+ import { z } from "zod";
1209
+ var ConnectEventEnvelopeCompleted = "envelope-completed";
1210
+ var ConnectEventEnvelopeDeclined = "envelope-declined";
1211
+ var ConnectEventEnvelopeVoided = "envelope-voided";
1212
+ var ConnectEnvelopeEvents = [ConnectEventEnvelopeCompleted, ConnectEventEnvelopeDeclined, ConnectEventEnvelopeVoided];
1213
+ var EnvelopeEventReceivedTriggerConfigurationSchema = z.strictObject({
1214
+ /** envelope-completed, envelope-declined, and envelope-voided; empty accepts all three. */
1215
+ events: z.array(z.string()).nullish()
1216
+ }).superRefine((configuration, context) => {
1217
+ const events = configuration.events ?? [];
1218
+ if (events.some((event) => !ConnectEnvelopeEvents.includes(event)) || new Set(events).size !== events.length) {
1219
+ context.addIssue({ code: "custom", message: `envelopeEventReceived events must be distinct values from ${ConnectEnvelopeEvents.join(", ")}` });
1220
+ }
1221
+ });
1222
+ var EnvelopeEventSchema = z.object({
1223
+ event: z.string(),
1224
+ envelopeId: z.string(),
1225
+ accountId: z.string(),
1226
+ generatedAt: z.string(),
1227
+ status: z.string().optional(),
1228
+ voidedReason: z.string().optional(),
1229
+ customFields: z.array(z.object({ name: z.string(), value: z.string() })).optional()
1230
+ }).transform((value) => value);
1231
+ function envelopeEventCustomFieldValue(event, name) {
1232
+ return event.customFields?.find((field2) => field2.name === name)?.value;
1233
+ }
1234
+ var ConnectMessageMalformedError = class extends Error {
1235
+ constructor() {
1236
+ super("DocuSign returned a malformed response");
1237
+ this.name = "ConnectMessageMalformedError";
1238
+ }
1239
+ };
1240
+ var ConnectSignatureInvalidError = class extends Error {
1241
+ constructor() {
1242
+ super("no X-DocuSign-Signature header matches the HMAC-SHA256 of the body");
1243
+ this.name = "ConnectSignatureInvalidError";
1244
+ }
1245
+ };
1246
+ var signatureHeaderPattern = /^x-docusign-signature-([0-9]+)$/;
1247
+ var base64Pattern = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
1248
+ function verifyConnectSignature(body, headers, hmacKey) {
1249
+ const expected = createHmac("sha256", hmacKey).update(body).digest();
1250
+ for (const [name, raw] of Object.entries(headers)) {
1251
+ const suffix = signatureHeaderPattern.exec(name.toLowerCase())?.[1];
1252
+ if (suffix === void 0 || Number(suffix) > 65535 || raw === void 0) continue;
1253
+ for (const value of (Array.isArray(raw) ? raw : [raw]).flatMap((entry) => entry.split(","))) {
1254
+ const encoded = value.trim();
1255
+ if (!base64Pattern.test(encoded)) continue;
1256
+ const signature = Buffer.from(encoded, "base64");
1257
+ if (signature.length === expected.length && timingSafeEqual(signature, expected)) return;
1258
+ }
1259
+ }
1260
+ throw new ConnectSignatureInvalidError();
1261
+ }
1262
+ function decodeConnectMessage(body, receivedAt) {
1263
+ let message;
1264
+ try {
1265
+ message = JSON.parse(Buffer.from(body).toString("utf8"));
1266
+ } catch {
1267
+ throw new ConnectMessageMalformedError();
1268
+ }
1269
+ const root = objectOrNull(message);
1270
+ const eventName = stringField(root, "event");
1271
+ const data = objectOrNull(field(root, "data"));
1272
+ const generatedDateTime = stringField(root, "generatedDateTime");
1273
+ if (eventName === "" || data === null) throw new ConnectMessageMalformedError();
1274
+ const accountId = stringField(data, "accountId");
1275
+ const envelopeId = stringField(data, "envelopeId");
1276
+ const summary = field(data, "envelopeSummary");
1277
+ if (!ConnectEnvelopeEvents.includes(eventName)) return void 0;
1278
+ if (!isGuid(envelopeId) || !isGuid(accountId)) throw new ConnectMessageMalformedError();
1279
+ const event = { event: eventName, envelopeId: envelopeId.toLowerCase(), accountId: accountId.toLowerCase(), generatedAt: goTimestamp2(receivedAt) };
1280
+ if (generatedDateTime !== "") {
1281
+ try {
1282
+ event.generatedAt = parseDocuSignTimestamp(generatedDateTime);
1283
+ } catch {
1284
+ }
1285
+ }
1286
+ if (summary !== null && typeof summary === "object" && !Array.isArray(summary)) {
1287
+ const status = stringField(summary, "status").toLowerCase();
1288
+ const voidedReason = stringField(summary, "voidedReason");
1289
+ const customFields = textCustomFields(objectOrNull(field(summary, "customFields")));
1290
+ if (status !== "") event.status = status;
1291
+ if (voidedReason !== "") event.voidedReason = voidedReason;
1292
+ if (customFields.length > 0) event.customFields = customFields;
1293
+ }
1294
+ return { id: `${event.envelopeId}:${event.event}`, occurredAt: new Date(event.generatedAt), payload: event };
1295
+ }
1296
+ function textCustomFields(fields) {
1297
+ const raw = field(fields, "textCustomFields");
1298
+ if (raw === null || raw === void 0) return [];
1299
+ if (!Array.isArray(raw)) throw new ConnectMessageMalformedError();
1300
+ const converted = [];
1301
+ for (const entry of raw) {
1302
+ const customField = objectOrNull(entry);
1303
+ const name = stringField(customField, "name");
1304
+ const value = stringField(customField, "value");
1305
+ if (name !== "") converted.push({ name, value });
1306
+ }
1307
+ return converted;
1308
+ }
1309
+ function field(object, name) {
1310
+ if (object === null) return void 0;
1311
+ let found;
1312
+ for (const [key, value] of Object.entries(object)) if (key === name || key.toLowerCase() === name.toLowerCase()) found = value;
1313
+ return found;
1314
+ }
1315
+ function stringField(object, name) {
1316
+ const value = field(object, name);
1317
+ if (value === void 0 || value === null) return "";
1318
+ if (typeof value !== "string") throw new ConnectMessageMalformedError();
1319
+ return value;
1320
+ }
1321
+ function objectOrNull(value) {
1322
+ if (value === void 0 || value === null) return null;
1323
+ if (typeof value !== "object" || Array.isArray(value)) throw new ConnectMessageMalformedError();
1324
+ return value;
1325
+ }
1326
+ function goTimestamp2(value) {
1327
+ return value.toISOString().replace(/\.([0-9]*?)0*Z$/, (_match, fraction) => fraction ? `.${fraction}Z` : "Z");
1328
+ }
1329
+
1330
+ // src/trigger.ts
1331
+ import {
1332
+ mustTriggerBindingDefinition,
1333
+ newTrigger
1334
+ } from "@superdurable/connectors-sdk";
1335
+ import { ObjectNotFoundError, decodeTriggerConfiguration, newDurableTriggerTarget, parseTriggerPayloadJSON } from "@superdurable/connectors-sdk/projectconfig";
1336
+ import { WebhookEndpoint, WebhookEndpointRunner, WebhookVerificationUnavailableError } from "@superdurable/connectors-sdk/webhooktrigger";
1337
+ var EnvelopeEventReceivedTriggerDefinition = {
1338
+ trigger: { connectorId: ConnectorID, triggerName: "envelopeEventReceived" },
1339
+ description: "Receive an HMAC-verified DocuSign Connect envelope-completed, envelope-declined, or envelope-voided event in the JSON SIM format, with the envelope ID and event name as its event ID and the envelope's text custom fields for correlation."
1340
+ };
1341
+ function defineEnvelopeEventReceivedTriggerBinding(config) {
1342
+ return mustTriggerBindingDefinition({ definition: EnvelopeEventReceivedTriggerDefinition, ...config });
1343
+ }
1344
+ function newEnvelopeEventReceivedTrigger(config) {
1345
+ const client = docuSignClientOf(config.connection);
1346
+ if (config.connectionName !== config.connection.reference.name) {
1347
+ throw new TypeError(`docusign connector trigger connectionName "${config.connectionName}" must equal its connection's name "${config.connection.reference.name}"`);
1348
+ }
1349
+ const configuration = EnvelopeEventReceivedTriggerConfigurationSchema.parse(config.configuration);
1350
+ const events = configuration.events ?? [];
1351
+ const source = envelopeEventReceivedWebhookEndpoint(config.connection).newSource((event) => events.length === 0 || events.includes(event.payload.event));
1352
+ return newTrigger({
1353
+ definition: EnvelopeEventReceivedTriggerDefinition,
1354
+ source,
1355
+ target: config.target,
1356
+ logger: client.triggerLogger,
1357
+ binding: { connection: config.connection.reference, trigger: EnvelopeEventReceivedTriggerDefinition.trigger, name: config.bindingName }
1358
+ });
1359
+ }
1360
+ function envelopeEventReceivedWebhookEndpoint(connection) {
1361
+ const client = docuSignClientOf(connection);
1362
+ let endpoints = webhookEndpointsByClient.get(client);
1363
+ if (!endpoints) {
1364
+ endpoints = /* @__PURE__ */ new Map();
1365
+ webhookEndpointsByClient.set(client, endpoints);
1366
+ }
1367
+ const key = `${connection.reference.provider}\0${connection.reference.name}`;
1368
+ let endpoint = endpoints.get(key);
1369
+ if (!endpoint) {
1370
+ endpoint = newConnectEndpoint(client, connection.reference);
1371
+ endpoints.set(key, endpoint);
1372
+ }
1373
+ return endpoint;
1374
+ }
1375
+ var EnvelopeEventReceivedEndpointRunner = class {
1376
+ #runner;
1377
+ #endpoint;
1378
+ /** @internal Use newProjectEnvelopeEventReceivedEndpointRunner. */
1379
+ constructor(endpoint, runners) {
1380
+ this.#endpoint = endpoint;
1381
+ this.#runner = new WebhookEndpointRunner(endpoint, runners);
1382
+ }
1383
+ /** Verify, record, and acknowledge one Connect delivery. It answers 503 until run has replayed every binding's pending events and started receiving, so Connect retries. */
1384
+ serveHTTP(request, response) {
1385
+ return this.#runner.serveHTTP(request, response);
1386
+ }
1387
+ /** Replay each binding's durable inbox, then deliver new events until the context ends, rejecting with TriggerDeliveryAbortedError, or a binding fails. */
1388
+ run(context) {
1389
+ return this.#runner.run(context);
1390
+ }
1391
+ /** How many bindings are receiving events; a readiness check can wait until it equals the number of routes. */
1392
+ runningSourceCount() {
1393
+ return this.#endpoint.runningSourceCount();
1394
+ }
1395
+ };
1396
+ function newProjectEnvelopeEventReceivedEndpointRunner(project, connectionName, routes, options = {}) {
1397
+ const connection = newProjectConnection2(project, connectionName, options);
1398
+ const client = docuSignClientOf(connection);
1399
+ return newEnvelopeEventReceivedEndpointRunner(connection, project.configuration, routes, (key, target) => newDurableTriggerTarget(project.triggerInbox(key), key, target, {
1400
+ logger: client.triggerLogger,
1401
+ decodePayload: (_payloadJSON, payloadBytes) => EnvelopeEventSchema.parse(parseTriggerPayloadJSON(payloadBytes))
1402
+ }));
1403
+ }
1404
+ function newEnvelopeEventReceivedEndpointRunner(connection, configuration, routes, makeDurable) {
1405
+ if (!routes || routes.length === 0) throw new TypeError("DocuSign envelopeEventReceived routes are required");
1406
+ const endpoint = envelopeEventReceivedWebhookEndpoint(connection);
1407
+ const connectionName = connection.reference.name;
1408
+ const triggerName = EnvelopeEventReceivedTriggerDefinition.trigger.triggerName;
1409
+ const bindingNames = /* @__PURE__ */ new Set();
1410
+ const runners = routes.map((route) => {
1411
+ const bindingName = route?.bindingName?.trim();
1412
+ if (!bindingName || !route.target) throw new TypeError("DocuSign envelopeEventReceived binding name and target are required");
1413
+ if (bindingNames.has(bindingName)) throw new TypeError(`DocuSign envelopeEventReceived binding name "${bindingName}" is duplicated`);
1414
+ bindingNames.add(bindingName);
1415
+ const key = { connectorId: ConnectorID, connectionName, triggerName, bindingName };
1416
+ let bindingConfiguration;
1417
+ try {
1418
+ bindingConfiguration = decodeTriggerConfiguration(configuration, key, EnvelopeEventReceivedTriggerConfigurationSchema);
1419
+ } catch (error) {
1420
+ if (error instanceof ObjectNotFoundError) throw error;
1421
+ throw new Error(`DocuSign envelopeEventReceived binding "${bindingName}" configuration: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
1422
+ }
1423
+ return newEnvelopeEventReceivedTrigger({ connection, connectionName, bindingName, configuration: bindingConfiguration, target: makeDurable(key, route.target) });
1424
+ });
1425
+ return new EnvelopeEventReceivedEndpointRunner(endpoint, runners);
1426
+ }
1427
+ var webhookEndpointsByClient = /* @__PURE__ */ new WeakMap();
1428
+ function newConnectEndpoint(client, connection) {
1429
+ return new WebhookEndpoint({
1430
+ connectorId: ConnectorID,
1431
+ triggerName: EnvelopeEventReceivedTriggerDefinition.trigger.triggerName,
1432
+ connection,
1433
+ credentials: client.credentials,
1434
+ credentialRefresh: client.refreshDriver,
1435
+ maxBodyBytes: client.connectMaxBodyBytes,
1436
+ now: () => client.currentTime(),
1437
+ logger: client.triggerLogger,
1438
+ verifyRequest: async (request, credentials) => {
1439
+ const hmacKey = (credentials.connectHmacKey?.reveal() ?? "").replaceAll('"', "");
1440
+ if (hmacKey === "") throw new WebhookVerificationUnavailableError();
1441
+ verifyConnectSignature(request.body, request.headers, hmacKey);
1442
+ },
1443
+ decodeEvent: (request) => decodeConnectMessage(request.body, request.receivedAt)
1444
+ });
1445
+ }
1446
+ function docuSignClientOf(connection) {
1447
+ if (!(connection?.operations instanceof DocuSignClient2)) throw new TypeError("DocuSign Triggers require a connection bound to a DocuSignClient");
1448
+ return connection.operations;
1449
+ }
1195
1450
  export {
1196
1451
  CombinedDocumentSchema,
1197
1452
  ConfigSchema,
1453
+ ConnectEnvelopeEvents,
1454
+ ConnectEventEnvelopeCompleted,
1455
+ ConnectEventEnvelopeDeclined,
1456
+ ConnectEventEnvelopeVoided,
1457
+ ConnectMessageMalformedError,
1458
+ ConnectSignatureInvalidError,
1198
1459
  Connection,
1199
1460
  ConnectorID,
1200
1461
  CreateEnvelopeFromTemplateBranch,
@@ -1220,6 +1481,10 @@ export {
1220
1481
  DownloadCombinedDocumentMetadata,
1221
1482
  DownloadCombinedDocumentOperation,
1222
1483
  EnvelopeCustomFieldSchema,
1484
+ EnvelopeEventReceivedEndpointRunner,
1485
+ EnvelopeEventReceivedTriggerConfigurationSchema,
1486
+ EnvelopeEventReceivedTriggerDefinition,
1487
+ EnvelopeEventSchema,
1223
1488
  EnvelopeRecipientsSchema,
1224
1489
  EnvelopeSchema,
1225
1490
  GetEnvelopeBranch,
@@ -1257,14 +1522,22 @@ export {
1257
1522
  VoidEnvelopeMetadata,
1258
1523
  VoidEnvelopeOperation,
1259
1524
  VoidedEnvelopeSchema,
1525
+ decodeConnectMessage,
1260
1526
  decodeCredentials,
1261
1527
  decodeSharedCredentials,
1528
+ defineEnvelopeEventReceivedTriggerBinding,
1529
+ envelopeEventCustomFieldValue,
1530
+ envelopeEventReceivedWebhookEndpoint,
1262
1531
  newConnection,
1263
1532
  newConnectionWithOperations,
1264
1533
  newCreateEnvelopeFromTemplateStep,
1265
1534
  newDownloadCombinedDocumentStep,
1535
+ newEnvelopeEventReceivedEndpointRunner,
1536
+ newEnvelopeEventReceivedTrigger,
1266
1537
  newGetEnvelopeStep,
1267
1538
  newListEnvelopeRecipientsStep,
1268
1539
  newProjectConnection2 as newProjectConnection,
1269
- newVoidEnvelopeStep
1540
+ newProjectEnvelopeEventReceivedEndpointRunner,
1541
+ newVoidEnvelopeStep,
1542
+ verifyConnectSignature
1270
1543
  };
package/dist/mock.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { MockMutation, MockQuery, Case } from '@superdurable/connectors-sdk/connectormock';
2
2
  import { Failure } from '@superdurable/connectors-sdk';
3
- import { O as Operations, b as Connection, C as CreateEnvelopeFromTemplateInput, a as CreatedEnvelope, G as GetEnvelopeInput, E as Envelope, L as ListEnvelopeRecipientsInput, d as EnvelopeRecipients, V as VoidEnvelopeInput, e as VoidedEnvelope, D as DownloadCombinedDocumentInput, c as CombinedDocument, n as EnvelopeCustomField, R as Recipient, T as TemplateRole } from './connection-YEptevxS.js';
3
+ import { O as Operations, b as Connection, C as CreateEnvelopeFromTemplateInput, a as CreatedEnvelope, G as GetEnvelopeInput, E as Envelope, L as ListEnvelopeRecipientsInput, d as EnvelopeRecipients, V as VoidEnvelopeInput, e as VoidedEnvelope, D as DownloadCombinedDocumentInput, c as CombinedDocument, i as EnvelopeCustomField, R as Recipient, T as TemplateRole } from './connection-BFvLyRgn.js';
4
4
  import 'zod';
5
5
 
6
6
  /** Go zero value for TemplateRole in scripted cases. */
package/dist/mock.js CHANGED
@@ -20,7 +20,7 @@ import {
20
20
  VoidEnvelopeInputSchema,
21
21
  VoidedEnvelopeSchema,
22
22
  newConnectionWithOperations
23
- } from "./chunk-42O7OZRV.js";
23
+ } from "./chunk-PSJGBNG4.js";
24
24
 
25
25
  // src/generated/mock.ts
26
26
  import { branchCase, retryCase, uncertainCase, newMutation, newQuery } from "@superdurable/connectors-sdk/connectormock";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superdurable/connector-docusign",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "DocuSign eSignature operations for Dex Flows",
5
5
  "repository": {
6
6
  "type": "git",
@@ -25,7 +25,8 @@
25
25
  "./mock": {
26
26
  "types": "./dist/mock.d.ts",
27
27
  "import": "./dist/mock.js"
28
- }
28
+ },
29
+ "./dex-connector.json": "./dex-connector.json"
29
30
  },
30
31
  "files": [
31
32
  "dist",
@@ -41,10 +42,11 @@
41
42
  "test:integration": "npm run build && vitest run test/integration/*.integration.test.ts",
42
43
  "check": "npm test && npm run typecheck && npm run lint",
43
44
  "example:envelope": "npm run build && tsx examples/envelope-status/main.ts",
44
- "example:envelope:start": "npm run build && tsx examples/envelope-status/start.ts"
45
+ "example:envelope:start": "npm run build && tsx examples/envelope-status/start.ts",
46
+ "example:outcome": "npm run build && tsx examples/envelope-outcome/main.ts"
45
47
  },
46
48
  "dependencies": {
47
- "@superdurable/connectors-sdk": "0.7.0",
49
+ "@superdurable/connectors-sdk": "0.11.0",
48
50
  "@superdurable/dex": ">=1.28.0",
49
51
  "zod": "^4.1.0"
50
52
  },