@f-o-t/e-signature 1.0.7 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +116 -2
- package/dist/appearance.d.ts +2 -2
- package/dist/appearance.d.ts.map +1 -1
- package/dist/batch.d.ts +61 -0
- package/dist/batch.d.ts.map +1 -0
- package/dist/icp-brasil.d.ts.map +1 -1
- package/dist/index.d.ts +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +314 -185
- package/dist/index.js.map +9 -8
- package/dist/schemas.d.ts +13 -0
- package/dist/schemas.d.ts.map +1 -1
- package/dist/sign-pdf.d.ts +2 -2
- package/dist/sign-pdf.d.ts.map +1 -1
- package/dist/timestamp.d.ts +8 -3
- package/dist/timestamp.d.ts.map +1 -1
- package/dist/types.d.ts +10 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +40 -40
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ bun add @f-o-t/e-signature
|
|
|
19
19
|
|
|
20
20
|
## API
|
|
21
21
|
|
|
22
|
-
### `signPdf(pdf: Uint8Array
|
|
22
|
+
### `signPdf(pdf: Uint8Array | ReadableStream<Uint8Array>, options: PdfSignOptions): Promise<Uint8Array>`
|
|
23
23
|
|
|
24
24
|
Sign a PDF document with a digital certificate. Supports PAdES-BES and PAdES with ICP-Brasil compliance (signing-certificate-v2 and signature-policy attributes).
|
|
25
25
|
|
|
@@ -46,6 +46,52 @@ const signedPdf = await signPdf(pdfBytes, {
|
|
|
46
46
|
await Bun.write("signed.pdf", signedPdf);
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
+
To stamp multiple pages with the same visual appearance:
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
const signedPdf = await signPdf(pdfBytes, {
|
|
53
|
+
certificate: { p12, password: "secret" },
|
|
54
|
+
reason: "Document approval",
|
|
55
|
+
appearances: [
|
|
56
|
+
{ x: 50, y: 730, width: 200, height: 80, page: 0, showCertInfo: true },
|
|
57
|
+
{ x: 50, y: 730, width: 200, height: 80, page: 1, showCertInfo: true },
|
|
58
|
+
{ x: 50, y: 730, width: 200, height: 80, page: 2, showCertInfo: true },
|
|
59
|
+
],
|
|
60
|
+
qrCode: { data: "https://validar.iti.gov.br", size: 128 },
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`appearance` and `appearances` can be used simultaneously — both stamps are rendered. An empty `appearances: []` is a no-op.
|
|
65
|
+
|
|
66
|
+
#### TSA Resilience
|
|
67
|
+
|
|
68
|
+
Control timeout, retry, and fallback behavior for timestamp requests:
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
const signedPdf = await signPdf(pdfBytes, {
|
|
72
|
+
certificate: { p12, password: "secret" },
|
|
73
|
+
timestamp: true,
|
|
74
|
+
tsaUrl: TIMESTAMP_SERVERS.VALID,
|
|
75
|
+
tsaTimeout: 5000, // 5s per attempt (default: 10000)
|
|
76
|
+
tsaRetries: 2, // 2 attempts on primary (default: 1)
|
|
77
|
+
tsaFallbackUrls: [ // tried in order after primary fails
|
|
78
|
+
TIMESTAMP_SERVERS.SAFEWEB,
|
|
79
|
+
TIMESTAMP_SERVERS.CERTISIGN,
|
|
80
|
+
],
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
If all servers fail, a `TimestampError` is thrown with a descriptive message identifying which servers were tried and the last error.
|
|
85
|
+
|
|
86
|
+
#### Signing from a ReadableStream
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
const stream: ReadableStream<Uint8Array> = getFileStream("document.pdf");
|
|
90
|
+
const signedPdf = await signPdf(stream, {
|
|
91
|
+
certificate: { p12, password: "secret" },
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
49
95
|
### `buildSigningCertificateV2(certDer: Uint8Array): Uint8Array`
|
|
50
96
|
|
|
51
97
|
Build the `id-aa-signingCertificateV2` attribute value (RFC 5035). Links the signature to the specific certificate used, preventing substitution attacks.
|
|
@@ -58,7 +104,7 @@ Build the `id-aa-ets-sigPolicyId` attribute value. Downloads the ICP-Brasil PAdE
|
|
|
58
104
|
|
|
59
105
|
Clear the cached signature policy data, forcing a re-download on the next call.
|
|
60
106
|
|
|
61
|
-
### `requestTimestamp(data: Uint8Array, tsaUrl: string, hashAlgorithm?: "sha256" | "sha384" | "sha512"): Promise<Uint8Array>`
|
|
107
|
+
### `requestTimestamp(data: Uint8Array, tsaUrl: string, hashAlgorithm?: "sha256" | "sha384" | "sha512", options?: { tsaTimeout?: number; tsaRetries?: number; tsaFallbackUrls?: string[] }): Promise<Uint8Array>`
|
|
62
108
|
|
|
63
109
|
Request an RFC 3161 timestamp from a TSA server. Returns the DER-encoded TimeStampToken.
|
|
64
110
|
|
|
@@ -68,6 +114,50 @@ import { requestTimestamp, TIMESTAMP_SERVERS } from "@f-o-t/e-signature";
|
|
|
68
114
|
const token = await requestTimestamp(signatureBytes, TIMESTAMP_SERVERS.VALID);
|
|
69
115
|
```
|
|
70
116
|
|
|
117
|
+
### `signPdfBatch(files: BatchSignInput[], options: PdfSignOptions): AsyncGenerator<BatchSignEvent>`
|
|
118
|
+
|
|
119
|
+
Sign multiple PDFs sequentially, yielding progress events. Yields control between each signing to prevent blocking the event loop.
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
import { signPdfBatch, signPdfBatchToArray, TIMESTAMP_SERVERS } from "@f-o-t/e-signature";
|
|
123
|
+
|
|
124
|
+
for await (const event of signPdfBatch(
|
|
125
|
+
[
|
|
126
|
+
{ filename: "doc1.pdf", pdf: pdf1Bytes },
|
|
127
|
+
{ filename: "doc2.pdf", pdf: pdf2Bytes, options: { reason: "Custom reason" } },
|
|
128
|
+
],
|
|
129
|
+
{ certificate: { p12, password: "secret" } },
|
|
130
|
+
)) {
|
|
131
|
+
switch (event.type) {
|
|
132
|
+
case "file_start": console.log(`Signing ${event.filename}...`); break;
|
|
133
|
+
case "file_complete": console.log(`Signed ${event.filename}`); break;
|
|
134
|
+
case "file_error": console.error(`Failed ${event.filename}: ${event.error}`); break;
|
|
135
|
+
case "batch_complete": console.log(`Done: ${event.totalFiles} files, ${event.errorCount} errors`); break;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Per-file `options` are merged with the base options (per-file takes priority). Error in one file emits a `file_error` event and continues with the next file.
|
|
141
|
+
|
|
142
|
+
### `signPdfBatchToArray(files: BatchSignInput[], options: PdfSignOptions): Promise<...[]>`
|
|
143
|
+
|
|
144
|
+
Convenience wrapper that collects all results:
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
const results = await signPdfBatchToArray(
|
|
148
|
+
[
|
|
149
|
+
{ filename: "doc1.pdf", pdf: pdf1Bytes },
|
|
150
|
+
{ filename: "doc2.pdf", pdf: pdf2Bytes },
|
|
151
|
+
],
|
|
152
|
+
{ certificate: { p12, password: "secret" } },
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
for (const r of results) {
|
|
156
|
+
if (r.signed) await Bun.write(r.filename, r.signed);
|
|
157
|
+
else console.error(`${r.filename}: ${r.error}`);
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
71
161
|
## Constants
|
|
72
162
|
|
|
73
163
|
### `ICP_BRASIL_OIDS`
|
|
@@ -102,7 +192,16 @@ type PdfSignOptions = {
|
|
|
102
192
|
policy?: "pades-ades" | "pades-icp-brasil";
|
|
103
193
|
timestamp?: boolean;
|
|
104
194
|
tsaUrl?: string;
|
|
195
|
+
/** Timeout in ms per TSA attempt (default: 10000) */
|
|
196
|
+
tsaTimeout?: number;
|
|
197
|
+
/** Number of retry attempts on primary TSA server before trying fallbacks (default: 1) */
|
|
198
|
+
tsaRetries?: number;
|
|
199
|
+
/** Fallback TSA server URLs tried in order after primary is exhausted */
|
|
200
|
+
tsaFallbackUrls?: string[];
|
|
201
|
+
/** Single visual stamp (false to disable) */
|
|
105
202
|
appearance?: SignatureAppearance | false;
|
|
203
|
+
/** Multiple visual stamps — renders one per entry, useful for multi-page documents */
|
|
204
|
+
appearances?: SignatureAppearance[];
|
|
106
205
|
qrCode?: QrCodeConfig;
|
|
107
206
|
docMdpPermission?: 1 | 2 | 3;
|
|
108
207
|
};
|
|
@@ -121,6 +220,21 @@ type QrCodeConfig = {
|
|
|
121
220
|
data?: string;
|
|
122
221
|
size?: number;
|
|
123
222
|
};
|
|
223
|
+
|
|
224
|
+
type BatchSignInput = {
|
|
225
|
+
/** Filename for identification in events */
|
|
226
|
+
filename: string;
|
|
227
|
+
/** PDF content as Uint8Array or ReadableStream */
|
|
228
|
+
pdf: Uint8Array | ReadableStream<Uint8Array>;
|
|
229
|
+
/** Per-file option overrides merged with base options */
|
|
230
|
+
options?: Partial<PdfSignOptions>;
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
type BatchSignEvent =
|
|
234
|
+
| { type: "file_start"; fileIndex: number; filename: string }
|
|
235
|
+
| { type: "file_complete"; fileIndex: number; filename: string; signed: Uint8Array }
|
|
236
|
+
| { type: "file_error"; fileIndex: number; filename: string; error: string }
|
|
237
|
+
| { type: "batch_complete"; totalFiles: number; errorCount: number };
|
|
124
238
|
```
|
|
125
239
|
|
|
126
240
|
## Error Classes
|
package/dist/appearance.d.ts
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* for visible digital signatures.
|
|
6
6
|
*/
|
|
7
7
|
import type { CertificateInfo } from "@f-o-t/digital-certificate";
|
|
8
|
-
import type {
|
|
9
|
-
import type {
|
|
8
|
+
import type { PdfDocument, PdfPage } from "@f-o-t/pdf/plugins/editing";
|
|
9
|
+
import type { QrCodeConfig, SignatureAppearance } from "./types.ts";
|
|
10
10
|
/**
|
|
11
11
|
* Draw the visual signature appearance on a PDF page.
|
|
12
12
|
*
|
package/dist/appearance.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"appearance.d.ts","sourceRoot":"","sources":["../src/appearance.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"appearance.d.ts","sourceRoot":"","sources":["../src/appearance.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAEvE,OAAO,KAAK,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEpE;;;;GAIG;AACH,wBAAgB,uBAAuB,CACpC,GAAG,EAAE,WAAW,EAChB,IAAI,EAAE,OAAO,EACb,UAAU,EAAE,mBAAmB,EAC/B,QAAQ,EAAE,eAAe,GAAG,IAAI,EAChC,OAAO,EAAE;IACN,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,OAAO,EAAE,UAAU,CAAC;CACtB,GACD,IAAI,CA6CN"}
|
package/dist/batch.d.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { PdfSignOptions } from "./types.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Input for a single PDF in a batch signing operation.
|
|
4
|
+
*/
|
|
5
|
+
export type BatchSignInput = {
|
|
6
|
+
/** Filename for identification in events */
|
|
7
|
+
filename: string;
|
|
8
|
+
/** PDF content as Uint8Array or ReadableStream */
|
|
9
|
+
pdf: Uint8Array | ReadableStream<Uint8Array>;
|
|
10
|
+
/** Per-file option overrides merged with base options */
|
|
11
|
+
options?: Partial<PdfSignOptions>;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Events emitted during batch signing.
|
|
15
|
+
*/
|
|
16
|
+
export type BatchSignEvent = {
|
|
17
|
+
type: "file_start";
|
|
18
|
+
fileIndex: number;
|
|
19
|
+
filename: string;
|
|
20
|
+
} | {
|
|
21
|
+
type: "file_complete";
|
|
22
|
+
fileIndex: number;
|
|
23
|
+
filename: string;
|
|
24
|
+
signed: Uint8Array;
|
|
25
|
+
} | {
|
|
26
|
+
type: "file_error";
|
|
27
|
+
fileIndex: number;
|
|
28
|
+
filename: string;
|
|
29
|
+
error: string;
|
|
30
|
+
} | {
|
|
31
|
+
type: "batch_complete";
|
|
32
|
+
totalFiles: number;
|
|
33
|
+
errorCount: number;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Sign multiple PDFs sequentially, yielding progress events.
|
|
37
|
+
*
|
|
38
|
+
* Yields control between each signing operation to prevent blocking
|
|
39
|
+
* the event loop under bulk load.
|
|
40
|
+
*
|
|
41
|
+
* @param files - Array of PDFs with filename and optional per-file options
|
|
42
|
+
* @param options - Base signing options (per-file options override these)
|
|
43
|
+
* @yields BatchSignEvent for each file start, completion, error, and batch complete
|
|
44
|
+
*/
|
|
45
|
+
export declare function signPdfBatch(files: BatchSignInput[], options: PdfSignOptions): AsyncGenerator<BatchSignEvent>;
|
|
46
|
+
/**
|
|
47
|
+
* Sign multiple PDFs sequentially, collecting all results.
|
|
48
|
+
*
|
|
49
|
+
* Convenience wrapper around {@link signPdfBatch} that collects all events
|
|
50
|
+
* into an array of results.
|
|
51
|
+
*
|
|
52
|
+
* @param files - Array of PDFs with filename and optional per-file options
|
|
53
|
+
* @param options - Base signing options
|
|
54
|
+
* @returns Array of results with signed PDF bytes or error per file
|
|
55
|
+
*/
|
|
56
|
+
export declare function signPdfBatchToArray(files: BatchSignInput[], options: PdfSignOptions): Promise<{
|
|
57
|
+
filename: string;
|
|
58
|
+
signed?: Uint8Array;
|
|
59
|
+
error?: string;
|
|
60
|
+
}[]>;
|
|
61
|
+
//# sourceMappingURL=batch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batch.d.ts","sourceRoot":"","sources":["../src/batch.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,4CAA4C;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,kDAAkD;IAClD,GAAG,EAAE,UAAU,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;IAC7C,yDAAyD;IACzD,OAAO,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;CACnC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC3D;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAAE,GAClF;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC1E;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvE;;;;;;;;;GASG;AACH,wBAAuB,YAAY,CACjC,KAAK,EAAE,cAAc,EAAE,EACvB,OAAO,EAAE,cAAc,GACtB,cAAc,CAAC,cAAc,CAAC,CA+BhC;AAED;;;;;;;;;GASG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,cAAc,EAAE,EACvB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,UAAU,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,CAAC,CAoBtE"}
|
package/dist/icp-brasil.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"icp-brasil.d.ts","sourceRoot":"","sources":["../src/icp-brasil.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA8CH;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,UAAU,GAAG,UAAU,CAuCzE;
|
|
1
|
+
{"version":3,"file":"icp-brasil.d.ts","sourceRoot":"","sources":["../src/icp-brasil.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA8CH;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,UAAU,GAAG,UAAU,CAuCzE;AAyED;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,UAAU,CAAC,CA8BhE;AAED;;GAEG;AACH,eAAO,MAAM,eAAe;;;CAGlB,CAAC;AAMX,qBAAa,oBAAqB,SAAQ,KAAK;gBAChC,OAAO,EAAE,MAAM;CAI7B"}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,9 +6,11 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @packageDocumentation
|
|
8
8
|
*/
|
|
9
|
-
export {
|
|
10
|
-
export {
|
|
11
|
-
export {
|
|
12
|
-
export type { PdfSignOptions, SignatureAppearance, QrCodeConfig } from "./types.ts";
|
|
9
|
+
export { signPdfBatch, signPdfBatchToArray } from "./batch.ts";
|
|
10
|
+
export type { BatchSignEvent, BatchSignInput } from "./batch.ts";
|
|
11
|
+
export { buildSignaturePolicy, buildSigningCertificateV2, clearPolicyCache, ICP_BRASIL_OIDS, SignaturePolicyError, } from "./icp-brasil.ts";
|
|
13
12
|
export { pdfSignOptionsSchema } from "./schemas.ts";
|
|
13
|
+
export { PdfSignError, signPdf } from "./sign-pdf.ts";
|
|
14
|
+
export { requestTimestamp, TIMESTAMP_SERVERS, TIMESTAMP_TOKEN_OID, TimestampError, } from "./timestamp.ts";
|
|
15
|
+
export type { PdfSignOptions, QrCodeConfig, SignatureAppearance, } from "./types.ts";
|
|
14
16
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAC/D,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,EACJ,oBAAoB,EACpB,yBAAyB,EACzB,gBAAgB,EAChB,eAAe,EACf,oBAAoB,GACtB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EACJ,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,cAAc,GAChB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACT,cAAc,EACd,YAAY,EACZ,mBAAmB,GACrB,MAAM,YAAY,CAAC"}
|