@assinafy/sdk 2.1.2 → 2.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/CHANGELOG.md +99 -1
- package/README.md +47 -6
- package/SECURITY.md +4 -2
- package/dist/index.d.mts +26 -10
- package/dist/index.d.ts +26 -10
- package/dist/index.js +64 -32
- package/dist/index.mjs +63 -32
- package/docs/COMPATIBILITY.md +23 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,102 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.2.0] - 2026-08-27
|
|
11
|
+
|
|
12
|
+
### Security
|
|
13
|
+
|
|
14
|
+
- **`baseUrl` now requires `https` for any remote host.** Every request on the
|
|
15
|
+
authenticated transport carries the caller's `X-Api-Key` or bearer token, so
|
|
16
|
+
a plaintext `http://` base URL put a long-lived credential on the wire in
|
|
17
|
+
cleartext. `http` remains accepted when the host is loopback (`localhost`,
|
|
18
|
+
the `127.0.0.0/8` block, or `::1`) so local mock servers and the packed
|
|
19
|
+
consumer smoke test keep working; anything else is rejected with a
|
|
20
|
+
`ValidationError` at construction time. `SECURITY.md` already required HTTPS
|
|
21
|
+
endpoints — the client now enforces it rather than trusting configuration.
|
|
22
|
+
- Callers pointing at a remote `http://` host must switch to `https://`.
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- **`signers.findByEmail()` requested 100 rows per page and received 50.** The
|
|
27
|
+
API clamps `per-page` to 50 and answers `200`, so the lookup scanned half the
|
|
28
|
+
window its documentation claimed. It now pins the real maximum, and the
|
|
29
|
+
method and `signers.list()` documentation state the clamp instead of naming
|
|
30
|
+
100 as the API maximum. `documents.list()` carried the same incorrect claim.
|
|
31
|
+
- **Unstructured error bodies were reported as the generic "API request
|
|
32
|
+
failed".** A gateway or proxy in front of the API answers with `text/plain`
|
|
33
|
+
or HTML rather than the Assinafy JSON envelope. `ApiError.fromResponse()`
|
|
34
|
+
only read `message`/`error` off an object, so exactly the failures with no
|
|
35
|
+
structured body to explain them lost their only explanation. A non-JSON body
|
|
36
|
+
is now used as the message, with whitespace collapsed and the text capped at
|
|
37
|
+
500 characters; the untouched body remains on `ApiError.responseData`.
|
|
38
|
+
|
|
39
|
+
### Added
|
|
40
|
+
|
|
41
|
+
- **`MAX_LIST_PAGE_SIZE`** — the largest page any list endpoint returns (50).
|
|
42
|
+
Exported so callers can size pagination loops against the value the server
|
|
43
|
+
actually honours rather than the one they request.
|
|
44
|
+
|
|
45
|
+
### Changed
|
|
46
|
+
|
|
47
|
+
- The email and E.164 phone-number patterns had five and two copies across the
|
|
48
|
+
resource files. They are now `isEmail()`, `assertEmail()`, and
|
|
49
|
+
`isE164PhoneNumber()` in `src/utils.ts`, so the accepted shape of a contact
|
|
50
|
+
value is defined in one place. Validation behaviour and error messages are
|
|
51
|
+
unchanged.
|
|
52
|
+
- `decodeBinaryErrorBody()` returns a non-JSON binary body as a string instead
|
|
53
|
+
of wrapping it in `{ message }`, so both unstructured-body paths converge on
|
|
54
|
+
the single rule in `ApiError.fromResponse()`.
|
|
55
|
+
- GitLab CI reads the Bun version from `package.json`'s `packageManager` field
|
|
56
|
+
instead of repeating the literal, matching how GitHub Actions' `setup-bun`
|
|
57
|
+
resolves it. Both CIs now move together when that field is bumped.
|
|
58
|
+
- README gained a contents map linking the setup, end-to-end workflow, and
|
|
59
|
+
per-resource sections; list examples use the `per-page` spelling the API
|
|
60
|
+
reads and document the page-size clamp.
|
|
61
|
+
|
|
62
|
+
## [2.1.2] - 2026-08-26
|
|
63
|
+
|
|
64
|
+
### Added
|
|
65
|
+
|
|
66
|
+
- `SDK_USER_AGENT` — every transport, including public and signer-access-code
|
|
67
|
+
requests, now identifies itself as `Assinafy-Typescript-SDK/v<VERSION>`.
|
|
68
|
+
- `scripts/node-consumer-smoke.mjs`, exercised by CI and the release workflow:
|
|
69
|
+
it installs the packed tarball into an isolated consumer and drives the real
|
|
70
|
+
Node HTTP, auth, and multipart paths through both CJS and ESM entrypoints.
|
|
71
|
+
- A manual **Sandbox integration** workflow with `read-only` and
|
|
72
|
+
`disposable-full` modes, gated on a protected environment.
|
|
73
|
+
|
|
74
|
+
### Security
|
|
75
|
+
|
|
76
|
+
- Requests on the authenticated transport are pinned to the configured API
|
|
77
|
+
origin. A cross-origin URL is rejected before dispatch, and `Authorization`
|
|
78
|
+
and `X-Api-Key` are marked redirect-sensitive so the redirect stack strips
|
|
79
|
+
them.
|
|
80
|
+
- Cross-origin redirects are blocked unless they are a `GET`/`HEAD` to an
|
|
81
|
+
`https` target whose source URL carries no sensitive query parameter.
|
|
82
|
+
- Public, login, and signer-access-code flows run on a transport whose
|
|
83
|
+
credential headers are removed at construction, so a configured API key
|
|
84
|
+
cannot reach a public endpoint.
|
|
85
|
+
- `AxiosError` is no longer attached as an SDK error `cause`. It retains the
|
|
86
|
+
full request config, including credentials and body, which error reporters
|
|
87
|
+
commonly serialize. Transport failures now carry a sanitized cause with
|
|
88
|
+
credential-shaped values redacted from the message.
|
|
89
|
+
- Caller-supplied loggers receive an allowlist of numeric operational counters
|
|
90
|
+
only. Logger exceptions and rejected promises are swallowed so telemetry
|
|
91
|
+
cannot change request semantics.
|
|
92
|
+
|
|
93
|
+
### Fixed
|
|
94
|
+
|
|
95
|
+
- `AssinafyClient.create()` no longer let its options bag override the
|
|
96
|
+
`apiKey`/`accountId` passed positionally, and it rejects missing or malformed
|
|
97
|
+
credentials instead of constructing an unusable client.
|
|
98
|
+
- Constructor options are validated up front: a non-object bag, a blank
|
|
99
|
+
credential, or an invalid `timeout`/`maxRetries` throws `ValidationError`.
|
|
100
|
+
- `uploadAndRequestSignatures()` validates every signer before uploading, so an
|
|
101
|
+
invalid later signer no longer leaves an orphaned document behind, and it no
|
|
102
|
+
longer waits for `metadata_ready` before creating a virtual assignment.
|
|
103
|
+
- HTTP 429 replay is limited to `GET`, `HEAD`, `OPTIONS`, and `DELETE`, with
|
|
104
|
+
`GET /sign` excluded because that read records a signer view.
|
|
105
|
+
|
|
10
106
|
## [2.1.1] - 2026-08-06
|
|
11
107
|
|
|
12
108
|
Maintenance only. The published `dist/` is byte-identical to 2.1.0; no runtime,
|
|
@@ -457,7 +553,9 @@ fields (`cpf`, `whatsapp_phone_number`) with the PHP SDK and n8n node.
|
|
|
457
553
|
- High-level `uploadAndRequestSignatures` helper on `AssinafyClient`.
|
|
458
554
|
- `PaginatedResult<T>` with parsed `X-Pagination-*` header meta.
|
|
459
555
|
|
|
460
|
-
[Unreleased]: https://github.com/assinafy/typescript-sdk/compare/v2.
|
|
556
|
+
[Unreleased]: https://github.com/assinafy/typescript-sdk/compare/v2.2.0...HEAD
|
|
557
|
+
[2.2.0]: https://github.com/assinafy/typescript-sdk/compare/v2.1.2...v2.2.0
|
|
558
|
+
[2.1.2]: https://github.com/assinafy/typescript-sdk/compare/v2.1.1...v2.1.2
|
|
461
559
|
[2.1.1]: https://github.com/assinafy/typescript-sdk/compare/v2.1.0...v2.1.1
|
|
462
560
|
[2.1.0]: https://github.com/assinafy/typescript-sdk/compare/v2.0.0...v2.1.0
|
|
463
561
|
[2.0.0]: https://github.com/assinafy/typescript-sdk/releases/tag/v2.0.0
|
package/README.md
CHANGED
|
@@ -13,6 +13,37 @@ See [API coverage](docs/API_COVERAGE.md) for the operation map and
|
|
|
13
13
|
[compatibility notes](docs/COMPATIBILITY.md) for deployment-specific request
|
|
14
14
|
and response variants.
|
|
15
15
|
|
|
16
|
+
## Contents
|
|
17
|
+
|
|
18
|
+
This document runs from setup to a complete signature workflow, then to
|
|
19
|
+
per-resource detail. Read it in order the first time; use it as a reference
|
|
20
|
+
afterwards.
|
|
21
|
+
|
|
22
|
+
**Getting set up** — [Requirements](#requirements) ·
|
|
23
|
+
[Installation](#installation) · [Quick start](#quick-start) ·
|
|
24
|
+
[Authentication](#authentication) · [Configuration](#configuration)
|
|
25
|
+
([rate limiting](#rate-limiting), [factories](#factories)) ·
|
|
26
|
+
[Endpoint coverage](#endpoint-coverage)
|
|
27
|
+
|
|
28
|
+
**The end-to-end flow** — [Document lifecycle](#document-lifecycle):
|
|
29
|
+
[upload](#1-upload-the-pdf) → [signers](#2-create-or-reuse-the-email-signers) →
|
|
30
|
+
[price and request signatures](#3-price-then-request-signatures) →
|
|
31
|
+
[the signer's side](#4-complete-the-email-signer-flow) →
|
|
32
|
+
[completion and artifacts](#5-observe-completion-and-download-artifacts)
|
|
33
|
+
|
|
34
|
+
**Per-resource detail** — [Resource reference](#resource-reference):
|
|
35
|
+
[documents](#documents) · [signers](#signers) · [assignments](#assignments) ·
|
|
36
|
+
[paid signing branches](#paid-signing-branches) · [templates](#templates) ·
|
|
37
|
+
[tags](#tags) · [workspaces](#workspaces) ·
|
|
38
|
+
[field definitions](#field-definitions) ·
|
|
39
|
+
[auth and API keys](#authentication--api-key-management) ·
|
|
40
|
+
[the current user](#authenticated-user) · [webhooks](#webhooks)
|
|
41
|
+
([verification](#webhook-verification)) ·
|
|
42
|
+
[signer-side endpoints](#signer-side-endpoints)
|
|
43
|
+
|
|
44
|
+
**Everything else** — [High-level helper](#high-level-helper) ·
|
|
45
|
+
[Errors](#errors) · [Development](#development) · [License](#license)
|
|
46
|
+
|
|
16
47
|
## Requirements
|
|
17
48
|
|
|
18
49
|
- Node.js 22+ for the built-in `FormData` / `Blob` APIs used by uploads. Packed
|
|
@@ -105,7 +136,7 @@ installed package version. The exact value is also exported as
|
|
|
105
136
|
| `apiKey` | string | — | Preferred credential (sent as `X-Api-Key`). |
|
|
106
137
|
| `token` | string | — | Access token (sent as `Authorization: Bearer`). |
|
|
107
138
|
| `accountId` | string | — | Default workspace/account ID. |
|
|
108
|
-
| `baseUrl` | string | `https://api.assinafy.com.br/v1` | Absolute
|
|
139
|
+
| `baseUrl` | string | `https://api.assinafy.com.br/v1` | Absolute API base without credentials, query, or fragment. Must be `https` unless the host is loopback. |
|
|
109
140
|
| `webhookSecret` | string | — | Opt-in HMAC secret used by `WebhookVerifier`; see its [contract caveat](docs/COMPATIBILITY.md#webhook-signature-verification-is-not-in-the-openapi-contract). |
|
|
110
141
|
| `timeout` | number | `30000` | Request timeout in milliseconds. |
|
|
111
142
|
| `maxRetries` | number | `2` | Auto-retries eligible HTTP 429 responses, honoring `Retry-After`. `0` disables. |
|
|
@@ -441,7 +472,7 @@ const doc = await client.documents.upload(
|
|
|
441
472
|
await client.documents.upload({ buffer, fileName: 'contract.pdf' });
|
|
442
473
|
|
|
443
474
|
// List → { data: IDocumentListItem[], meta?: { current_page, per_page, total, last_page } }
|
|
444
|
-
const { data, meta } = await client.documents.list({ page: 1,
|
|
475
|
+
const { data, meta } = await client.documents.list({ page: 1, 'per-page': 20, sort: 'updated_at' });
|
|
445
476
|
|
|
446
477
|
// Search is the lightweight alternative to list: same item shape, but the API
|
|
447
478
|
// skips the expanded `assignment`/`pages`. Prefer it for name lookups.
|
|
@@ -501,7 +532,17 @@ account authentication as their download operations. Prefer
|
|
|
501
532
|
credential and returns a `Buffer`. `bundle` contains `original`, `certificated`,
|
|
502
533
|
and `certificate-page`, plus `pades` when available.
|
|
503
534
|
|
|
504
|
-
List endpoints return `{ data, meta }
|
|
535
|
+
List endpoints return `{ data, meta }`, where `meta` is populated from the
|
|
536
|
+
`X-Pagination-*` response headers. Two API behaviours are worth knowing because
|
|
537
|
+
both are silent rather than errors:
|
|
538
|
+
|
|
539
|
+
- Only the hyphenated `per-page` is read. `per_page` is accepted and ignored,
|
|
540
|
+
falling back to 20 rows — so the SDK rewrites `per_page` to `per-page` on
|
|
541
|
+
every list method, and an explicit `per-page` wins when both are given.
|
|
542
|
+
- `per-page` is clamped to **50**. Asking for 100 returns 50 rows with a `200`.
|
|
543
|
+
The exported `MAX_LIST_PAGE_SIZE` constant is that ceiling; page through with
|
|
544
|
+
`page` rather than requesting a larger page, and trust `meta.per_page` over
|
|
545
|
+
the value you asked for.
|
|
505
546
|
|
|
506
547
|
### Signers
|
|
507
548
|
|
|
@@ -525,7 +566,7 @@ await client.signers.create({
|
|
|
525
566
|
});
|
|
526
567
|
|
|
527
568
|
await client.signers.get(signerId);
|
|
528
|
-
await client.signers.list({ page: 1,
|
|
569
|
+
await client.signers.list({ page: 1, 'per-page': 50, search: 'john' });
|
|
529
570
|
await client.signers.update(signerId, {
|
|
530
571
|
full_name: 'Johnny Doe',
|
|
531
572
|
government_id: '390.533.447-05', // official update field; sent as digits
|
|
@@ -718,7 +759,7 @@ const created = await client.templates.create(
|
|
|
718
759
|
// pages: [], tags: [], created_at: '2026-…', updated_at: '2026-…'
|
|
719
760
|
// }
|
|
720
761
|
|
|
721
|
-
const { data, meta } = await client.templates.list({ search: 'NDA',
|
|
762
|
+
const { data, meta } = await client.templates.list({ search: 'NDA', 'per-page': 20 });
|
|
722
763
|
const template = await client.templates.get(created.id); // includes pages[] + default_document_tags
|
|
723
764
|
await client.templates.update(created.id, { name: 'NDA v2', message: 'Please sign' });
|
|
724
765
|
const firstPage = template.pages?.[0];
|
|
@@ -1053,7 +1094,7 @@ await client.signerDocuments.self(accessCode);
|
|
|
1053
1094
|
await client.signerDocuments.verifyEmail({ signerAccessCode: accessCode, verificationCode: '123456' });
|
|
1054
1095
|
|
|
1055
1096
|
await client.signerDocuments.getCurrent(signerId, accessCode);
|
|
1056
|
-
const { data } = await client.signerDocuments.list(signerId, accessCode, {
|
|
1097
|
+
const { data } = await client.signerDocuments.list(signerId, accessCode, { 'per-page': 20 });
|
|
1057
1098
|
// Signer-side counterpart of documents.search(), authorised by the access code.
|
|
1058
1099
|
const found = await client.signerDocuments.search(signerId, accessCode, 'invoice');
|
|
1059
1100
|
await client.signerDocuments.download(signerId, documentId, 'original');
|
package/SECURITY.md
CHANGED
|
@@ -44,8 +44,10 @@ opportunity to upgrade.
|
|
|
44
44
|
authentication, and signer-access-code flows use an auth-free transport so
|
|
45
45
|
configured account credentials are not sent to public endpoints.
|
|
46
46
|
- Consumers must use HTTPS endpoints and protect all SDK configuration as
|
|
47
|
-
secrets.
|
|
48
|
-
|
|
47
|
+
secrets. The client enforces this: a `baseUrl` using plaintext `http` is
|
|
48
|
+
rejected unless its host is loopback, so a credential cannot be sent in the
|
|
49
|
+
clear to a remote host. Debug logging and error telemetry must be reviewed
|
|
50
|
+
for response data before being enabled in production.
|
|
49
51
|
- The webhook HMAC helper is an opt-in utility, not proof of an official
|
|
50
52
|
Assinafy signing contract. Confirm the header, algorithm, encoding, and secret
|
|
51
53
|
delivery mechanism with Assinafy before enforcing it. See
|
package/dist/index.d.mts
CHANGED
|
@@ -1261,7 +1261,8 @@ declare class DocumentResource extends BaseResource {
|
|
|
1261
1261
|
* @param params - Filters and pagination: `status`; `method` (`virtual` or
|
|
1262
1262
|
* `collect`); `tags` (comma-separated IDs, all of which must match);
|
|
1263
1263
|
* `search` (document name, signer name, or signer email); `sort` (`name` or
|
|
1264
|
-
* `updated_at`); `page`; and `per-page` (
|
|
1264
|
+
* `updated_at`); `page`; and `per-page` (the server clamps this to 50
|
|
1265
|
+
* rather than rejecting a larger value).
|
|
1265
1266
|
* @param accountId - Override the client's default account ID.
|
|
1266
1267
|
* @returns Matching documents, with pagination in `meta`. Each item:
|
|
1267
1268
|
* ```jsonc
|
|
@@ -2099,7 +2100,8 @@ declare class SignerResource extends BaseResource {
|
|
|
2099
2100
|
* Pagination info (if any) is attached in `meta`.
|
|
2100
2101
|
*
|
|
2101
2102
|
* @param params - `page`, `per-page`, and `search` (matches `full_name` or
|
|
2102
|
-
* `email`). The
|
|
2103
|
+
* `email`). The server clamps `per-page` to {@link MAX_LIST_PAGE_SIZE}
|
|
2104
|
+
* (50); a larger value is not rejected, it is silently reduced.
|
|
2103
2105
|
* @param accountId - Override the client's default account ID.
|
|
2104
2106
|
* @returns The matching signers, with pagination in `meta`. Each item:
|
|
2105
2107
|
* ```jsonc
|
|
@@ -2181,10 +2183,10 @@ declare class SignerResource extends BaseResource {
|
|
|
2181
2183
|
* `search` is a substring match across signer fields, so the result is
|
|
2182
2184
|
* re-filtered here for an exact, case-insensitive email match.
|
|
2183
2185
|
*
|
|
2184
|
-
* Page size is pinned to the
|
|
2185
|
-
* An exact address realistically matches one
|
|
2186
|
-
* matched more than
|
|
2187
|
-
* exact-email filter to rule that out.
|
|
2186
|
+
* Page size is pinned to {@link MAX_LIST_PAGE_SIZE}, the largest page the
|
|
2187
|
+
* server actually returns. An exact address realistically matches one
|
|
2188
|
+
* signer, but a search term that matched more than that could in principle
|
|
2189
|
+
* miss one — the API exposes no exact-email filter to rule that out.
|
|
2188
2190
|
*
|
|
2189
2191
|
* A `404` from the underlying list is treated as "no match" and mapped to
|
|
2190
2192
|
* `null`; any other {@link ApiError} propagates.
|
|
@@ -5287,9 +5289,14 @@ declare class ApiError extends AssinafyError {
|
|
|
5287
5289
|
* Convert a status/body pair into an {@link ApiError}.
|
|
5288
5290
|
*
|
|
5289
5291
|
* @param statusCode - Non-success HTTP response status.
|
|
5290
|
-
* @param responseData -
|
|
5291
|
-
* followed by string `error
|
|
5292
|
-
*
|
|
5292
|
+
* @param responseData - API body. For a JSON object, string `message` takes
|
|
5293
|
+
* priority, followed by string `error`. A non-JSON body (a proxy's
|
|
5294
|
+
* `text/plain` or HTML error page) is used verbatim rather than discarded —
|
|
5295
|
+
* otherwise the only failures reported as the generic fallback would be the
|
|
5296
|
+
* ones with no structured body to explain them. Anything else falls back to
|
|
5297
|
+
* the stable message.
|
|
5298
|
+
* @returns An `ApiError` retaining the original response body in
|
|
5299
|
+
* {@link ApiError.responseData}; `message` is truncated for legibility.
|
|
5293
5300
|
*
|
|
5294
5301
|
* @example
|
|
5295
5302
|
* ```ts
|
|
@@ -5331,6 +5338,15 @@ declare class NetworkError extends AssinafyError {
|
|
|
5331
5338
|
constructor(message: string, options?: ErrorOptions);
|
|
5332
5339
|
}
|
|
5333
5340
|
|
|
5341
|
+
/**
|
|
5342
|
+
* Largest page the list endpoints actually return.
|
|
5343
|
+
*
|
|
5344
|
+
* The API silently clamps `per-page` to this value rather than rejecting a
|
|
5345
|
+
* larger one, so a caller asking for 100 receives 50 and no error. Methods that
|
|
5346
|
+
* need "as many rows as one request can give" pin this instead of guessing.
|
|
5347
|
+
*/
|
|
5348
|
+
declare const MAX_LIST_PAGE_SIZE = 50;
|
|
5349
|
+
|
|
5334
5350
|
declare const SDK_USER_AGENT: string;
|
|
5335
5351
|
|
|
5336
|
-
export { type AccountLogoUploadSource, type AnyString, ApiError, type AssignmentDisplaySettings, type AssignmentMethod, type AssignmentNotificationMethod, AssignmentResource, type AssignmentVerificationMethod, AssinafyClient, type AssinafyClientOptions, AssinafyError, AuthenticationResource, type ClientConfigInput, DEFAULT_WEBHOOK_EVENTS, type DocumentArtifactName, DocumentResource, type DocumentStatsGranularity, type DocumentStatus, type DocumentUploadSource, FieldsResource, type IAccountTheme, type IApiKeyResponse, type IAssignment, type IAssignmentCostSigner, type IAssignmentEntry, type IAssignmentItem, type IAssignmentListParams, type IAssignmentListResponse, type IAssignmentSigner, type IAuthenticatedUser, type IConfirmSignerDataPayload, type ICostEstimate, type ICreateAssignmentPayload, type ICreateAssignmentResponse, type ICreateDocumentFromTemplateOptions, type ICreateFieldPayload, type ICreateSignerPayload, type ICreateSignerResponse, type ICreateTagPayload, type ICreateWorkspacePayload, type IDeleteTagResponse, type IDetachDocumentTagResponse, type IDisplaySettings, type IDocumentActivity, type IDocumentDetailsResponse, type IDocumentListItem, type IDocumentListParams, type IDocumentListResponse, type IDocumentSearchParams, type IDocumentStatsParams, type IDocumentStatsRow, type IDocumentStatusInfo, type IDocumentUploadOptions, type IDocumentUploadResponse, type IDocumentVerification, type IEstimateAssignmentCostPayload, type IFieldDefinition, type IFieldType, type IFieldValidateMultipleEntry, type IFieldValidationMultipleResult, type IFieldValidationResponse, type IFieldValidationResult, type IInlineTag, type ILegacyConfirmSignerDataPayload, type ILegacyResendCostEstimate, type ILegacyUploadSignatureOptions, type IListParams, type ILoginResponse, type IMaskedApiKeyResponse, type INotificationHistoryEntry, type INotificationPreferences, type IPage, type IPaginatedResponse, type IPublicDocumentInfo, type IRenameDocumentResponse, type IResendCostEstimate, type IResendEmailResponse, type ISignFieldEntry, type ISigner, type ISignerListParams, type ISignerListResponse, type ISignerSelf, type ISigningProgress, type ITag, type ITemplateCostSigner, type ITemplateDetailsResponse, type ITemplateFieldPlacement, type ITemplateListItem, type ITemplateListParams, type ITemplateListResponse, type ITemplateRole, type ITemplateSigner, type IUpdateFieldPayload, type IUpdateNotificationPreferences, type IUpdateSignerPayload, type IUpdateTagPayload, type IUpdateTemplatePayload, type IUpdateWorkspacePayload, type IUploadAndRequestSignaturesResult, type IUploadAndRequestSignaturesSigner, type IUploadSignatureOptions, type IWebhookDispatch, type IWebhookDispatchListParams, type IWebhookEventTypeInfo, type IWebhookPayload, type IWebhookRegisterPayload, type IWebhookSubscription, type IWhatsAppNotification, type IWorkspaceListItem, type IWorkspaceListResponse, type IWorkspaceResponse, type Logger, MAX_UPLOAD_BYTES, NetworkError, type NotificationSenderType, type PaginatedResult, type PaginationMeta, SDK_USER_AGENT, type SendTokenChannel, type SignatureImageType, SignerDocumentsResource, type SignerReference, SignerResource, TagResource, TemplateResource, UserResource, ValidationError, type WebhookEventType, WebhookResource, WebhookVerifier, WorkspaceResource, buildAssignmentPayload };
|
|
5352
|
+
export { type AccountLogoUploadSource, type AnyString, ApiError, type AssignmentDisplaySettings, type AssignmentMethod, type AssignmentNotificationMethod, AssignmentResource, type AssignmentVerificationMethod, AssinafyClient, type AssinafyClientOptions, AssinafyError, AuthenticationResource, type ClientConfigInput, DEFAULT_WEBHOOK_EVENTS, type DocumentArtifactName, DocumentResource, type DocumentStatsGranularity, type DocumentStatus, type DocumentUploadSource, FieldsResource, type IAccountTheme, type IApiKeyResponse, type IAssignment, type IAssignmentCostSigner, type IAssignmentEntry, type IAssignmentItem, type IAssignmentListParams, type IAssignmentListResponse, type IAssignmentSigner, type IAuthenticatedUser, type IConfirmSignerDataPayload, type ICostEstimate, type ICreateAssignmentPayload, type ICreateAssignmentResponse, type ICreateDocumentFromTemplateOptions, type ICreateFieldPayload, type ICreateSignerPayload, type ICreateSignerResponse, type ICreateTagPayload, type ICreateWorkspacePayload, type IDeleteTagResponse, type IDetachDocumentTagResponse, type IDisplaySettings, type IDocumentActivity, type IDocumentDetailsResponse, type IDocumentListItem, type IDocumentListParams, type IDocumentListResponse, type IDocumentSearchParams, type IDocumentStatsParams, type IDocumentStatsRow, type IDocumentStatusInfo, type IDocumentUploadOptions, type IDocumentUploadResponse, type IDocumentVerification, type IEstimateAssignmentCostPayload, type IFieldDefinition, type IFieldType, type IFieldValidateMultipleEntry, type IFieldValidationMultipleResult, type IFieldValidationResponse, type IFieldValidationResult, type IInlineTag, type ILegacyConfirmSignerDataPayload, type ILegacyResendCostEstimate, type ILegacyUploadSignatureOptions, type IListParams, type ILoginResponse, type IMaskedApiKeyResponse, type INotificationHistoryEntry, type INotificationPreferences, type IPage, type IPaginatedResponse, type IPublicDocumentInfo, type IRenameDocumentResponse, type IResendCostEstimate, type IResendEmailResponse, type ISignFieldEntry, type ISigner, type ISignerListParams, type ISignerListResponse, type ISignerSelf, type ISigningProgress, type ITag, type ITemplateCostSigner, type ITemplateDetailsResponse, type ITemplateFieldPlacement, type ITemplateListItem, type ITemplateListParams, type ITemplateListResponse, type ITemplateRole, type ITemplateSigner, type IUpdateFieldPayload, type IUpdateNotificationPreferences, type IUpdateSignerPayload, type IUpdateTagPayload, type IUpdateTemplatePayload, type IUpdateWorkspacePayload, type IUploadAndRequestSignaturesResult, type IUploadAndRequestSignaturesSigner, type IUploadSignatureOptions, type IWebhookDispatch, type IWebhookDispatchListParams, type IWebhookEventTypeInfo, type IWebhookPayload, type IWebhookRegisterPayload, type IWebhookSubscription, type IWhatsAppNotification, type IWorkspaceListItem, type IWorkspaceListResponse, type IWorkspaceResponse, type Logger, MAX_LIST_PAGE_SIZE, MAX_UPLOAD_BYTES, NetworkError, type NotificationSenderType, type PaginatedResult, type PaginationMeta, SDK_USER_AGENT, type SendTokenChannel, type SignatureImageType, SignerDocumentsResource, type SignerReference, SignerResource, TagResource, TemplateResource, UserResource, ValidationError, type WebhookEventType, WebhookResource, WebhookVerifier, WorkspaceResource, buildAssignmentPayload };
|
package/dist/index.d.ts
CHANGED
|
@@ -1261,7 +1261,8 @@ declare class DocumentResource extends BaseResource {
|
|
|
1261
1261
|
* @param params - Filters and pagination: `status`; `method` (`virtual` or
|
|
1262
1262
|
* `collect`); `tags` (comma-separated IDs, all of which must match);
|
|
1263
1263
|
* `search` (document name, signer name, or signer email); `sort` (`name` or
|
|
1264
|
-
* `updated_at`); `page`; and `per-page` (
|
|
1264
|
+
* `updated_at`); `page`; and `per-page` (the server clamps this to 50
|
|
1265
|
+
* rather than rejecting a larger value).
|
|
1265
1266
|
* @param accountId - Override the client's default account ID.
|
|
1266
1267
|
* @returns Matching documents, with pagination in `meta`. Each item:
|
|
1267
1268
|
* ```jsonc
|
|
@@ -2099,7 +2100,8 @@ declare class SignerResource extends BaseResource {
|
|
|
2099
2100
|
* Pagination info (if any) is attached in `meta`.
|
|
2100
2101
|
*
|
|
2101
2102
|
* @param params - `page`, `per-page`, and `search` (matches `full_name` or
|
|
2102
|
-
* `email`). The
|
|
2103
|
+
* `email`). The server clamps `per-page` to {@link MAX_LIST_PAGE_SIZE}
|
|
2104
|
+
* (50); a larger value is not rejected, it is silently reduced.
|
|
2103
2105
|
* @param accountId - Override the client's default account ID.
|
|
2104
2106
|
* @returns The matching signers, with pagination in `meta`. Each item:
|
|
2105
2107
|
* ```jsonc
|
|
@@ -2181,10 +2183,10 @@ declare class SignerResource extends BaseResource {
|
|
|
2181
2183
|
* `search` is a substring match across signer fields, so the result is
|
|
2182
2184
|
* re-filtered here for an exact, case-insensitive email match.
|
|
2183
2185
|
*
|
|
2184
|
-
* Page size is pinned to the
|
|
2185
|
-
* An exact address realistically matches one
|
|
2186
|
-
* matched more than
|
|
2187
|
-
* exact-email filter to rule that out.
|
|
2186
|
+
* Page size is pinned to {@link MAX_LIST_PAGE_SIZE}, the largest page the
|
|
2187
|
+
* server actually returns. An exact address realistically matches one
|
|
2188
|
+
* signer, but a search term that matched more than that could in principle
|
|
2189
|
+
* miss one — the API exposes no exact-email filter to rule that out.
|
|
2188
2190
|
*
|
|
2189
2191
|
* A `404` from the underlying list is treated as "no match" and mapped to
|
|
2190
2192
|
* `null`; any other {@link ApiError} propagates.
|
|
@@ -5287,9 +5289,14 @@ declare class ApiError extends AssinafyError {
|
|
|
5287
5289
|
* Convert a status/body pair into an {@link ApiError}.
|
|
5288
5290
|
*
|
|
5289
5291
|
* @param statusCode - Non-success HTTP response status.
|
|
5290
|
-
* @param responseData -
|
|
5291
|
-
* followed by string `error
|
|
5292
|
-
*
|
|
5292
|
+
* @param responseData - API body. For a JSON object, string `message` takes
|
|
5293
|
+
* priority, followed by string `error`. A non-JSON body (a proxy's
|
|
5294
|
+
* `text/plain` or HTML error page) is used verbatim rather than discarded —
|
|
5295
|
+
* otherwise the only failures reported as the generic fallback would be the
|
|
5296
|
+
* ones with no structured body to explain them. Anything else falls back to
|
|
5297
|
+
* the stable message.
|
|
5298
|
+
* @returns An `ApiError` retaining the original response body in
|
|
5299
|
+
* {@link ApiError.responseData}; `message` is truncated for legibility.
|
|
5293
5300
|
*
|
|
5294
5301
|
* @example
|
|
5295
5302
|
* ```ts
|
|
@@ -5331,6 +5338,15 @@ declare class NetworkError extends AssinafyError {
|
|
|
5331
5338
|
constructor(message: string, options?: ErrorOptions);
|
|
5332
5339
|
}
|
|
5333
5340
|
|
|
5341
|
+
/**
|
|
5342
|
+
* Largest page the list endpoints actually return.
|
|
5343
|
+
*
|
|
5344
|
+
* The API silently clamps `per-page` to this value rather than rejecting a
|
|
5345
|
+
* larger one, so a caller asking for 100 receives 50 and no error. Methods that
|
|
5346
|
+
* need "as many rows as one request can give" pin this instead of guessing.
|
|
5347
|
+
*/
|
|
5348
|
+
declare const MAX_LIST_PAGE_SIZE = 50;
|
|
5349
|
+
|
|
5334
5350
|
declare const SDK_USER_AGENT: string;
|
|
5335
5351
|
|
|
5336
|
-
export { type AccountLogoUploadSource, type AnyString, ApiError, type AssignmentDisplaySettings, type AssignmentMethod, type AssignmentNotificationMethod, AssignmentResource, type AssignmentVerificationMethod, AssinafyClient, type AssinafyClientOptions, AssinafyError, AuthenticationResource, type ClientConfigInput, DEFAULT_WEBHOOK_EVENTS, type DocumentArtifactName, DocumentResource, type DocumentStatsGranularity, type DocumentStatus, type DocumentUploadSource, FieldsResource, type IAccountTheme, type IApiKeyResponse, type IAssignment, type IAssignmentCostSigner, type IAssignmentEntry, type IAssignmentItem, type IAssignmentListParams, type IAssignmentListResponse, type IAssignmentSigner, type IAuthenticatedUser, type IConfirmSignerDataPayload, type ICostEstimate, type ICreateAssignmentPayload, type ICreateAssignmentResponse, type ICreateDocumentFromTemplateOptions, type ICreateFieldPayload, type ICreateSignerPayload, type ICreateSignerResponse, type ICreateTagPayload, type ICreateWorkspacePayload, type IDeleteTagResponse, type IDetachDocumentTagResponse, type IDisplaySettings, type IDocumentActivity, type IDocumentDetailsResponse, type IDocumentListItem, type IDocumentListParams, type IDocumentListResponse, type IDocumentSearchParams, type IDocumentStatsParams, type IDocumentStatsRow, type IDocumentStatusInfo, type IDocumentUploadOptions, type IDocumentUploadResponse, type IDocumentVerification, type IEstimateAssignmentCostPayload, type IFieldDefinition, type IFieldType, type IFieldValidateMultipleEntry, type IFieldValidationMultipleResult, type IFieldValidationResponse, type IFieldValidationResult, type IInlineTag, type ILegacyConfirmSignerDataPayload, type ILegacyResendCostEstimate, type ILegacyUploadSignatureOptions, type IListParams, type ILoginResponse, type IMaskedApiKeyResponse, type INotificationHistoryEntry, type INotificationPreferences, type IPage, type IPaginatedResponse, type IPublicDocumentInfo, type IRenameDocumentResponse, type IResendCostEstimate, type IResendEmailResponse, type ISignFieldEntry, type ISigner, type ISignerListParams, type ISignerListResponse, type ISignerSelf, type ISigningProgress, type ITag, type ITemplateCostSigner, type ITemplateDetailsResponse, type ITemplateFieldPlacement, type ITemplateListItem, type ITemplateListParams, type ITemplateListResponse, type ITemplateRole, type ITemplateSigner, type IUpdateFieldPayload, type IUpdateNotificationPreferences, type IUpdateSignerPayload, type IUpdateTagPayload, type IUpdateTemplatePayload, type IUpdateWorkspacePayload, type IUploadAndRequestSignaturesResult, type IUploadAndRequestSignaturesSigner, type IUploadSignatureOptions, type IWebhookDispatch, type IWebhookDispatchListParams, type IWebhookEventTypeInfo, type IWebhookPayload, type IWebhookRegisterPayload, type IWebhookSubscription, type IWhatsAppNotification, type IWorkspaceListItem, type IWorkspaceListResponse, type IWorkspaceResponse, type Logger, MAX_UPLOAD_BYTES, NetworkError, type NotificationSenderType, type PaginatedResult, type PaginationMeta, SDK_USER_AGENT, type SendTokenChannel, type SignatureImageType, SignerDocumentsResource, type SignerReference, SignerResource, TagResource, TemplateResource, UserResource, ValidationError, type WebhookEventType, WebhookResource, WebhookVerifier, WorkspaceResource, buildAssignmentPayload };
|
|
5352
|
+
export { type AccountLogoUploadSource, type AnyString, ApiError, type AssignmentDisplaySettings, type AssignmentMethod, type AssignmentNotificationMethod, AssignmentResource, type AssignmentVerificationMethod, AssinafyClient, type AssinafyClientOptions, AssinafyError, AuthenticationResource, type ClientConfigInput, DEFAULT_WEBHOOK_EVENTS, type DocumentArtifactName, DocumentResource, type DocumentStatsGranularity, type DocumentStatus, type DocumentUploadSource, FieldsResource, type IAccountTheme, type IApiKeyResponse, type IAssignment, type IAssignmentCostSigner, type IAssignmentEntry, type IAssignmentItem, type IAssignmentListParams, type IAssignmentListResponse, type IAssignmentSigner, type IAuthenticatedUser, type IConfirmSignerDataPayload, type ICostEstimate, type ICreateAssignmentPayload, type ICreateAssignmentResponse, type ICreateDocumentFromTemplateOptions, type ICreateFieldPayload, type ICreateSignerPayload, type ICreateSignerResponse, type ICreateTagPayload, type ICreateWorkspacePayload, type IDeleteTagResponse, type IDetachDocumentTagResponse, type IDisplaySettings, type IDocumentActivity, type IDocumentDetailsResponse, type IDocumentListItem, type IDocumentListParams, type IDocumentListResponse, type IDocumentSearchParams, type IDocumentStatsParams, type IDocumentStatsRow, type IDocumentStatusInfo, type IDocumentUploadOptions, type IDocumentUploadResponse, type IDocumentVerification, type IEstimateAssignmentCostPayload, type IFieldDefinition, type IFieldType, type IFieldValidateMultipleEntry, type IFieldValidationMultipleResult, type IFieldValidationResponse, type IFieldValidationResult, type IInlineTag, type ILegacyConfirmSignerDataPayload, type ILegacyResendCostEstimate, type ILegacyUploadSignatureOptions, type IListParams, type ILoginResponse, type IMaskedApiKeyResponse, type INotificationHistoryEntry, type INotificationPreferences, type IPage, type IPaginatedResponse, type IPublicDocumentInfo, type IRenameDocumentResponse, type IResendCostEstimate, type IResendEmailResponse, type ISignFieldEntry, type ISigner, type ISignerListParams, type ISignerListResponse, type ISignerSelf, type ISigningProgress, type ITag, type ITemplateCostSigner, type ITemplateDetailsResponse, type ITemplateFieldPlacement, type ITemplateListItem, type ITemplateListParams, type ITemplateListResponse, type ITemplateRole, type ITemplateSigner, type IUpdateFieldPayload, type IUpdateNotificationPreferences, type IUpdateSignerPayload, type IUpdateTagPayload, type IUpdateTemplatePayload, type IUpdateWorkspacePayload, type IUploadAndRequestSignaturesResult, type IUploadAndRequestSignaturesSigner, type IUploadSignatureOptions, type IWebhookDispatch, type IWebhookDispatchListParams, type IWebhookEventTypeInfo, type IWebhookPayload, type IWebhookRegisterPayload, type IWebhookSubscription, type IWhatsAppNotification, type IWorkspaceListItem, type IWorkspaceListResponse, type IWorkspaceResponse, type Logger, MAX_LIST_PAGE_SIZE, MAX_UPLOAD_BYTES, NetworkError, type NotificationSenderType, type PaginatedResult, type PaginationMeta, SDK_USER_AGENT, type SendTokenChannel, type SignatureImageType, SignerDocumentsResource, type SignerReference, SignerResource, TagResource, TemplateResource, UserResource, ValidationError, type WebhookEventType, WebhookResource, WebhookVerifier, WorkspaceResource, buildAssignmentPayload };
|
package/dist/index.js
CHANGED
|
@@ -38,6 +38,7 @@ __export(index_exports, {
|
|
|
38
38
|
DEFAULT_WEBHOOK_EVENTS: () => DEFAULT_WEBHOOK_EVENTS,
|
|
39
39
|
DocumentResource: () => DocumentResource,
|
|
40
40
|
FieldsResource: () => FieldsResource,
|
|
41
|
+
MAX_LIST_PAGE_SIZE: () => MAX_LIST_PAGE_SIZE,
|
|
41
42
|
MAX_UPLOAD_BYTES: () => MAX_UPLOAD_BYTES,
|
|
42
43
|
NetworkError: () => NetworkError,
|
|
43
44
|
SDK_USER_AGENT: () => SDK_USER_AGENT,
|
|
@@ -59,6 +60,12 @@ var import_axios3 = __toESM(require("axios"));
|
|
|
59
60
|
var import_promises = require("timers/promises");
|
|
60
61
|
|
|
61
62
|
// src/errors.ts
|
|
63
|
+
var FALLBACK_MESSAGE = "API request failed";
|
|
64
|
+
var MAX_MESSAGE_LENGTH = 500;
|
|
65
|
+
function summarize(text) {
|
|
66
|
+
const collapsed = text.replaceAll(/\s+/gu, " ");
|
|
67
|
+
return collapsed.length > MAX_MESSAGE_LENGTH ? `${collapsed.slice(0, MAX_MESSAGE_LENGTH)}\u2026` : collapsed;
|
|
68
|
+
}
|
|
62
69
|
var AssinafyError = class extends Error {
|
|
63
70
|
context;
|
|
64
71
|
/**
|
|
@@ -100,9 +107,14 @@ var ApiError = class _ApiError extends AssinafyError {
|
|
|
100
107
|
* Convert a status/body pair into an {@link ApiError}.
|
|
101
108
|
*
|
|
102
109
|
* @param statusCode - Non-success HTTP response status.
|
|
103
|
-
* @param responseData -
|
|
104
|
-
* followed by string `error
|
|
105
|
-
*
|
|
110
|
+
* @param responseData - API body. For a JSON object, string `message` takes
|
|
111
|
+
* priority, followed by string `error`. A non-JSON body (a proxy's
|
|
112
|
+
* `text/plain` or HTML error page) is used verbatim rather than discarded —
|
|
113
|
+
* otherwise the only failures reported as the generic fallback would be the
|
|
114
|
+
* ones with no structured body to explain them. Anything else falls back to
|
|
115
|
+
* the stable message.
|
|
116
|
+
* @returns An `ApiError` retaining the original response body in
|
|
117
|
+
* {@link ApiError.responseData}; `message` is truncated for legibility.
|
|
106
118
|
*
|
|
107
119
|
* @example
|
|
108
120
|
* ```ts
|
|
@@ -111,10 +123,14 @@ var ApiError = class _ApiError extends AssinafyError {
|
|
|
111
123
|
* ```
|
|
112
124
|
*/
|
|
113
125
|
static fromResponse(statusCode, responseData) {
|
|
126
|
+
if (typeof responseData === "string") {
|
|
127
|
+
const text = responseData.trim();
|
|
128
|
+
return new _ApiError(text ? summarize(text) : FALLBACK_MESSAGE, statusCode, responseData);
|
|
129
|
+
}
|
|
114
130
|
const data = responseData ?? {};
|
|
115
131
|
const rawMessage = data["message"];
|
|
116
132
|
const rawError = data["error"];
|
|
117
|
-
const message = typeof rawMessage === "string" && rawMessage.length > 0 ? rawMessage : typeof rawError === "string" ? rawError :
|
|
133
|
+
const message = typeof rawMessage === "string" && rawMessage.length > 0 ? rawMessage : typeof rawError === "string" ? rawError : FALLBACK_MESSAGE;
|
|
118
134
|
return new _ApiError(message, statusCode, responseData);
|
|
119
135
|
}
|
|
120
136
|
};
|
|
@@ -189,7 +205,7 @@ function decodeBinaryErrorBody(data) {
|
|
|
189
205
|
try {
|
|
190
206
|
return JSON.parse(text);
|
|
191
207
|
} catch {
|
|
192
|
-
return text.length > 0 ?
|
|
208
|
+
return text.length > 0 ? text : null;
|
|
193
209
|
}
|
|
194
210
|
}
|
|
195
211
|
function toSdkError(error, fallbackMessage) {
|
|
@@ -270,6 +286,18 @@ function sanitiseNetworkCause(error) {
|
|
|
270
286
|
function redactSensitiveErrorText(message) {
|
|
271
287
|
return message.replace(SENSITIVE_ERROR_VALUE_RE, "$1[REDACTED]").replace(/(https?:\/\/)[^/@\s]+:[^/@\s]+@/gi, "$1[REDACTED]@");
|
|
272
288
|
}
|
|
289
|
+
function isEmail(value) {
|
|
290
|
+
return typeof value === "string" && /^[^\s@]+@[^\s@]+\.[^\s@]+$/u.test(value);
|
|
291
|
+
}
|
|
292
|
+
function assertEmail(value, label = "email") {
|
|
293
|
+
if (!isEmail(value)) {
|
|
294
|
+
throw new ValidationError(`${label} must be a valid email address`, { [label]: value });
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
function isE164PhoneNumber(value) {
|
|
298
|
+
return typeof value === "string" && /^\+[1-9]\d{1,14}$/u.test(value);
|
|
299
|
+
}
|
|
300
|
+
var MAX_LIST_PAGE_SIZE = 50;
|
|
273
301
|
function assertRecord(value, label) {
|
|
274
302
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
275
303
|
throw new ValidationError(`${label} must be an object`);
|
|
@@ -505,7 +533,7 @@ var import_axios2 = __toESM(require("axios"));
|
|
|
505
533
|
// package.json
|
|
506
534
|
var package_default = {
|
|
507
535
|
name: "@assinafy/sdk",
|
|
508
|
-
version: "2.
|
|
536
|
+
version: "2.2.0",
|
|
509
537
|
packageManager: "bun@1.4.0",
|
|
510
538
|
description: "TypeScript SDK for Assinafy API - Digital signature platform",
|
|
511
539
|
type: "commonjs",
|
|
@@ -1554,7 +1582,6 @@ var FAILED_STATUSES = /* @__PURE__ */ new Set([
|
|
|
1554
1582
|
"rejected_by_user",
|
|
1555
1583
|
"expired"
|
|
1556
1584
|
]);
|
|
1557
|
-
var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
1558
1585
|
var DocumentResource = class extends BaseResource {
|
|
1559
1586
|
publicHttp;
|
|
1560
1587
|
constructor(http, defaultAccountId, logger, publicHttp) {
|
|
@@ -1639,7 +1666,8 @@ var DocumentResource = class extends BaseResource {
|
|
|
1639
1666
|
* @param params - Filters and pagination: `status`; `method` (`virtual` or
|
|
1640
1667
|
* `collect`); `tags` (comma-separated IDs, all of which must match);
|
|
1641
1668
|
* `search` (document name, signer name, or signer email); `sort` (`name` or
|
|
1642
|
-
* `updated_at`); `page`; and `per-page` (
|
|
1669
|
+
* `updated_at`); `page`; and `per-page` (the server clamps this to 50
|
|
1670
|
+
* rather than rejecting a larger value).
|
|
1643
1671
|
* @param accountId - Override the client's default account ID.
|
|
1644
1672
|
* @returns Matching documents, with pagination in `meta`. Each item:
|
|
1645
1673
|
* ```jsonc
|
|
@@ -2542,7 +2570,7 @@ var DocumentResource = class extends BaseResource {
|
|
|
2542
2570
|
const id = this.requireId(documentId, "Document ID");
|
|
2543
2571
|
assertNonEmptyString(recipient, "recipient");
|
|
2544
2572
|
if (channel !== void 0) assertNonEmptyString(channel, "channel");
|
|
2545
|
-
if (channel === void 0 && !
|
|
2573
|
+
if (channel === void 0 && !isEmail(recipient)) {
|
|
2546
2574
|
throw new ValidationError("recipient must be a valid email address");
|
|
2547
2575
|
}
|
|
2548
2576
|
const path2 = `/public/documents/${this.pathSegment(id, "Document ID")}/send-token`;
|
|
@@ -2729,7 +2757,6 @@ function isLegacySendTokenValidation(error) {
|
|
|
2729
2757
|
}
|
|
2730
2758
|
|
|
2731
2759
|
// src/resources/signers.ts
|
|
2732
|
-
var EMAIL_RE2 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
2733
2760
|
var SignerResource = class extends BaseResource {
|
|
2734
2761
|
/**
|
|
2735
2762
|
* Create a signer in the workspace (`POST /accounts/{accountId}/signers`).
|
|
@@ -2855,7 +2882,8 @@ var SignerResource = class extends BaseResource {
|
|
|
2855
2882
|
* Pagination info (if any) is attached in `meta`.
|
|
2856
2883
|
*
|
|
2857
2884
|
* @param params - `page`, `per-page`, and `search` (matches `full_name` or
|
|
2858
|
-
* `email`). The
|
|
2885
|
+
* `email`). The server clamps `per-page` to {@link MAX_LIST_PAGE_SIZE}
|
|
2886
|
+
* (50); a larger value is not rejected, it is silently reduced.
|
|
2859
2887
|
* @param accountId - Override the client's default account ID.
|
|
2860
2888
|
* @returns The matching signers, with pagination in `meta`. Each item:
|
|
2861
2889
|
* ```jsonc
|
|
@@ -2965,10 +2993,10 @@ var SignerResource = class extends BaseResource {
|
|
|
2965
2993
|
* `search` is a substring match across signer fields, so the result is
|
|
2966
2994
|
* re-filtered here for an exact, case-insensitive email match.
|
|
2967
2995
|
*
|
|
2968
|
-
* Page size is pinned to the
|
|
2969
|
-
* An exact address realistically matches one
|
|
2970
|
-
* matched more than
|
|
2971
|
-
* exact-email filter to rule that out.
|
|
2996
|
+
* Page size is pinned to {@link MAX_LIST_PAGE_SIZE}, the largest page the
|
|
2997
|
+
* server actually returns. An exact address realistically matches one
|
|
2998
|
+
* signer, but a search term that matched more than that could in principle
|
|
2999
|
+
* miss one — the API exposes no exact-email filter to rule that out.
|
|
2972
3000
|
*
|
|
2973
3001
|
* A `404` from the underlying list is treated as "no match" and mapped to
|
|
2974
3002
|
* `null`; any other {@link ApiError} propagates.
|
|
@@ -2998,7 +3026,10 @@ var SignerResource = class extends BaseResource {
|
|
|
2998
3026
|
async findByEmail(email, accountId) {
|
|
2999
3027
|
this.assertEmail(email);
|
|
3000
3028
|
try {
|
|
3001
|
-
const { data } = await this.list(
|
|
3029
|
+
const { data } = await this.list(
|
|
3030
|
+
{ search: email, "per-page": MAX_LIST_PAGE_SIZE },
|
|
3031
|
+
accountId
|
|
3032
|
+
);
|
|
3002
3033
|
const lower = email.toLowerCase();
|
|
3003
3034
|
return data.find((s) => (s.email ?? "").toLowerCase() === lower) ?? null;
|
|
3004
3035
|
} catch (err) {
|
|
@@ -3009,7 +3040,7 @@ var SignerResource = class extends BaseResource {
|
|
|
3009
3040
|
}
|
|
3010
3041
|
}
|
|
3011
3042
|
assertEmail(email) {
|
|
3012
|
-
if (!
|
|
3043
|
+
if (!isEmail(email)) {
|
|
3013
3044
|
throw new ValidationError("Invalid email address", { email });
|
|
3014
3045
|
}
|
|
3015
3046
|
}
|
|
@@ -3022,7 +3053,7 @@ function validateCreateSignerPayload(payload) {
|
|
|
3022
3053
|
throw new ValidationError("full_name is required");
|
|
3023
3054
|
}
|
|
3024
3055
|
const phone = payload.whatsapp_phone_number ?? payload.phone;
|
|
3025
|
-
if (payload.email !== void 0 &&
|
|
3056
|
+
if (payload.email !== void 0 && !isEmail(payload.email)) {
|
|
3026
3057
|
throw new ValidationError("Invalid email address", { email: payload.email });
|
|
3027
3058
|
}
|
|
3028
3059
|
validateOptionalPhone(phone);
|
|
@@ -3047,7 +3078,7 @@ function validateUpdateSignerPayload(payload) {
|
|
|
3047
3078
|
if (payload.full_name !== void 0 && (typeof payload.full_name !== "string" || !payload.full_name.trim())) {
|
|
3048
3079
|
throw new ValidationError("full_name cannot be empty");
|
|
3049
3080
|
}
|
|
3050
|
-
if (payload.email !== void 0 &&
|
|
3081
|
+
if (payload.email !== void 0 && !isEmail(payload.email)) {
|
|
3051
3082
|
throw new ValidationError("Invalid email address", { email: payload.email });
|
|
3052
3083
|
}
|
|
3053
3084
|
const phone = payload.whatsapp_phone_number ?? payload.phone;
|
|
@@ -3063,7 +3094,7 @@ function validateOptionalDigits(value, field) {
|
|
|
3063
3094
|
}
|
|
3064
3095
|
function validateOptionalPhone(value) {
|
|
3065
3096
|
if (value === void 0) return;
|
|
3066
|
-
if (
|
|
3097
|
+
if (!isE164PhoneNumber(value)) {
|
|
3067
3098
|
throw new ValidationError("whatsapp_phone_number must use E.164 format");
|
|
3068
3099
|
}
|
|
3069
3100
|
}
|
|
@@ -3508,7 +3539,6 @@ var DEFAULT_WEBHOOK_EVENTS = Object.freeze([
|
|
|
3508
3539
|
"signer_rejected_document",
|
|
3509
3540
|
"document_processing_failed"
|
|
3510
3541
|
]);
|
|
3511
|
-
var EMAIL_RE3 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
3512
3542
|
var WebhookResource = class extends BaseResource {
|
|
3513
3543
|
/**
|
|
3514
3544
|
* Register (or replace) the workspace's single webhook subscription
|
|
@@ -3561,7 +3591,7 @@ var WebhookResource = class extends BaseResource {
|
|
|
3561
3591
|
throw new ValidationError("Webhook subscription payload is required");
|
|
3562
3592
|
}
|
|
3563
3593
|
validateWebhookUrl(payload.url);
|
|
3564
|
-
if (!
|
|
3594
|
+
if (!isEmail(payload.email)) {
|
|
3565
3595
|
throw new ValidationError("Webhook email must be a valid email address", {
|
|
3566
3596
|
email: payload.email
|
|
3567
3597
|
});
|
|
@@ -4364,7 +4394,6 @@ function validateTagColor(value) {
|
|
|
4364
4394
|
}
|
|
4365
4395
|
|
|
4366
4396
|
// src/resources/authentication.ts
|
|
4367
|
-
var EMAIL_RE4 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
4368
4397
|
var AuthenticationResource = class extends BaseResource {
|
|
4369
4398
|
publicHttp;
|
|
4370
4399
|
constructor(http, defaultAccountId, logger, publicHttp) {
|
|
@@ -4807,11 +4836,6 @@ var AuthenticationResource = class extends BaseResource {
|
|
|
4807
4836
|
return url.toString();
|
|
4808
4837
|
}
|
|
4809
4838
|
};
|
|
4810
|
-
function assertEmail(value) {
|
|
4811
|
-
if (typeof value !== "string" || !EMAIL_RE4.test(value)) {
|
|
4812
|
-
throw new ValidationError("email must be a valid email address");
|
|
4813
|
-
}
|
|
4814
|
-
}
|
|
4815
4839
|
|
|
4816
4840
|
// src/resources/fields.ts
|
|
4817
4841
|
var FieldsResource = class extends BaseResource {
|
|
@@ -5190,8 +5214,6 @@ function validateSignerAccessCode(value) {
|
|
|
5190
5214
|
}
|
|
5191
5215
|
|
|
5192
5216
|
// src/resources/signer-documents.ts
|
|
5193
|
-
var EMAIL_RE5 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
5194
|
-
var E164_RE = /^\+[1-9]\d{1,14}$/u;
|
|
5195
5217
|
var SignerDocumentsResource = class extends BaseResource {
|
|
5196
5218
|
constructor(http, defaultAccountId, logger, publicHttp) {
|
|
5197
5219
|
super(withoutCredentials(publicHttp ?? http), defaultAccountId, logger);
|
|
@@ -5848,10 +5870,10 @@ function validateConfirmDataPayload(payload) {
|
|
|
5848
5870
|
throw new ValidationError(`${key} must be a string`);
|
|
5849
5871
|
}
|
|
5850
5872
|
}
|
|
5851
|
-
if (payload.email !== void 0 &&
|
|
5873
|
+
if (payload.email !== void 0 && !isEmail(payload.email)) {
|
|
5852
5874
|
throw new ValidationError("email must be a valid email address");
|
|
5853
5875
|
}
|
|
5854
|
-
if (payload.whatsapp_phone_number !== void 0 &&
|
|
5876
|
+
if (payload.whatsapp_phone_number !== void 0 && !isE164PhoneNumber(payload.whatsapp_phone_number)) {
|
|
5855
5877
|
throw new ValidationError("whatsapp_phone_number must use E.164 format");
|
|
5856
5878
|
}
|
|
5857
5879
|
if (payload.has_accepted_terms !== void 0 && typeof payload.has_accepted_terms !== "boolean") {
|
|
@@ -6617,11 +6639,20 @@ function normaliseBaseUrl(raw) {
|
|
|
6617
6639
|
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
6618
6640
|
throw new ValidationError("baseUrl must use http or https");
|
|
6619
6641
|
}
|
|
6642
|
+
if (url.protocol === "http:" && !isLoopbackHost(url.hostname)) {
|
|
6643
|
+
throw new ValidationError(
|
|
6644
|
+
"baseUrl must use https for a remote host; http is only allowed for localhost"
|
|
6645
|
+
);
|
|
6646
|
+
}
|
|
6620
6647
|
if (url.username || url.password || raw.includes("?") || raw.includes("#")) {
|
|
6621
6648
|
throw new ValidationError("baseUrl must not contain credentials, a query, or a fragment");
|
|
6622
6649
|
}
|
|
6623
6650
|
return url.href.replace(/\/+$/, "");
|
|
6624
6651
|
}
|
|
6652
|
+
function isLoopbackHost(hostname) {
|
|
6653
|
+
const host = hostname.replace(/^\[|\]$/gu, "");
|
|
6654
|
+
return host === "localhost" || host === "::1" || /^127(?:\.\d{1,3}){3}$/u.test(host);
|
|
6655
|
+
}
|
|
6625
6656
|
function installCredentialOriginGuard(http, baseURL) {
|
|
6626
6657
|
const allowedOrigin = new URL(baseURL).origin;
|
|
6627
6658
|
http.interceptors.request.use((config) => {
|
|
@@ -6696,6 +6727,7 @@ function hasIdempotencyKey(headers) {
|
|
|
6696
6727
|
DEFAULT_WEBHOOK_EVENTS,
|
|
6697
6728
|
DocumentResource,
|
|
6698
6729
|
FieldsResource,
|
|
6730
|
+
MAX_LIST_PAGE_SIZE,
|
|
6699
6731
|
MAX_UPLOAD_BYTES,
|
|
6700
6732
|
NetworkError,
|
|
6701
6733
|
SDK_USER_AGENT,
|
package/dist/index.mjs
CHANGED
|
@@ -3,6 +3,12 @@ import axios3 from "axios";
|
|
|
3
3
|
import { setTimeout as delay } from "timers/promises";
|
|
4
4
|
|
|
5
5
|
// src/errors.ts
|
|
6
|
+
var FALLBACK_MESSAGE = "API request failed";
|
|
7
|
+
var MAX_MESSAGE_LENGTH = 500;
|
|
8
|
+
function summarize(text) {
|
|
9
|
+
const collapsed = text.replaceAll(/\s+/gu, " ");
|
|
10
|
+
return collapsed.length > MAX_MESSAGE_LENGTH ? `${collapsed.slice(0, MAX_MESSAGE_LENGTH)}\u2026` : collapsed;
|
|
11
|
+
}
|
|
6
12
|
var AssinafyError = class extends Error {
|
|
7
13
|
context;
|
|
8
14
|
/**
|
|
@@ -44,9 +50,14 @@ var ApiError = class _ApiError extends AssinafyError {
|
|
|
44
50
|
* Convert a status/body pair into an {@link ApiError}.
|
|
45
51
|
*
|
|
46
52
|
* @param statusCode - Non-success HTTP response status.
|
|
47
|
-
* @param responseData -
|
|
48
|
-
* followed by string `error
|
|
49
|
-
*
|
|
53
|
+
* @param responseData - API body. For a JSON object, string `message` takes
|
|
54
|
+
* priority, followed by string `error`. A non-JSON body (a proxy's
|
|
55
|
+
* `text/plain` or HTML error page) is used verbatim rather than discarded —
|
|
56
|
+
* otherwise the only failures reported as the generic fallback would be the
|
|
57
|
+
* ones with no structured body to explain them. Anything else falls back to
|
|
58
|
+
* the stable message.
|
|
59
|
+
* @returns An `ApiError` retaining the original response body in
|
|
60
|
+
* {@link ApiError.responseData}; `message` is truncated for legibility.
|
|
50
61
|
*
|
|
51
62
|
* @example
|
|
52
63
|
* ```ts
|
|
@@ -55,10 +66,14 @@ var ApiError = class _ApiError extends AssinafyError {
|
|
|
55
66
|
* ```
|
|
56
67
|
*/
|
|
57
68
|
static fromResponse(statusCode, responseData) {
|
|
69
|
+
if (typeof responseData === "string") {
|
|
70
|
+
const text = responseData.trim();
|
|
71
|
+
return new _ApiError(text ? summarize(text) : FALLBACK_MESSAGE, statusCode, responseData);
|
|
72
|
+
}
|
|
58
73
|
const data = responseData ?? {};
|
|
59
74
|
const rawMessage = data["message"];
|
|
60
75
|
const rawError = data["error"];
|
|
61
|
-
const message = typeof rawMessage === "string" && rawMessage.length > 0 ? rawMessage : typeof rawError === "string" ? rawError :
|
|
76
|
+
const message = typeof rawMessage === "string" && rawMessage.length > 0 ? rawMessage : typeof rawError === "string" ? rawError : FALLBACK_MESSAGE;
|
|
62
77
|
return new _ApiError(message, statusCode, responseData);
|
|
63
78
|
}
|
|
64
79
|
};
|
|
@@ -133,7 +148,7 @@ function decodeBinaryErrorBody(data) {
|
|
|
133
148
|
try {
|
|
134
149
|
return JSON.parse(text);
|
|
135
150
|
} catch {
|
|
136
|
-
return text.length > 0 ?
|
|
151
|
+
return text.length > 0 ? text : null;
|
|
137
152
|
}
|
|
138
153
|
}
|
|
139
154
|
function toSdkError(error, fallbackMessage) {
|
|
@@ -214,6 +229,18 @@ function sanitiseNetworkCause(error) {
|
|
|
214
229
|
function redactSensitiveErrorText(message) {
|
|
215
230
|
return message.replace(SENSITIVE_ERROR_VALUE_RE, "$1[REDACTED]").replace(/(https?:\/\/)[^/@\s]+:[^/@\s]+@/gi, "$1[REDACTED]@");
|
|
216
231
|
}
|
|
232
|
+
function isEmail(value) {
|
|
233
|
+
return typeof value === "string" && /^[^\s@]+@[^\s@]+\.[^\s@]+$/u.test(value);
|
|
234
|
+
}
|
|
235
|
+
function assertEmail(value, label = "email") {
|
|
236
|
+
if (!isEmail(value)) {
|
|
237
|
+
throw new ValidationError(`${label} must be a valid email address`, { [label]: value });
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
function isE164PhoneNumber(value) {
|
|
241
|
+
return typeof value === "string" && /^\+[1-9]\d{1,14}$/u.test(value);
|
|
242
|
+
}
|
|
243
|
+
var MAX_LIST_PAGE_SIZE = 50;
|
|
217
244
|
function assertRecord(value, label) {
|
|
218
245
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
219
246
|
throw new ValidationError(`${label} must be an object`);
|
|
@@ -449,7 +476,7 @@ import axios2 from "axios";
|
|
|
449
476
|
// package.json
|
|
450
477
|
var package_default = {
|
|
451
478
|
name: "@assinafy/sdk",
|
|
452
|
-
version: "2.
|
|
479
|
+
version: "2.2.0",
|
|
453
480
|
packageManager: "bun@1.4.0",
|
|
454
481
|
description: "TypeScript SDK for Assinafy API - Digital signature platform",
|
|
455
482
|
type: "commonjs",
|
|
@@ -1498,7 +1525,6 @@ var FAILED_STATUSES = /* @__PURE__ */ new Set([
|
|
|
1498
1525
|
"rejected_by_user",
|
|
1499
1526
|
"expired"
|
|
1500
1527
|
]);
|
|
1501
|
-
var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
1502
1528
|
var DocumentResource = class extends BaseResource {
|
|
1503
1529
|
publicHttp;
|
|
1504
1530
|
constructor(http, defaultAccountId, logger, publicHttp) {
|
|
@@ -1583,7 +1609,8 @@ var DocumentResource = class extends BaseResource {
|
|
|
1583
1609
|
* @param params - Filters and pagination: `status`; `method` (`virtual` or
|
|
1584
1610
|
* `collect`); `tags` (comma-separated IDs, all of which must match);
|
|
1585
1611
|
* `search` (document name, signer name, or signer email); `sort` (`name` or
|
|
1586
|
-
* `updated_at`); `page`; and `per-page` (
|
|
1612
|
+
* `updated_at`); `page`; and `per-page` (the server clamps this to 50
|
|
1613
|
+
* rather than rejecting a larger value).
|
|
1587
1614
|
* @param accountId - Override the client's default account ID.
|
|
1588
1615
|
* @returns Matching documents, with pagination in `meta`. Each item:
|
|
1589
1616
|
* ```jsonc
|
|
@@ -2486,7 +2513,7 @@ var DocumentResource = class extends BaseResource {
|
|
|
2486
2513
|
const id = this.requireId(documentId, "Document ID");
|
|
2487
2514
|
assertNonEmptyString(recipient, "recipient");
|
|
2488
2515
|
if (channel !== void 0) assertNonEmptyString(channel, "channel");
|
|
2489
|
-
if (channel === void 0 && !
|
|
2516
|
+
if (channel === void 0 && !isEmail(recipient)) {
|
|
2490
2517
|
throw new ValidationError("recipient must be a valid email address");
|
|
2491
2518
|
}
|
|
2492
2519
|
const path2 = `/public/documents/${this.pathSegment(id, "Document ID")}/send-token`;
|
|
@@ -2673,7 +2700,6 @@ function isLegacySendTokenValidation(error) {
|
|
|
2673
2700
|
}
|
|
2674
2701
|
|
|
2675
2702
|
// src/resources/signers.ts
|
|
2676
|
-
var EMAIL_RE2 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
2677
2703
|
var SignerResource = class extends BaseResource {
|
|
2678
2704
|
/**
|
|
2679
2705
|
* Create a signer in the workspace (`POST /accounts/{accountId}/signers`).
|
|
@@ -2799,7 +2825,8 @@ var SignerResource = class extends BaseResource {
|
|
|
2799
2825
|
* Pagination info (if any) is attached in `meta`.
|
|
2800
2826
|
*
|
|
2801
2827
|
* @param params - `page`, `per-page`, and `search` (matches `full_name` or
|
|
2802
|
-
* `email`). The
|
|
2828
|
+
* `email`). The server clamps `per-page` to {@link MAX_LIST_PAGE_SIZE}
|
|
2829
|
+
* (50); a larger value is not rejected, it is silently reduced.
|
|
2803
2830
|
* @param accountId - Override the client's default account ID.
|
|
2804
2831
|
* @returns The matching signers, with pagination in `meta`. Each item:
|
|
2805
2832
|
* ```jsonc
|
|
@@ -2909,10 +2936,10 @@ var SignerResource = class extends BaseResource {
|
|
|
2909
2936
|
* `search` is a substring match across signer fields, so the result is
|
|
2910
2937
|
* re-filtered here for an exact, case-insensitive email match.
|
|
2911
2938
|
*
|
|
2912
|
-
* Page size is pinned to the
|
|
2913
|
-
* An exact address realistically matches one
|
|
2914
|
-
* matched more than
|
|
2915
|
-
* exact-email filter to rule that out.
|
|
2939
|
+
* Page size is pinned to {@link MAX_LIST_PAGE_SIZE}, the largest page the
|
|
2940
|
+
* server actually returns. An exact address realistically matches one
|
|
2941
|
+
* signer, but a search term that matched more than that could in principle
|
|
2942
|
+
* miss one — the API exposes no exact-email filter to rule that out.
|
|
2916
2943
|
*
|
|
2917
2944
|
* A `404` from the underlying list is treated as "no match" and mapped to
|
|
2918
2945
|
* `null`; any other {@link ApiError} propagates.
|
|
@@ -2942,7 +2969,10 @@ var SignerResource = class extends BaseResource {
|
|
|
2942
2969
|
async findByEmail(email, accountId) {
|
|
2943
2970
|
this.assertEmail(email);
|
|
2944
2971
|
try {
|
|
2945
|
-
const { data } = await this.list(
|
|
2972
|
+
const { data } = await this.list(
|
|
2973
|
+
{ search: email, "per-page": MAX_LIST_PAGE_SIZE },
|
|
2974
|
+
accountId
|
|
2975
|
+
);
|
|
2946
2976
|
const lower = email.toLowerCase();
|
|
2947
2977
|
return data.find((s) => (s.email ?? "").toLowerCase() === lower) ?? null;
|
|
2948
2978
|
} catch (err) {
|
|
@@ -2953,7 +2983,7 @@ var SignerResource = class extends BaseResource {
|
|
|
2953
2983
|
}
|
|
2954
2984
|
}
|
|
2955
2985
|
assertEmail(email) {
|
|
2956
|
-
if (!
|
|
2986
|
+
if (!isEmail(email)) {
|
|
2957
2987
|
throw new ValidationError("Invalid email address", { email });
|
|
2958
2988
|
}
|
|
2959
2989
|
}
|
|
@@ -2966,7 +2996,7 @@ function validateCreateSignerPayload(payload) {
|
|
|
2966
2996
|
throw new ValidationError("full_name is required");
|
|
2967
2997
|
}
|
|
2968
2998
|
const phone = payload.whatsapp_phone_number ?? payload.phone;
|
|
2969
|
-
if (payload.email !== void 0 &&
|
|
2999
|
+
if (payload.email !== void 0 && !isEmail(payload.email)) {
|
|
2970
3000
|
throw new ValidationError("Invalid email address", { email: payload.email });
|
|
2971
3001
|
}
|
|
2972
3002
|
validateOptionalPhone(phone);
|
|
@@ -2991,7 +3021,7 @@ function validateUpdateSignerPayload(payload) {
|
|
|
2991
3021
|
if (payload.full_name !== void 0 && (typeof payload.full_name !== "string" || !payload.full_name.trim())) {
|
|
2992
3022
|
throw new ValidationError("full_name cannot be empty");
|
|
2993
3023
|
}
|
|
2994
|
-
if (payload.email !== void 0 &&
|
|
3024
|
+
if (payload.email !== void 0 && !isEmail(payload.email)) {
|
|
2995
3025
|
throw new ValidationError("Invalid email address", { email: payload.email });
|
|
2996
3026
|
}
|
|
2997
3027
|
const phone = payload.whatsapp_phone_number ?? payload.phone;
|
|
@@ -3007,7 +3037,7 @@ function validateOptionalDigits(value, field) {
|
|
|
3007
3037
|
}
|
|
3008
3038
|
function validateOptionalPhone(value) {
|
|
3009
3039
|
if (value === void 0) return;
|
|
3010
|
-
if (
|
|
3040
|
+
if (!isE164PhoneNumber(value)) {
|
|
3011
3041
|
throw new ValidationError("whatsapp_phone_number must use E.164 format");
|
|
3012
3042
|
}
|
|
3013
3043
|
}
|
|
@@ -3452,7 +3482,6 @@ var DEFAULT_WEBHOOK_EVENTS = Object.freeze([
|
|
|
3452
3482
|
"signer_rejected_document",
|
|
3453
3483
|
"document_processing_failed"
|
|
3454
3484
|
]);
|
|
3455
|
-
var EMAIL_RE3 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
3456
3485
|
var WebhookResource = class extends BaseResource {
|
|
3457
3486
|
/**
|
|
3458
3487
|
* Register (or replace) the workspace's single webhook subscription
|
|
@@ -3505,7 +3534,7 @@ var WebhookResource = class extends BaseResource {
|
|
|
3505
3534
|
throw new ValidationError("Webhook subscription payload is required");
|
|
3506
3535
|
}
|
|
3507
3536
|
validateWebhookUrl(payload.url);
|
|
3508
|
-
if (!
|
|
3537
|
+
if (!isEmail(payload.email)) {
|
|
3509
3538
|
throw new ValidationError("Webhook email must be a valid email address", {
|
|
3510
3539
|
email: payload.email
|
|
3511
3540
|
});
|
|
@@ -4308,7 +4337,6 @@ function validateTagColor(value) {
|
|
|
4308
4337
|
}
|
|
4309
4338
|
|
|
4310
4339
|
// src/resources/authentication.ts
|
|
4311
|
-
var EMAIL_RE4 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
4312
4340
|
var AuthenticationResource = class extends BaseResource {
|
|
4313
4341
|
publicHttp;
|
|
4314
4342
|
constructor(http, defaultAccountId, logger, publicHttp) {
|
|
@@ -4751,11 +4779,6 @@ var AuthenticationResource = class extends BaseResource {
|
|
|
4751
4779
|
return url.toString();
|
|
4752
4780
|
}
|
|
4753
4781
|
};
|
|
4754
|
-
function assertEmail(value) {
|
|
4755
|
-
if (typeof value !== "string" || !EMAIL_RE4.test(value)) {
|
|
4756
|
-
throw new ValidationError("email must be a valid email address");
|
|
4757
|
-
}
|
|
4758
|
-
}
|
|
4759
4782
|
|
|
4760
4783
|
// src/resources/fields.ts
|
|
4761
4784
|
var FieldsResource = class extends BaseResource {
|
|
@@ -5134,8 +5157,6 @@ function validateSignerAccessCode(value) {
|
|
|
5134
5157
|
}
|
|
5135
5158
|
|
|
5136
5159
|
// src/resources/signer-documents.ts
|
|
5137
|
-
var EMAIL_RE5 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
5138
|
-
var E164_RE = /^\+[1-9]\d{1,14}$/u;
|
|
5139
5160
|
var SignerDocumentsResource = class extends BaseResource {
|
|
5140
5161
|
constructor(http, defaultAccountId, logger, publicHttp) {
|
|
5141
5162
|
super(withoutCredentials(publicHttp ?? http), defaultAccountId, logger);
|
|
@@ -5792,10 +5813,10 @@ function validateConfirmDataPayload(payload) {
|
|
|
5792
5813
|
throw new ValidationError(`${key} must be a string`);
|
|
5793
5814
|
}
|
|
5794
5815
|
}
|
|
5795
|
-
if (payload.email !== void 0 &&
|
|
5816
|
+
if (payload.email !== void 0 && !isEmail(payload.email)) {
|
|
5796
5817
|
throw new ValidationError("email must be a valid email address");
|
|
5797
5818
|
}
|
|
5798
|
-
if (payload.whatsapp_phone_number !== void 0 &&
|
|
5819
|
+
if (payload.whatsapp_phone_number !== void 0 && !isE164PhoneNumber(payload.whatsapp_phone_number)) {
|
|
5799
5820
|
throw new ValidationError("whatsapp_phone_number must use E.164 format");
|
|
5800
5821
|
}
|
|
5801
5822
|
if (payload.has_accepted_terms !== void 0 && typeof payload.has_accepted_terms !== "boolean") {
|
|
@@ -6561,11 +6582,20 @@ function normaliseBaseUrl(raw) {
|
|
|
6561
6582
|
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
6562
6583
|
throw new ValidationError("baseUrl must use http or https");
|
|
6563
6584
|
}
|
|
6585
|
+
if (url.protocol === "http:" && !isLoopbackHost(url.hostname)) {
|
|
6586
|
+
throw new ValidationError(
|
|
6587
|
+
"baseUrl must use https for a remote host; http is only allowed for localhost"
|
|
6588
|
+
);
|
|
6589
|
+
}
|
|
6564
6590
|
if (url.username || url.password || raw.includes("?") || raw.includes("#")) {
|
|
6565
6591
|
throw new ValidationError("baseUrl must not contain credentials, a query, or a fragment");
|
|
6566
6592
|
}
|
|
6567
6593
|
return url.href.replace(/\/+$/, "");
|
|
6568
6594
|
}
|
|
6595
|
+
function isLoopbackHost(hostname) {
|
|
6596
|
+
const host = hostname.replace(/^\[|\]$/gu, "");
|
|
6597
|
+
return host === "localhost" || host === "::1" || /^127(?:\.\d{1,3}){3}$/u.test(host);
|
|
6598
|
+
}
|
|
6569
6599
|
function installCredentialOriginGuard(http, baseURL) {
|
|
6570
6600
|
const allowedOrigin = new URL(baseURL).origin;
|
|
6571
6601
|
http.interceptors.request.use((config) => {
|
|
@@ -6639,6 +6669,7 @@ export {
|
|
|
6639
6669
|
DEFAULT_WEBHOOK_EVENTS,
|
|
6640
6670
|
DocumentResource,
|
|
6641
6671
|
FieldsResource,
|
|
6672
|
+
MAX_LIST_PAGE_SIZE,
|
|
6642
6673
|
MAX_UPLOAD_BYTES,
|
|
6643
6674
|
NetworkError,
|
|
6644
6675
|
SDK_USER_AGENT,
|
package/docs/COMPATIBILITY.md
CHANGED
|
@@ -58,6 +58,29 @@ method counters. Notification counters are not mutually exclusive.
|
|
|
58
58
|
are mutually exclusive and sum to `signature_requests`. Older unsuffixed email
|
|
59
59
|
and WhatsApp counters remain optional.
|
|
60
60
|
|
|
61
|
+
## List pagination
|
|
62
|
+
|
|
63
|
+
Two behaviours of the list endpoints are silent rather than loud, so neither
|
|
64
|
+
surfaces as an error a caller could react to:
|
|
65
|
+
|
|
66
|
+
- Only the hyphenated `per-page` is read. `per_page` is accepted and discarded,
|
|
67
|
+
and the response falls back to the default of 20 rows. The SDK normalizes
|
|
68
|
+
`per_page` to `per-page` on every list method, and an explicit `per-page`
|
|
69
|
+
wins if both are supplied.
|
|
70
|
+
- `per-page` is clamped to **50**. A request for 100 returns 50 rows with a
|
|
71
|
+
`200`, so page-size arithmetic based on the requested value is wrong by half.
|
|
72
|
+
That maximum is exported as `MAX_LIST_PAGE_SIZE`.
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
import { MAX_LIST_PAGE_SIZE } from '@assinafy/sdk';
|
|
76
|
+
|
|
77
|
+
const { data, meta } = await client.documents.list({ 'per-page': MAX_LIST_PAGE_SIZE });
|
|
78
|
+
// meta is read from the X-Pagination-* response headers, so it reports the
|
|
79
|
+
// page size the server actually applied — prefer it over the requested value.
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`signers.findByEmail()` pins this maximum for its single lookup request.
|
|
83
|
+
|
|
61
84
|
## Assignment list account context
|
|
62
85
|
|
|
63
86
|
`GET /v1/assignments` requires the workspace in the camel-case `accountId`
|