@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 CHANGED
@@ -19,7 +19,7 @@ bun add @f-o-t/e-signature
19
19
 
20
20
  ## API
21
21
 
22
- ### `signPdf(pdf: Uint8Array, options: PdfSignOptions): Promise<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
@@ -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 { PdfPage, PdfDocument } from "@f-o-t/pdf/plugins/editing";
9
- import type { SignatureAppearance, QrCodeConfig } from "./types.ts";
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
  *
@@ -1 +1 @@
1
- {"version":3,"file":"appearance.d.ts","sourceRoot":"","sources":["../src/appearance.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAGvE,OAAO,KAAK,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEpE;;;;GAIG;AACH,wBAAgB,uBAAuB,CACtC,GAAG,EAAE,WAAW,EAChB,IAAI,EAAE,OAAO,EACb,UAAU,EAAE,mBAAmB,EAC/B,QAAQ,EAAE,eAAe,GAAG,IAAI,EAChC,OAAO,EAAE;IACR,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,OAAO,EAAE,UAAU,CAAC;CACpB,GACC,IAAI,CA6CN"}
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"}
@@ -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"}
@@ -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;AA2ED;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,UAAU,CAAC,CA8BhE;AAED;;GAEG;AACH,eAAO,MAAM,eAAe;;;CAGlB,CAAC;AAMX,qBAAa,oBAAqB,SAAQ,KAAK;gBAClC,OAAO,EAAE,MAAM;CAI3B"}
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 { signPdf, PdfSignError } from "./sign-pdf.ts";
10
- export { buildSigningCertificateV2, buildSignaturePolicy, clearPolicyCache, SignaturePolicyError, ICP_BRASIL_OIDS, } from "./icp-brasil.ts";
11
- export { requestTimestamp, TimestampError, TIMESTAMP_SERVERS, TIMESTAMP_TOKEN_OID, } from "./timestamp.ts";
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
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EACN,yBAAyB,EACzB,oBAAoB,EACpB,gBAAgB,EAChB,oBAAoB,EACpB,eAAe,GACf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACN,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,mBAAmB,GACnB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,cAAc,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACpF,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC"}
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"}