@assinafy/sdk 1.4.0 → 1.5.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 +75 -12
- package/dist/index.d.mts +209 -35
- package/dist/index.d.ts +209 -35
- package/dist/index.js +229 -74
- package/dist/index.mjs +228 -74
- package/package.json +11 -8
package/dist/index.mjs
CHANGED
|
@@ -87,9 +87,40 @@ function cleanParams(params) {
|
|
|
87
87
|
return out;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
// src/
|
|
91
|
-
|
|
92
|
-
|
|
90
|
+
// src/support/retry.ts
|
|
91
|
+
function header(headers, name) {
|
|
92
|
+
if (!headers) return void 0;
|
|
93
|
+
const lower = name.toLowerCase();
|
|
94
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
95
|
+
if (key.toLowerCase() === lower && value != null) {
|
|
96
|
+
return Array.isArray(value) ? String(value[0]) : String(value);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return void 0;
|
|
100
|
+
}
|
|
101
|
+
function retryDelayFromHeaders(headers) {
|
|
102
|
+
const retryAfter = header(headers, "retry-after");
|
|
103
|
+
if (retryAfter !== void 0) {
|
|
104
|
+
const seconds = Number(retryAfter);
|
|
105
|
+
if (Number.isFinite(seconds)) return Math.max(0, seconds * 1e3);
|
|
106
|
+
const date = Date.parse(retryAfter);
|
|
107
|
+
if (!Number.isNaN(date)) return Math.max(0, date - Date.now());
|
|
108
|
+
}
|
|
109
|
+
const reset = header(headers, "x-rate-limit-reset");
|
|
110
|
+
if (reset !== void 0) {
|
|
111
|
+
const seconds = Number(reset);
|
|
112
|
+
if (Number.isFinite(seconds)) return Math.max(0, seconds * 1e3);
|
|
113
|
+
}
|
|
114
|
+
return void 0;
|
|
115
|
+
}
|
|
116
|
+
function backoffMs(attempt) {
|
|
117
|
+
return Math.min(1e3 * 2 ** Math.max(0, attempt - 1), 8e3);
|
|
118
|
+
}
|
|
119
|
+
function nextRetryDelayMs(headers, attempt, maxDelayMs = 3e4) {
|
|
120
|
+
const hinted = retryDelayFromHeaders(headers);
|
|
121
|
+
if (hinted !== void 0) return Math.min(hinted, maxDelayMs);
|
|
122
|
+
return Math.min(backoffMs(attempt), maxDelayMs);
|
|
123
|
+
}
|
|
93
124
|
|
|
94
125
|
// src/resources/base.ts
|
|
95
126
|
var BaseResource = class {
|
|
@@ -194,8 +225,49 @@ function toInt(value) {
|
|
|
194
225
|
return Number.isFinite(n) ? n : void 0;
|
|
195
226
|
}
|
|
196
227
|
|
|
197
|
-
// src/resources/
|
|
228
|
+
// src/resources/upload.ts
|
|
229
|
+
import { promises as fs } from "fs";
|
|
230
|
+
import path from "path";
|
|
198
231
|
var MAX_UPLOAD_BYTES = 25 * 1024 * 1024;
|
|
232
|
+
async function loadSource(source) {
|
|
233
|
+
if ("buffer" in source) {
|
|
234
|
+
if (!source.fileName) {
|
|
235
|
+
throw new ValidationError("fileName is required when uploading a Buffer");
|
|
236
|
+
}
|
|
237
|
+
return { buffer: source.buffer, fileName: source.fileName };
|
|
238
|
+
}
|
|
239
|
+
if (!source.filePath) {
|
|
240
|
+
throw new ValidationError("filePath is required");
|
|
241
|
+
}
|
|
242
|
+
const buffer = await fs.readFile(source.filePath);
|
|
243
|
+
return { buffer, fileName: source.fileName ?? path.basename(source.filePath) };
|
|
244
|
+
}
|
|
245
|
+
function validateUpload(buffer, fileName) {
|
|
246
|
+
if (!buffer || buffer.byteLength === 0) {
|
|
247
|
+
throw new ValidationError("File buffer is empty", { fileName });
|
|
248
|
+
}
|
|
249
|
+
if (!fileName.toLowerCase().endsWith(".pdf")) {
|
|
250
|
+
throw new ValidationError("Only PDF files are supported", { fileName });
|
|
251
|
+
}
|
|
252
|
+
if (buffer.byteLength > MAX_UPLOAD_BYTES) {
|
|
253
|
+
throw new ValidationError("File size exceeds maximum allowed (25MB)", {
|
|
254
|
+
fileSize: buffer.byteLength,
|
|
255
|
+
maxSize: MAX_UPLOAD_BYTES
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
function buildUploadForm(buffer, fileName, options = {}) {
|
|
260
|
+
const form = new FormData();
|
|
261
|
+
const view = new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
262
|
+
form.append("file", new Blob([view], { type: "application/pdf" }), fileName);
|
|
263
|
+
form.append("name", options.name ?? fileName);
|
|
264
|
+
if (options.metadata) {
|
|
265
|
+
form.append("metadata", JSON.stringify(options.metadata));
|
|
266
|
+
}
|
|
267
|
+
return form;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// src/resources/documents.ts
|
|
199
271
|
var READY_STATUSES = /* @__PURE__ */ new Set([
|
|
200
272
|
"metadata_ready",
|
|
201
273
|
"pending_signature",
|
|
@@ -221,7 +293,9 @@ var DocumentResource = class extends BaseResource {
|
|
|
221
293
|
const { buffer, fileName } = await loadSource(source);
|
|
222
294
|
validateUpload(buffer, fileName);
|
|
223
295
|
const accountId = this.accountId(options.accountId);
|
|
224
|
-
const
|
|
296
|
+
const formOptions = {};
|
|
297
|
+
if (options.metadata !== void 0) formOptions.metadata = options.metadata;
|
|
298
|
+
const form = buildUploadForm(buffer, fileName, formOptions);
|
|
225
299
|
this.logger.info("Uploading document", { fileName, size: buffer.byteLength });
|
|
226
300
|
const document = await this.call(
|
|
227
301
|
"Document upload failed",
|
|
@@ -397,7 +471,12 @@ var DocumentResource = class extends BaseResource {
|
|
|
397
471
|
() => this.http.post(`/accounts/${accId}/templates/${tmplId}/documents`, body)
|
|
398
472
|
);
|
|
399
473
|
}
|
|
400
|
-
/**
|
|
474
|
+
/**
|
|
475
|
+
* Estimate the credit cost of creating a document from a template.
|
|
476
|
+
*
|
|
477
|
+
* @returns an {@link ICostEstimate}: `total_credits`, balances, and a
|
|
478
|
+
* per-line `breakdown` of what the operation would consume.
|
|
479
|
+
*/
|
|
401
480
|
async estimateCostFromTemplate(templateId, signers, accountId) {
|
|
402
481
|
const tmplId = this.requireId(templateId, "Template ID");
|
|
403
482
|
const accId = this.accountId(accountId);
|
|
@@ -466,43 +545,6 @@ var DocumentResource = class extends BaseResource {
|
|
|
466
545
|
return { signed, total, pending, percentage };
|
|
467
546
|
}
|
|
468
547
|
};
|
|
469
|
-
async function loadSource(source) {
|
|
470
|
-
if ("buffer" in source) {
|
|
471
|
-
if (!source.fileName) {
|
|
472
|
-
throw new ValidationError("fileName is required when uploading a Buffer");
|
|
473
|
-
}
|
|
474
|
-
return { buffer: source.buffer, fileName: source.fileName };
|
|
475
|
-
}
|
|
476
|
-
if (!source.filePath) {
|
|
477
|
-
throw new ValidationError("filePath is required");
|
|
478
|
-
}
|
|
479
|
-
const buffer = await fs.readFile(source.filePath);
|
|
480
|
-
return { buffer, fileName: source.fileName ?? path.basename(source.filePath) };
|
|
481
|
-
}
|
|
482
|
-
function validateUpload(buffer, fileName) {
|
|
483
|
-
if (!buffer || buffer.byteLength === 0) {
|
|
484
|
-
throw new ValidationError("File buffer is empty", { fileName });
|
|
485
|
-
}
|
|
486
|
-
if (!fileName.toLowerCase().endsWith(".pdf")) {
|
|
487
|
-
throw new ValidationError("Only PDF files are supported", { fileName });
|
|
488
|
-
}
|
|
489
|
-
if (buffer.byteLength > MAX_UPLOAD_BYTES) {
|
|
490
|
-
throw new ValidationError("File size exceeds maximum allowed (25MB)", {
|
|
491
|
-
fileSize: buffer.byteLength,
|
|
492
|
-
maxSize: MAX_UPLOAD_BYTES
|
|
493
|
-
});
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
function buildUploadForm(buffer, fileName, metadata) {
|
|
497
|
-
const form = new FormData();
|
|
498
|
-
const view = new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
499
|
-
form.append("file", new Blob([view], { type: "application/pdf" }), fileName);
|
|
500
|
-
form.append("name", fileName);
|
|
501
|
-
if (metadata) {
|
|
502
|
-
form.append("metadata", JSON.stringify(metadata));
|
|
503
|
-
}
|
|
504
|
-
return form;
|
|
505
|
-
}
|
|
506
548
|
function sleep(ms) {
|
|
507
549
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
508
550
|
}
|
|
@@ -718,7 +760,15 @@ var AssignmentResource = class extends BaseResource {
|
|
|
718
760
|
() => this.http.post(`/documents/${docId}/assignments`, body)
|
|
719
761
|
);
|
|
720
762
|
}
|
|
721
|
-
/**
|
|
763
|
+
/**
|
|
764
|
+
* Estimate the cost (in credits/documents) of creating the assignment.
|
|
765
|
+
*
|
|
766
|
+
* Signer entries may omit `id` and supply only `verification_method` /
|
|
767
|
+
* `notification_methods` when only the channel mix matters for the estimate.
|
|
768
|
+
*
|
|
769
|
+
* @returns an {@link ICostEstimate} with `total_credits`, balances, and a
|
|
770
|
+
* line-item `breakdown`.
|
|
771
|
+
*/
|
|
722
772
|
async estimateCost(documentId, payload) {
|
|
723
773
|
const docId = this.requireId(documentId, "Document ID");
|
|
724
774
|
return this.call(
|
|
@@ -753,7 +803,11 @@ var AssignmentResource = class extends BaseResource {
|
|
|
753
803
|
() => this.http.put(`/documents/${docId}/assignments/${asgId}/signers/${sid}/resend`)
|
|
754
804
|
);
|
|
755
805
|
}
|
|
756
|
-
/**
|
|
806
|
+
/**
|
|
807
|
+
* Estimate the cost of resending a signer notification.
|
|
808
|
+
*
|
|
809
|
+
* @returns an {@link IResendCostEstimate} (`total`, `breakdown`, balances).
|
|
810
|
+
*/
|
|
757
811
|
async estimateResendCost(documentId, assignmentId, signerId) {
|
|
758
812
|
const docId = this.requireId(documentId, "Document ID");
|
|
759
813
|
const asgId = this.requireId(assignmentId, "Assignment ID");
|
|
@@ -777,26 +831,10 @@ var AssignmentResource = class extends BaseResource {
|
|
|
777
831
|
() => this.http.get(`/documents/${docId}/assignments/${asgId}/whatsapp-notifications`)
|
|
778
832
|
);
|
|
779
833
|
}
|
|
780
|
-
/**
|
|
781
|
-
* Cancel a signature request. This endpoint is not listed in the public
|
|
782
|
-
* Swagger but is exposed by the platform.
|
|
783
|
-
*/
|
|
784
|
-
async cancel(documentId, reason, accountId) {
|
|
785
|
-
const docId = this.requireId(documentId, "Document ID");
|
|
786
|
-
const accId = this.accountId(accountId);
|
|
787
|
-
this.logger.info("Cancelling signature request", { documentId: docId, reason });
|
|
788
|
-
return this.call(
|
|
789
|
-
"Failed to cancel signature request",
|
|
790
|
-
() => this.http.post(
|
|
791
|
-
`/accounts/${accId}/signature-requests/${docId}/cancel`,
|
|
792
|
-
{ document_id: docId, reason }
|
|
793
|
-
)
|
|
794
|
-
);
|
|
795
|
-
}
|
|
796
834
|
};
|
|
797
835
|
|
|
798
836
|
// src/resources/webhooks.ts
|
|
799
|
-
var
|
|
837
|
+
var DEFAULT_WEBHOOK_EVENTS = [
|
|
800
838
|
"document_ready",
|
|
801
839
|
"document_prepared",
|
|
802
840
|
"signer_signed_document",
|
|
@@ -804,7 +842,21 @@ var DEFAULT_EVENTS = [
|
|
|
804
842
|
"document_processing_failed"
|
|
805
843
|
];
|
|
806
844
|
var WebhookResource = class extends BaseResource {
|
|
807
|
-
/**
|
|
845
|
+
/**
|
|
846
|
+
* Register (or replace) the workspace's single webhook subscription
|
|
847
|
+
* (`PUT /accounts/{id}/webhooks/subscriptions`). There is exactly one
|
|
848
|
+
* subscription per workspace, keyed by URL.
|
|
849
|
+
*
|
|
850
|
+
* When `events` is omitted or empty, {@link DEFAULT_WEBHOOK_EVENTS} is used
|
|
851
|
+
* (`document_ready`, `document_prepared`, `signer_signed_document`,
|
|
852
|
+
* `signer_rejected_document`, `document_processing_failed`).
|
|
853
|
+
*
|
|
854
|
+
* @example
|
|
855
|
+
* ```ts
|
|
856
|
+
* await client.webhooks.register({ url: 'https://example.com/hook', email: 'ops@example.com' });
|
|
857
|
+
* // → { url, email, events: [...], is_active: true, updated_at: '2026-…' }
|
|
858
|
+
* ```
|
|
859
|
+
*/
|
|
808
860
|
async register(payload, accountId) {
|
|
809
861
|
if (!payload.url) throw new ValidationError("Webhook URL is required");
|
|
810
862
|
if (!payload.email) throw new ValidationError("Webhook email is required");
|
|
@@ -812,7 +864,7 @@ var WebhookResource = class extends BaseResource {
|
|
|
812
864
|
const body = {
|
|
813
865
|
url: payload.url,
|
|
814
866
|
email: payload.email,
|
|
815
|
-
events: payload.events && payload.events.length > 0 ? payload.events :
|
|
867
|
+
events: payload.events && payload.events.length > 0 ? payload.events : DEFAULT_WEBHOOK_EVENTS,
|
|
816
868
|
is_active: payload.is_active ?? true
|
|
817
869
|
};
|
|
818
870
|
this.logger.info("Registering webhook", { url: payload.url });
|
|
@@ -877,6 +929,46 @@ var WebhookResource = class extends BaseResource {
|
|
|
877
929
|
|
|
878
930
|
// src/resources/templates.ts
|
|
879
931
|
var TemplateResource = class extends BaseResource {
|
|
932
|
+
/**
|
|
933
|
+
* Create a template by uploading a PDF (`POST /accounts/{id}/templates`).
|
|
934
|
+
*
|
|
935
|
+
* The template is created in `Uploaded` status and transitions to `Ready`
|
|
936
|
+
* once the platform finishes processing its pages. Configure roles/fields
|
|
937
|
+
* afterwards in the Assinafy editor.
|
|
938
|
+
*
|
|
939
|
+
* @example
|
|
940
|
+
* ```ts
|
|
941
|
+
* const tmpl = await client.templates.create(
|
|
942
|
+
* { filePath: './nda.pdf' },
|
|
943
|
+
* { name: 'NDA template' },
|
|
944
|
+
* );
|
|
945
|
+
* // → { resource: 'template', id, name, status: 'Uploaded',
|
|
946
|
+
* // roles: [{ id, name: 'TemplateEditor', assignment_type: 'Editor' }],
|
|
947
|
+
* // pages: [], tags: [], created_at, updated_at }
|
|
948
|
+
* ```
|
|
949
|
+
*/
|
|
950
|
+
async create(source, options = {}) {
|
|
951
|
+
const { buffer, fileName } = await loadSource(source);
|
|
952
|
+
validateUpload(buffer, fileName);
|
|
953
|
+
const id = this.accountId(options.accountId);
|
|
954
|
+
const formOptions = {};
|
|
955
|
+
if (options.name !== void 0) formOptions.name = options.name;
|
|
956
|
+
const form = buildUploadForm(buffer, fileName, formOptions);
|
|
957
|
+
this.logger.info("Creating template", { fileName, size: buffer.byteLength });
|
|
958
|
+
const template = await this.call(
|
|
959
|
+
"Failed to create template",
|
|
960
|
+
() => this.http.post(`/accounts/${id}/templates`, form, {
|
|
961
|
+
headers: { "Content-Type": "multipart/form-data" }
|
|
962
|
+
})
|
|
963
|
+
);
|
|
964
|
+
if (!template?.id) {
|
|
965
|
+
throw new ValidationError("Template upload succeeded but no template ID was returned", {
|
|
966
|
+
response: template
|
|
967
|
+
});
|
|
968
|
+
}
|
|
969
|
+
this.logger.info("Template created", { templateId: template.id });
|
|
970
|
+
return template;
|
|
971
|
+
}
|
|
880
972
|
/** List templates for the workspace. */
|
|
881
973
|
async list(params = {}, accountId) {
|
|
882
974
|
const id = this.accountId(accountId);
|
|
@@ -886,11 +978,10 @@ var TemplateResource = class extends BaseResource {
|
|
|
886
978
|
);
|
|
887
979
|
}
|
|
888
980
|
/**
|
|
889
|
-
* Get a template by ID.
|
|
981
|
+
* Get a template by ID (`GET /accounts/{id}/templates/{template_id}`).
|
|
890
982
|
*
|
|
891
|
-
*
|
|
892
|
-
*
|
|
893
|
-
* by the official PHP SDK.
|
|
983
|
+
* Unlike the list endpoint, the single-template response includes `pages`
|
|
984
|
+
* (with per-page `download_url`) and `default_document_tags`.
|
|
894
985
|
*/
|
|
895
986
|
async get(templateId, accountId) {
|
|
896
987
|
const id = this.accountId(accountId);
|
|
@@ -901,9 +992,40 @@ var TemplateResource = class extends BaseResource {
|
|
|
901
992
|
);
|
|
902
993
|
}
|
|
903
994
|
/**
|
|
904
|
-
* `
|
|
905
|
-
*
|
|
906
|
-
*
|
|
995
|
+
* Update a template's `name` and/or default `message`
|
|
996
|
+
* (`PUT /accounts/{id}/templates/{template_id}`). Returns the updated template.
|
|
997
|
+
*
|
|
998
|
+
* @example
|
|
999
|
+
* ```ts
|
|
1000
|
+
* await client.templates.update(templateId, { name: 'NDA v2', message: 'Please sign' });
|
|
1001
|
+
* ```
|
|
1002
|
+
*/
|
|
1003
|
+
async update(templateId, payload, accountId) {
|
|
1004
|
+
const id = this.accountId(accountId);
|
|
1005
|
+
const tmplId = this.requireId(templateId, "Template ID");
|
|
1006
|
+
return this.call(
|
|
1007
|
+
"Failed to update template",
|
|
1008
|
+
() => this.http.put(
|
|
1009
|
+
`/accounts/${id}/templates/${tmplId}`,
|
|
1010
|
+
cleanParams(payload)
|
|
1011
|
+
)
|
|
1012
|
+
);
|
|
1013
|
+
}
|
|
1014
|
+
/** Delete a template (`DELETE /accounts/{id}/templates/{template_id}`). */
|
|
1015
|
+
async delete(templateId, accountId) {
|
|
1016
|
+
const id = this.accountId(accountId);
|
|
1017
|
+
const tmplId = this.requireId(templateId, "Template ID");
|
|
1018
|
+
return this.callVoid(
|
|
1019
|
+
"Failed to delete template",
|
|
1020
|
+
() => this.http.delete(`/accounts/${id}/templates/${tmplId}`)
|
|
1021
|
+
);
|
|
1022
|
+
}
|
|
1023
|
+
/**
|
|
1024
|
+
* Download a template page as a JPEG
|
|
1025
|
+
* (`GET /accounts/{id}/templates/{template_id}/pages/{page_id}/download`).
|
|
1026
|
+
*
|
|
1027
|
+
* Used by template editors to render page thumbnails on the client. The
|
|
1028
|
+
* matching `download_url` is also returned on each `template.pages[]` entry.
|
|
907
1029
|
*/
|
|
908
1030
|
async downloadPage(templateId, pageId, accountId) {
|
|
909
1031
|
const id = this.accountId(accountId);
|
|
@@ -1271,7 +1393,12 @@ var SignerDocumentsResource = class extends BaseResource {
|
|
|
1271
1393
|
})
|
|
1272
1394
|
);
|
|
1273
1395
|
}
|
|
1274
|
-
/**
|
|
1396
|
+
/**
|
|
1397
|
+
* `GET /sign?signer-access-code=…` — fetch the assignment as the signer sees it.
|
|
1398
|
+
*
|
|
1399
|
+
* @param hasAcceptedTerms maps to the `has_accepted_terms` query param
|
|
1400
|
+
* (server default `false`); pass `true` once the signer has accepted terms.
|
|
1401
|
+
*/
|
|
1275
1402
|
async getAssignment(signerAccessCode, hasAcceptedTerms) {
|
|
1276
1403
|
const code = this.requireId(signerAccessCode, "signer-access-code");
|
|
1277
1404
|
return this.call(
|
|
@@ -1301,8 +1428,8 @@ var SignerDocumentsResource = class extends BaseResource {
|
|
|
1301
1428
|
}
|
|
1302
1429
|
/**
|
|
1303
1430
|
* `PUT /documents/{documentId}/assignments/{assignmentId}/reject?signer-access-code=…`
|
|
1304
|
-
* — signer-side decline. (
|
|
1305
|
-
* workspace
|
|
1431
|
+
* — signer-side decline. (The workspace-side equivalent is to delete the
|
|
1432
|
+
* document via `documents.delete`; there is no workspace "cancel" endpoint.)
|
|
1306
1433
|
*/
|
|
1307
1434
|
async decline(documentId, assignmentId, signerAccessCode, declineReason) {
|
|
1308
1435
|
const did = this.requireId(documentId, "Document ID");
|
|
@@ -1393,6 +1520,10 @@ var AssinafyClient = class _AssinafyClient {
|
|
|
1393
1520
|
timeout: options.timeout ?? 3e4,
|
|
1394
1521
|
headers
|
|
1395
1522
|
});
|
|
1523
|
+
const maxRetries = options.maxRetries ?? 2;
|
|
1524
|
+
if (maxRetries > 0) {
|
|
1525
|
+
installRateLimitRetry(this.axiosInstance, maxRetries, this.logger);
|
|
1526
|
+
}
|
|
1396
1527
|
this.documents = new DocumentResource(this.axiosInstance, this.defaultAccountId, this.logger);
|
|
1397
1528
|
this.signers = new SignerResource(this.axiosInstance, this.defaultAccountId, this.logger);
|
|
1398
1529
|
this.workspaces = new WorkspaceResource(this.axiosInstance, void 0, this.logger);
|
|
@@ -1431,6 +1562,7 @@ var AssinafyClient = class _AssinafyClient {
|
|
|
1431
1562
|
if (baseUrl !== void 0) opts.baseUrl = baseUrl;
|
|
1432
1563
|
if (webhookSecret !== void 0) opts.webhookSecret = webhookSecret;
|
|
1433
1564
|
if (config.timeout !== void 0) opts.timeout = config.timeout;
|
|
1565
|
+
if (config.maxRetries !== void 0) opts.maxRetries = config.maxRetries;
|
|
1434
1566
|
if (config.logger !== void 0) opts.logger = config.logger;
|
|
1435
1567
|
return new _AssinafyClient(opts);
|
|
1436
1568
|
}
|
|
@@ -1490,12 +1622,34 @@ var AssinafyClient = class _AssinafyClient {
|
|
|
1490
1622
|
function normaliseBaseUrl(raw) {
|
|
1491
1623
|
return raw.endsWith("/") ? raw.slice(0, -1) : raw;
|
|
1492
1624
|
}
|
|
1625
|
+
function installRateLimitRetry(http, maxRetries, logger) {
|
|
1626
|
+
http.interceptors.response.use(
|
|
1627
|
+
(response) => response,
|
|
1628
|
+
async (error) => {
|
|
1629
|
+
if (!axios2.isAxiosError(error) || error.response?.status !== 429 || !error.config) {
|
|
1630
|
+
throw error;
|
|
1631
|
+
}
|
|
1632
|
+
const config = error.config;
|
|
1633
|
+
const attempt = (config._retryCount ?? 0) + 1;
|
|
1634
|
+
if (attempt > maxRetries) throw error;
|
|
1635
|
+
config._retryCount = attempt;
|
|
1636
|
+
const delayMs = nextRetryDelayMs(
|
|
1637
|
+
error.response.headers,
|
|
1638
|
+
attempt
|
|
1639
|
+
);
|
|
1640
|
+
logger.warn("Rate limited (429); retrying after delay", { attempt, maxRetries, delayMs });
|
|
1641
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
1642
|
+
return http(config);
|
|
1643
|
+
}
|
|
1644
|
+
);
|
|
1645
|
+
}
|
|
1493
1646
|
export {
|
|
1494
1647
|
ApiError,
|
|
1495
1648
|
AssignmentResource,
|
|
1496
1649
|
AssinafyClient,
|
|
1497
1650
|
AssinafyError,
|
|
1498
1651
|
AuthenticationResource,
|
|
1652
|
+
DEFAULT_WEBHOOK_EVENTS,
|
|
1499
1653
|
DocumentResource,
|
|
1500
1654
|
FieldsResource,
|
|
1501
1655
|
NetworkError,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assinafy/sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "TypeScript SDK for Assinafy API - Digital signature platform",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
"scripts": {
|
|
16
16
|
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
17
17
|
"test": "bun test",
|
|
18
|
-
"lint": "eslint src
|
|
19
|
-
"lint:fix": "eslint src --
|
|
18
|
+
"lint": "eslint src",
|
|
19
|
+
"lint:fix": "eslint src --fix",
|
|
20
20
|
"prepublishOnly": "bun run build",
|
|
21
21
|
"release": "bun run typecheck && bun run lint && bun test && bun run build && npm publish --access public && npm publish --registry=https://npm.pkg.github.com",
|
|
22
22
|
"typecheck": "tsc --noEmit"
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"author": "Assinafy API Team",
|
|
41
41
|
"license": "MIT",
|
|
42
42
|
"engines": {
|
|
43
|
-
"node": ">=
|
|
43
|
+
"node": ">=20"
|
|
44
44
|
},
|
|
45
45
|
"repository": {
|
|
46
46
|
"type": "git",
|
|
@@ -51,16 +51,19 @@
|
|
|
51
51
|
},
|
|
52
52
|
"homepage": "https://github.com/assinafy/typescript-sdk/#readme",
|
|
53
53
|
"devDependencies": {
|
|
54
|
+
"@eslint/js": "^9.0.0",
|
|
54
55
|
"@types/bun": "^1.3.0",
|
|
55
|
-
"
|
|
56
|
-
"@typescript-eslint/parser": "^6.0.0",
|
|
57
|
-
"eslint": "^8.0.0",
|
|
56
|
+
"eslint": "^9.0.0",
|
|
58
57
|
"tsup": "^8.0.0",
|
|
59
|
-
"typescript": "^5.0.0"
|
|
58
|
+
"typescript": "^5.0.0",
|
|
59
|
+
"typescript-eslint": "^8.0.0"
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
62
|
"axios": "^1.6.0"
|
|
63
63
|
},
|
|
64
|
+
"overrides": {
|
|
65
|
+
"esbuild": "0.27.0"
|
|
66
|
+
},
|
|
64
67
|
"files": [
|
|
65
68
|
"dist",
|
|
66
69
|
"README.md",
|