@happyvertical/signatures 0.80.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/AGENT.md +49 -0
- package/LICENSE +7 -0
- package/README.md +129 -0
- package/dist/adapters/boldsign.d.ts +48 -0
- package/dist/adapters/boldsign.d.ts.map +1 -0
- package/dist/adapters/boldsign.js +1443 -0
- package/dist/adapters/boldsign.js.map +1 -0
- package/dist/chunks/errors-Bnx7QrSA.js +59 -0
- package/dist/chunks/errors-Bnx7QrSA.js.map +1 -0
- package/dist/errors.d.ts +46 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/factory.d.ts +7 -0
- package/dist/factory.d.ts.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/shared.d.ts +12 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/types.d.ts +173 -0
- package/dist/types.d.ts.map +1 -0
- package/metadata.json +28 -0
- package/package.json +62 -0
package/AGENT.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# @happyvertical/signatures
|
|
2
|
+
|
|
3
|
+
<!-- BEGIN AGENT:GENERATED -->
|
|
4
|
+
## Purpose
|
|
5
|
+
Provider-neutral e-signature workflows with a BoldSign adapter
|
|
6
|
+
|
|
7
|
+
## Package Map
|
|
8
|
+
- Package: `@happyvertical/signatures`
|
|
9
|
+
- Hierarchy path: `@happyvertical/sdk > packages > signatures`
|
|
10
|
+
- Workspace position: `25 of 32` local packages
|
|
11
|
+
- Internal dependencies: none
|
|
12
|
+
- Internal dependents: none
|
|
13
|
+
- Knowledge graph files: `AGENT.md`, `metadata.json`, `ecosystem-manifest.json`
|
|
14
|
+
|
|
15
|
+
## Build & Test
|
|
16
|
+
```bash
|
|
17
|
+
pnpm --filter @happyvertical/signatures build
|
|
18
|
+
pnpm --filter @happyvertical/signatures test
|
|
19
|
+
pnpm --filter @happyvertical/signatures typecheck
|
|
20
|
+
pnpm --filter @happyvertical/signatures clean
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Agent Correction Loops
|
|
24
|
+
- If Vite or TypeScript reports missing packages, run `pnpm install` at the repo root and rerun `pnpm --filter @happyvertical/signatures build`.
|
|
25
|
+
- If tests or exports fail after API, type, or bundle changes, run `pnpm --filter @happyvertical/signatures clean` followed by `pnpm --filter @happyvertical/signatures build` and `pnpm --filter @happyvertical/signatures test`.
|
|
26
|
+
- If failures span multiple packages or Turborepo ordering looks wrong, run `pnpm build` and `pnpm typecheck` from the repo root before retrying package-scoped commands.
|
|
27
|
+
|
|
28
|
+
## Ecosystem Relationships
|
|
29
|
+
- Provides: Provider-neutral e-signature workflows with a BoldSign adapter
|
|
30
|
+
- Implements: BoldSign
|
|
31
|
+
- Requires: none
|
|
32
|
+
- Stability: experimental (Marked as preview or experimental in package guidance.)
|
|
33
|
+
<!-- END AGENT:GENERATED -->
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
## Package Status
|
|
37
|
+
|
|
38
|
+
Experimental while the first downstream agreement workflow validates the provider-neutral contract.
|
|
39
|
+
|
|
40
|
+
## Adapters
|
|
41
|
+
|
|
42
|
+
- BoldSign (`type: 'boldsign'`) uses the regional v1 document API, HMAC-verified webhooks, and PDF artifact endpoints.
|
|
43
|
+
|
|
44
|
+
## Security Boundaries
|
|
45
|
+
|
|
46
|
+
- Construct one adapter per tenant credential and webhook secret set.
|
|
47
|
+
- Preserve the raw webhook request body until HMAC verification completes.
|
|
48
|
+
- Persist provider event IDs and creation idempotency keys under durable unique constraints; BoldSign does not advertise provider-enforced send idempotency.
|
|
49
|
+
- Download signed documents and audit trails only after the normalized request reaches `completed`, consume each single-use stream into immutable storage, then await and persist its SHA-256 digest.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright <2025> <Happy Vertical Corporation>
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# @happyvertical/signatures
|
|
2
|
+
|
|
3
|
+
Provider-neutral e-signature request, lifecycle, verified-webhook, and execution-evidence contracts. BoldSign is the first adapter.
|
|
4
|
+
|
|
5
|
+
## Status
|
|
6
|
+
|
|
7
|
+
This package is experimental while the first downstream agreement workflow validates the provider-neutral contract.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pnpm add @happyvertical/signatures
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## BoldSign quick start
|
|
16
|
+
|
|
17
|
+
Create one adapter per tenant credential and webhook signing secret. HappyVertical deployments use BoldSign's Canadian region by default.
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import { createSignatureProvider } from '@happyvertical/signatures';
|
|
21
|
+
|
|
22
|
+
const signatures = await createSignatureProvider({
|
|
23
|
+
type: 'boldsign',
|
|
24
|
+
tenantId: 'tenant_123',
|
|
25
|
+
apiKey: process.env.BOLDSIGN_API_KEY!,
|
|
26
|
+
webhookSecrets: [process.env.BOLDSIGN_WEBHOOK_SECRET!],
|
|
27
|
+
region: 'ca',
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const request = await signatures.createRequest({
|
|
31
|
+
tenantId: 'tenant_123',
|
|
32
|
+
idempotencyKey: 'referral-agreement:version_456',
|
|
33
|
+
title: 'Referral Agreement',
|
|
34
|
+
documents: [
|
|
35
|
+
{
|
|
36
|
+
name: 'referral-agreement.pdf',
|
|
37
|
+
mediaType: 'application/pdf',
|
|
38
|
+
data: generatedPdf,
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
signers: [
|
|
42
|
+
{
|
|
43
|
+
name: 'Alex Example',
|
|
44
|
+
email: 'alex@example.com',
|
|
45
|
+
authentication: { method: 'email_otp' },
|
|
46
|
+
fields: [
|
|
47
|
+
{
|
|
48
|
+
id: 'referrer_signature',
|
|
49
|
+
type: 'signature',
|
|
50
|
+
page: 3,
|
|
51
|
+
bounds: { x: 72, y: 620, width: 180, height: 36 },
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
expiresInDays: 30,
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Adapters
|
|
61
|
+
|
|
62
|
+
- BoldSign (`type: 'boldsign'`) uses the regional v1 document API, HMAC-verified webhooks, and PDF artifact endpoints.
|
|
63
|
+
|
|
64
|
+
The package sends JSON with base64 documents to BoldSign's asynchronous `POST /v1/document/send` API. A successful create response means BoldSign accepted processing; the verified `Sent` or `SendFailed` webhook is authoritative for the next transition.
|
|
65
|
+
|
|
66
|
+
## Webhooks and replay protection
|
|
67
|
+
|
|
68
|
+
Pass the exact raw UTF-8 request body and `X-BoldSign-Signature` header. Do not parse and re-serialize the body before verification.
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
const event = signatures.parseWebhook({
|
|
72
|
+
payload: rawBody,
|
|
73
|
+
signature: request.headers.get('x-boldsign-signature')!,
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The adapter verifies BoldSign's HMAC-SHA256 signature using constant-time comparison and rejects timestamps outside the configured tolerance (five minutes by default). It also requires the signed payload's `hvTenantId` metadata to match the adapter tenant. Persist `event.id` under a unique constraint before applying an event; the SDK intentionally does not pretend an in-memory replay cache is durable.
|
|
78
|
+
|
|
79
|
+
Configure the webhook for `Sent`, `Signed`, `Completed`, `Viewed`, `Declined`, `Revoked`, `Expired`, `DeliveryFailed`, and `SendFailed`. Other signed event types are rejected until the provider-neutral contract defines their semantics.
|
|
80
|
+
|
|
81
|
+
BoldSign sends an unsigned `Verification` callback while a webhook endpoint is configured. Acknowledge that control request before calling `parseWebhook`; do not treat it as a document event.
|
|
82
|
+
|
|
83
|
+
## Idempotency and ambiguous failures
|
|
84
|
+
|
|
85
|
+
`createRequest` requires an idempotency key and stores it in BoldSign metadata as `hvIdempotencyKey`. BoldSign's public send-document contract does not advertise an atomic idempotency header, so `capabilities.providerEnforcedIdempotency` is `false`. The durable application layer must claim the tenant/key before sending and persist the returned document ID. When `SignatureProviderError.requestMayHaveSucceeded` is true, reconcile rather than blindly sending a duplicate agreement.
|
|
86
|
+
|
|
87
|
+
## Execution evidence
|
|
88
|
+
|
|
89
|
+
Artifacts are available only after the normalized request status is `completed`:
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
const signed = await signatures.downloadArtifact({
|
|
93
|
+
tenantId: 'tenant_123',
|
|
94
|
+
requestId: request.id,
|
|
95
|
+
kind: 'signed_document',
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const audit = await signatures.downloadArtifact({
|
|
99
|
+
tenantId: 'tenant_123',
|
|
100
|
+
requestId: request.id,
|
|
101
|
+
kind: 'audit_trail',
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
await immutableAssets.put(signed.filename, signed.stream);
|
|
105
|
+
const signedSha256 = await signed.sha256;
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Each artifact exposes a single-use byte stream and a SHA-256 promise that resolves after that stream is consumed. Persist the stream to immutable/versioned storage, then await and record the digest together with the provider request ID, event ID, tenant, signer identity, retrieval time, and agreement version. The provider is a retrieval source, not the system of record for an executed agreement.
|
|
109
|
+
|
|
110
|
+
## Operations and security
|
|
111
|
+
|
|
112
|
+
- Keep API keys, OAuth tokens, access codes, and webhook secrets in the SDK/SMRT secret store; never put them in agreement metadata or logs.
|
|
113
|
+
- Scope an adapter instance to exactly one tenant. Reads, mutations, downloads, and webhooks all enforce that tenant binding.
|
|
114
|
+
- For secret rotation, configure both valid webhook secrets temporarily. BoldSign may also include both `s0` and `s1` signatures in its header.
|
|
115
|
+
- `cancelRequest` requires a reason. `extendExpiry` verifies tenant ownership first and only accepts a future date.
|
|
116
|
+
- API credentials may use either `X-API-KEY` or an OAuth bearer token, never both.
|
|
117
|
+
- Use the sandbox for contract testing, but do not treat its watermarked PDFs as legal artifacts.
|
|
118
|
+
|
|
119
|
+
## Provider references
|
|
120
|
+
|
|
121
|
+
- [Regional API base URLs](https://developers.boldsign.com/api-overview/versioning/)
|
|
122
|
+
- [Send document](https://developers.boldsign.com/documents/send-document/)
|
|
123
|
+
- [Document details and status](https://developers.boldsign.com/documents/document-details-and-status)
|
|
124
|
+
- [Revoke document](https://developers.boldsign.com/documents/revoke-document/)
|
|
125
|
+
- [Extend document expiry](https://developers.boldsign.com/documents/extend-document-expiry/)
|
|
126
|
+
- [Verify webhook events](https://developers.boldsign.com/webhooks/verify-webhook-events/)
|
|
127
|
+
- [Webhook event metadata](https://developers.boldsign.com/webhooks/event-metadata/)
|
|
128
|
+
- [Download signed document](https://developers.boldsign.com/documents/download-document/)
|
|
129
|
+
- [Download audit trail](https://developers.boldsign.com/documents/download-audit-trail/)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { SignatureFetch } from '../shared.js';
|
|
2
|
+
import { CancelSignatureRequestInput, CreateSignatureRequestInput, DownloadSignatureArtifactInput, ExtendSignatureRequestExpiryInput, ParseSignatureWebhookInput, SignatureArtifact, SignatureProvider, SignatureProviderCapabilities, SignatureRequest, SignatureRequestReference, SignatureWebhookEvent } from '../types.js';
|
|
3
|
+
export declare const BOLDSIGN_PROVIDER_ID = "boldsign";
|
|
4
|
+
export declare const BOLDSIGN_TENANT_METADATA_KEY = "hvTenantId";
|
|
5
|
+
export declare const BOLDSIGN_IDEMPOTENCY_METADATA_KEY = "hvIdempotencyKey";
|
|
6
|
+
export type BoldSignRegion = 'us' | 'eu' | 'ca' | 'au';
|
|
7
|
+
export interface BoldSignAdapterOptions {
|
|
8
|
+
/** Bind one adapter and its credential/webhook secret to one tenant. */
|
|
9
|
+
tenantId: string;
|
|
10
|
+
apiKey?: string;
|
|
11
|
+
accessToken?: string;
|
|
12
|
+
/** Defaults to Canada for HappyVertical's first deployment. */
|
|
13
|
+
region?: BoldSignRegion;
|
|
14
|
+
apiBaseUrl?: string;
|
|
15
|
+
webhookSecrets?: string | readonly string[];
|
|
16
|
+
webhookToleranceSeconds?: number;
|
|
17
|
+
fetch?: SignatureFetch;
|
|
18
|
+
/** Testable wall clock used for replay checks and evidence timestamps. */
|
|
19
|
+
now?: () => Date;
|
|
20
|
+
}
|
|
21
|
+
export declare class BoldSignAdapter implements SignatureProvider {
|
|
22
|
+
readonly capabilities: SignatureProviderCapabilities;
|
|
23
|
+
private readonly tenantId;
|
|
24
|
+
private readonly apiKey?;
|
|
25
|
+
private readonly accessToken?;
|
|
26
|
+
private readonly apiBaseUrl;
|
|
27
|
+
private readonly webhookSecrets;
|
|
28
|
+
private readonly webhookToleranceSeconds;
|
|
29
|
+
private readonly fetch;
|
|
30
|
+
private readonly now;
|
|
31
|
+
constructor(options: BoldSignAdapterOptions);
|
|
32
|
+
createRequest(input: CreateSignatureRequestInput): Promise<SignatureRequest>;
|
|
33
|
+
getRequest(input: SignatureRequestReference): Promise<SignatureRequest>;
|
|
34
|
+
cancelRequest(input: CancelSignatureRequestInput): Promise<SignatureRequest>;
|
|
35
|
+
extendExpiry(input: ExtendSignatureRequestExpiryInput): Promise<SignatureRequest>;
|
|
36
|
+
downloadArtifact(input: DownloadSignatureArtifactInput): Promise<SignatureArtifact>;
|
|
37
|
+
parseWebhook(input: ParseSignatureWebhookInput): SignatureWebhookEvent;
|
|
38
|
+
private assertTenant;
|
|
39
|
+
private assertProviderTenant;
|
|
40
|
+
private request;
|
|
41
|
+
}
|
|
42
|
+
export interface VerifyBoldSignWebhookSignatureInput extends ParseSignatureWebhookInput {
|
|
43
|
+
secrets: string | readonly string[];
|
|
44
|
+
toleranceSeconds?: number;
|
|
45
|
+
now?: Date;
|
|
46
|
+
}
|
|
47
|
+
export declare function verifyBoldSignWebhookSignature(input: VerifyBoldSignWebhookSignatureInput): void;
|
|
48
|
+
//# sourceMappingURL=boldsign.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boldsign.d.ts","sourceRoot":"","sources":["../../src/adapters/boldsign.ts"],"names":[],"mappings":"AAUA,OAAO,EAWL,KAAK,cAAc,EACpB,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EACV,2BAA2B,EAC3B,2BAA2B,EAC3B,8BAA8B,EAC9B,iCAAiC,EACjC,0BAA0B,EAC1B,iBAAiB,EAIjB,iBAAiB,EACjB,6BAA6B,EAC7B,gBAAgB,EAChB,yBAAyB,EAKzB,qBAAqB,EACtB,MAAM,aAAa,CAAC;AAErB,eAAO,MAAM,oBAAoB,aAAa,CAAC;AAC/C,eAAO,MAAM,4BAA4B,eAAe,CAAC;AACzD,eAAO,MAAM,iCAAiC,qBAAqB,CAAC;AAsBpE,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AASvD,MAAM,WAAW,sBAAsB;IACrC,wEAAwE;IACxE,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+DAA+D;IAC/D,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;IAC5C,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,0EAA0E;IAC1E,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAUD,qBAAa,eAAgB,YAAW,iBAAiB;IACvD,QAAQ,CAAC,YAAY,EAAE,6BAA6B,CAAC;IAErD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAoB;IACnD,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAS;IACjD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiB;IACvC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAa;gBAErB,OAAO,EAAE,sBAAsB;IAsErC,aAAa,CACjB,KAAK,EAAE,2BAA2B,GACjC,OAAO,CAAC,gBAAgB,CAAC;IAuDtB,UAAU,CACd,KAAK,EAAE,yBAAyB,GAC/B,OAAO,CAAC,gBAAgB,CAAC;IAgBtB,aAAa,CACjB,KAAK,EAAE,2BAA2B,GACjC,OAAO,CAAC,gBAAgB,CAAC;IA2BtB,YAAY,CAChB,KAAK,EAAE,iCAAiC,GACvC,OAAO,CAAC,gBAAgB,CAAC;IAsDtB,gBAAgB,CACpB,KAAK,EAAE,8BAA8B,GACpC,OAAO,CAAC,iBAAiB,CAAC;IA2C7B,YAAY,CAAC,KAAK,EAAE,0BAA0B,GAAG,qBAAqB;IAmFtE,OAAO,CAAC,YAAY;IAUpB,OAAO,CAAC,oBAAoB;YAWd,OAAO;CA+EtB;AAED,MAAM,WAAW,mCACf,SAAQ,0BAA0B;IAClC,OAAO,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;IACpC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;AAED,wBAAgB,8BAA8B,CAC5C,KAAK,EAAE,mCAAmC,GACzC,IAAI,CAgGN"}
|