@josephomills/esign 0.8.0 → 0.9.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/dist/{ports-CkBaAepo.d.cts → ports--YsOnh8H.d.cts} +120 -15
- package/dist/{ports-CkBaAepo.d.ts → ports--YsOnh8H.d.ts} +120 -15
- package/dist/prisma/index.cjs +21 -4
- package/dist/prisma/index.cjs.map +1 -1
- package/dist/prisma/index.d.cts +1 -1
- package/dist/prisma/index.d.ts +1 -1
- package/dist/prisma/index.js +21 -4
- package/dist/prisma/index.js.map +1 -1
- package/dist/server/index.cjs +112 -12
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +9 -5
- package/dist/server/index.d.ts +9 -5
- package/dist/server/index.js +108 -13
- package/dist/server/index.js.map +1 -1
- package/dist/ui/index.cjs +243 -27
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.d.cts +20 -0
- package/dist/ui/index.d.ts +20 -0
- package/dist/ui/index.js +243 -27
- package/dist/ui/index.js.map +1 -1
- package/package.json +1 -1
package/dist/server/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as SubjectSelection, E as EsignChannel, R as ResendPolicy, P as PersistencePort, a as SubjectsPort, N as NotifierPort, C as Clock, b as SkippedSubject, c as StoragePort, H as HooksPort, d as SignatureField, e as SigningDocumentVersionDTO, f as SigningDocumentDTO, g as SigningRequestDTO, h as EsignConfig, V as VersionPolicy, i as SubjectSigningStatus, j as CoverageReport, k as CampaignStatsRow, D as DocumentStatsRow, l as StatsRange, m as ScopeStatsRow, O as OutstandingRequest, n as SubjectTypeDescriptor, o as
|
|
2
|
-
export { A as ACTIVE_STATUSES, p as AffectedSubject, q as AuthPort, r as
|
|
1
|
+
import { S as SubjectSelection, E as EsignChannel, R as ResendPolicy, P as PersistencePort, a as SubjectsPort, N as NotifierPort, C as Clock, b as SkippedSubject, c as StoragePort, H as HooksPort, d as SignatureField, e as SigningDocumentVersionDTO, f as SigningDocumentDTO, g as SigningRequestDTO, h as EsignConfig, V as VersionPolicy, i as SubjectSigningStatus, j as CoverageReport, k as CampaignStatsRow, D as DocumentStatsRow, l as StatsRange, m as ScopeStatsRow, O as OutstandingRequest, n as SubjectTypeDescriptor, o as DocumentField } from '../ports--YsOnh8H.js';
|
|
2
|
+
export { A as ACTIVE_STATUSES, p as AffectedSubject, q as AuthPort, r as DATE_FORMATS, s as DATE_FORMAT_LABELS, t as DEFAULT_DATE_FORMAT, u as DateFormat, v as DeclineInput, w as DocumentDeletionContext, x as ESIGN_CHANNELS, y as ESIGN_STATUSES, z as EsignStatus, F as FieldType, G as GroupBreakdown, B as NewAuditEventRow, I as NewRequestRow, J as NotifierAttachment, K as Placement, L as Recipient, M as RequestSummary, Q as SigningAuditEventDTO, T as SigningCampaignDTO, U as StatusCountMap, W as SubjectFilter, X as SubjectFilterField, Y as SubjectSigningState, Z as SubjectSummary, _ as SubmitSignatureInput, $ as TERMINAL_STATUSES, a0 as assertTransition, a1 as canTransition, a2 as declineSchema, a3 as documentFieldSchema, a4 as esignChannelSchema, a5 as formatPickedDate, a6 as isActive, a7 as isTerminal, a8 as placementSchema, a9 as placementsSchema, aa as signatureFieldSchema, ab as subjectSelectionSchema, ac as submitSignatureSchema } from '../ports--YsOnh8H.js';
|
|
3
3
|
import { PDFDocument } from 'pdf-lib';
|
|
4
4
|
import { ZodType } from 'zod';
|
|
5
5
|
|
|
@@ -176,6 +176,8 @@ interface SubmitInput {
|
|
|
176
176
|
token: string;
|
|
177
177
|
signaturePng: Uint8Array;
|
|
178
178
|
signerName: string;
|
|
179
|
+
/** Picked date per date-field id (ISO `yyyy-mm-dd`). */
|
|
180
|
+
dateValues?: Record<string, string>;
|
|
179
181
|
ip: string | null;
|
|
180
182
|
userAgent: string | null;
|
|
181
183
|
}
|
|
@@ -344,8 +346,10 @@ interface SealInput {
|
|
|
344
346
|
sourcePdf: Uint8Array;
|
|
345
347
|
/** PNG bytes of the drawn or typed signature. */
|
|
346
348
|
signaturePng: Uint8Array;
|
|
347
|
-
/**
|
|
348
|
-
|
|
349
|
+
/** The version's fields. Signature fields get the signature; date fields the picked date. */
|
|
350
|
+
fields: DocumentField[];
|
|
351
|
+
/** Picked date per date-field id (ISO `yyyy-mm-dd`). */
|
|
352
|
+
dateValues: Record<string, string>;
|
|
349
353
|
signerName: string;
|
|
350
354
|
/** ISO instant — also pins the PDF metadata dates for reproducibility. */
|
|
351
355
|
signedAt: string;
|
|
@@ -390,4 +394,4 @@ declare class EsignError extends Error {
|
|
|
390
394
|
}
|
|
391
395
|
declare function toErrorResponse(err: unknown): Response;
|
|
392
396
|
|
|
393
|
-
export { type AddVersionInput, type AuditEventInput, type AuditLink, type CampaignContentBuilder, CampaignStatsRow, type CertInfo, type ChannelContent, Clock, type CoverageDeps, CoverageReport, type CreateCampaignArgs, type CreateCampaignDeps, type CreateCampaignResult, type CreateDocumentInput, type DeclineRequestInput, type DeleteDocumentDeps, type DeleteDocumentInput, type DeleteDocumentResult, type DeleteMode, DocumentStatsRow, EsignChannel, EsignConfig, EsignError, type EsignErrorCode, type EsignRuntime, HooksPort, NotifierPort, OutstandingRequest, PersistencePort,
|
|
397
|
+
export { type AddVersionInput, type AuditEventInput, type AuditLink, type CampaignContentBuilder, CampaignStatsRow, type CertInfo, type ChannelContent, Clock, type CoverageDeps, CoverageReport, type CreateCampaignArgs, type CreateCampaignDeps, type CreateCampaignResult, type CreateDocumentInput, type DeclineRequestInput, type DeleteDocumentDeps, type DeleteDocumentInput, type DeleteDocumentResult, type DeleteMode, DocumentField, DocumentStatsRow, EsignChannel, EsignConfig, EsignError, type EsignErrorCode, type EsignRuntime, HooksPort, NotifierPort, OutstandingRequest, PersistencePort, ResendPolicy, type ResolvedEsignConfig, ScopeStatsRow, type SealInput, type SealResult, type SignDeps, type SignServeDeps, SignatureField, SigningDocumentDTO, SigningDocumentVersionDTO, SigningRequestDTO, type SigningToken, type SigningView, SkippedSubject, StatsRange, StoragePort, SubjectSelection, SubjectSigningStatus, type SubjectSigningStatusArgs, SubjectTypeDescriptor, SubjectsPort, type SubmitInput, type SubmitResult, VersionPolicy, type VersionSource, addVersion, appendEvent, buildChain, canonicalize, clientIp, configureEsign, createCampaign, createDocument, createSignActionsRoute, createSignServeRoute, declineRequest, deleteDocument, documentCoverage, getSigningView, linkHash, newSigningToken, ok, parseBody, recordEvent, recordView, registerSubjectTypes, renderAuditCertificatePage, sealPdf, sha256Hex, submitSignature, toErrorResponse, uid, verifyChain };
|
package/dist/server/index.js
CHANGED
|
@@ -111,6 +111,58 @@ function toErrorResponse(err) {
|
|
|
111
111
|
{ status: 500 }
|
|
112
112
|
);
|
|
113
113
|
}
|
|
114
|
+
|
|
115
|
+
// src/shared/fields.ts
|
|
116
|
+
var DATE_FORMATS = [
|
|
117
|
+
"DD/MM/YYYY",
|
|
118
|
+
"MM/DD/YYYY",
|
|
119
|
+
"YYYY-MM-DD",
|
|
120
|
+
"D MMM YYYY",
|
|
121
|
+
"Do MMM YYYY",
|
|
122
|
+
"Do MMMM YYYY",
|
|
123
|
+
"MMMM D, YYYY"
|
|
124
|
+
];
|
|
125
|
+
var DEFAULT_DATE_FORMAT = "Do MMMM YYYY";
|
|
126
|
+
var MONTHS_SHORT = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
127
|
+
var MONTHS_LONG = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
|
|
128
|
+
function ordinal(n) {
|
|
129
|
+
const s = ["th", "st", "nd", "rd"];
|
|
130
|
+
const v = n % 100;
|
|
131
|
+
return `${n}${s[(v - 20) % 10] ?? s[v] ?? s[0]}`;
|
|
132
|
+
}
|
|
133
|
+
function formatPickedDate(iso, format) {
|
|
134
|
+
const [y, m, d] = iso.split("-").map(Number);
|
|
135
|
+
if (!y || !m || !d) return iso;
|
|
136
|
+
const dd = String(d).padStart(2, "0");
|
|
137
|
+
const mm = String(m).padStart(2, "0");
|
|
138
|
+
switch (format) {
|
|
139
|
+
case "DD/MM/YYYY":
|
|
140
|
+
return `${dd}/${mm}/${y}`;
|
|
141
|
+
case "MM/DD/YYYY":
|
|
142
|
+
return `${mm}/${dd}/${y}`;
|
|
143
|
+
case "YYYY-MM-DD":
|
|
144
|
+
return `${y}-${mm}-${dd}`;
|
|
145
|
+
case "D MMM YYYY":
|
|
146
|
+
return `${d} ${MONTHS_SHORT[m - 1]} ${y}`;
|
|
147
|
+
case "Do MMM YYYY":
|
|
148
|
+
return `${ordinal(d)} ${MONTHS_SHORT[m - 1]} ${y}`;
|
|
149
|
+
case "Do MMMM YYYY":
|
|
150
|
+
return `${ordinal(d)} ${MONTHS_LONG[m - 1]} ${y}`;
|
|
151
|
+
case "MMMM D, YYYY":
|
|
152
|
+
return `${MONTHS_LONG[m - 1]} ${d}, ${y}`;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
var DATE_FORMAT_LABELS = {
|
|
156
|
+
"DD/MM/YYYY": "02/10/2026",
|
|
157
|
+
"MM/DD/YYYY": "10/02/2026",
|
|
158
|
+
"YYYY-MM-DD": "2026-10-02",
|
|
159
|
+
"D MMM YYYY": "2 Oct 2026",
|
|
160
|
+
"Do MMM YYYY": "2nd Oct 2026",
|
|
161
|
+
"Do MMMM YYYY": "2nd October 2026",
|
|
162
|
+
"MMMM D, YYYY": "October 2, 2026"
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
// src/server/types.ts
|
|
114
166
|
var ESIGN_CHANNELS = [
|
|
115
167
|
"EMAIL",
|
|
116
168
|
"TELEGRAM",
|
|
@@ -126,11 +178,18 @@ var placementSchema = z.object({
|
|
|
126
178
|
w: z.number().min(0.01).max(1),
|
|
127
179
|
h: z.number().min(0.01).max(1)
|
|
128
180
|
}).strict();
|
|
129
|
-
var
|
|
181
|
+
var isoDate = z.string().regex(/^\d{4}-\d{2}-\d{2}$/);
|
|
182
|
+
var documentFieldSchema = placementSchema.extend({
|
|
130
183
|
id: z.string().min(1).max(64),
|
|
131
|
-
label: z.string().trim().max(80)
|
|
184
|
+
label: z.string().trim().max(80),
|
|
185
|
+
type: z.enum(["signature", "date"]).default("signature"),
|
|
186
|
+
dateFormat: z.enum(DATE_FORMATS).optional(),
|
|
187
|
+
minDate: isoDate.nullable().optional()
|
|
188
|
+
}).strict();
|
|
189
|
+
var signatureFieldSchema = documentFieldSchema;
|
|
190
|
+
var placementsSchema = z.array(documentFieldSchema).min(1).max(20).refine((fields) => fields.some((f) => f.type === "signature"), {
|
|
191
|
+
message: "A document needs at least one signature field."
|
|
132
192
|
});
|
|
133
|
-
var placementsSchema = z.array(signatureFieldSchema).min(1).max(20);
|
|
134
193
|
function addressFor(recipient, channel) {
|
|
135
194
|
switch (channel) {
|
|
136
195
|
case "EMAIL":
|
|
@@ -160,7 +219,8 @@ var submitSignatureSchema = z.object({
|
|
|
160
219
|
signaturePng: z.string().min(1).max(15e5),
|
|
161
220
|
// ~1MB cap on the data URL
|
|
162
221
|
signerName: z.string().trim().min(1).max(200),
|
|
163
|
-
consent: z.literal(true)
|
|
222
|
+
consent: z.literal(true),
|
|
223
|
+
dateValues: z.record(z.string().min(1).max(64), isoDate).optional()
|
|
164
224
|
}).strict();
|
|
165
225
|
var declineSchema = z.object({ reason: z.string().trim().max(500).optional() }).strict();
|
|
166
226
|
var ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
@@ -650,15 +710,35 @@ async function sealPdf(input) {
|
|
|
650
710
|
const png = await pdf.embedPng(input.signaturePng);
|
|
651
711
|
const font = await pdf.embedFont(StandardFonts.Helvetica);
|
|
652
712
|
const caption = `Signed by ${input.signerName} \xB7 ${input.signedAt}${input.signerIp ? ` \xB7 IP ${input.signerIp}` : ""}`;
|
|
653
|
-
|
|
654
|
-
|
|
713
|
+
let captionDrawn = false;
|
|
714
|
+
input.fields.forEach((field) => {
|
|
715
|
+
const pageIndex = Math.min(Math.max(field.page - 1, 0), pages.length - 1);
|
|
655
716
|
const page = pages[pageIndex];
|
|
656
717
|
const pageW = page.getWidth();
|
|
657
718
|
const pageH = page.getHeight();
|
|
658
|
-
const boxX =
|
|
659
|
-
const boxW =
|
|
660
|
-
const boxH =
|
|
661
|
-
const boxBottomY = pageH * (1 -
|
|
719
|
+
const boxX = field.x * pageW;
|
|
720
|
+
const boxW = field.w * pageW;
|
|
721
|
+
const boxH = field.h * pageH;
|
|
722
|
+
const boxBottomY = pageH * (1 - field.y - field.h);
|
|
723
|
+
if (field.type === "date") {
|
|
724
|
+
const iso = input.dateValues[field.id];
|
|
725
|
+
if (!iso) return;
|
|
726
|
+
const text = formatPickedDate(iso, field.dateFormat ?? DEFAULT_DATE_FORMAT);
|
|
727
|
+
let size = Math.min(boxH * 0.62, 15);
|
|
728
|
+
let textW = font.widthOfTextAtSize(text, size);
|
|
729
|
+
if (textW > boxW && textW > 0) {
|
|
730
|
+
size *= boxW / textW;
|
|
731
|
+
textW = font.widthOfTextAtSize(text, size);
|
|
732
|
+
}
|
|
733
|
+
page.drawText(text, {
|
|
734
|
+
x: boxX + Math.max(0, (boxW - textW) / 2),
|
|
735
|
+
y: boxBottomY + (boxH - size) / 2 + size * 0.22,
|
|
736
|
+
size,
|
|
737
|
+
font,
|
|
738
|
+
color: rgb(0.1, 0.1, 0.12)
|
|
739
|
+
});
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
662
742
|
const scale = Math.min(boxW / png.width, boxH / png.height);
|
|
663
743
|
const drawW = png.width * scale;
|
|
664
744
|
const drawH = png.height * scale;
|
|
@@ -668,7 +748,8 @@ async function sealPdf(input) {
|
|
|
668
748
|
width: drawW,
|
|
669
749
|
height: drawH
|
|
670
750
|
});
|
|
671
|
-
if (
|
|
751
|
+
if (!captionDrawn) {
|
|
752
|
+
captionDrawn = true;
|
|
672
753
|
page.drawText(caption, {
|
|
673
754
|
x: boxX,
|
|
674
755
|
y: Math.max(boxBottomY - 11, 4),
|
|
@@ -784,6 +865,18 @@ async function submitSignature(deps, input) {
|
|
|
784
865
|
const src = await storage.get(version.sourceObjectKey);
|
|
785
866
|
if (!src) throw new EsignError("INTERNAL", "Source document missing.");
|
|
786
867
|
const document = await persistence.getDocument(request.documentId);
|
|
868
|
+
const dateValues = input.dateValues ?? {};
|
|
869
|
+
const createdFloor = document?.createdAt ? document.createdAt.slice(0, 10) : null;
|
|
870
|
+
for (const field of version.placements) {
|
|
871
|
+
if (field.type !== "date") continue;
|
|
872
|
+
const value = dateValues[field.id];
|
|
873
|
+
const name = field.label || "date";
|
|
874
|
+
if (!value) throw new EsignError("INVALID_INPUT", `Please fill in the "${name}" date.`);
|
|
875
|
+
const floor = field.minDate ?? createdFloor;
|
|
876
|
+
if (floor && value < floor) {
|
|
877
|
+
throw new EsignError("INVALID_INPUT", `The "${name}" date can't be before ${floor}.`);
|
|
878
|
+
}
|
|
879
|
+
}
|
|
787
880
|
const existing = (await persistence.listAuditEvents(request.id)).map(toLink);
|
|
788
881
|
const nowIso = now.toISOString();
|
|
789
882
|
const consented = await recordEvent(persistence, request.id, existing.at(-1) ?? null, {
|
|
@@ -798,7 +891,8 @@ async function submitSignature(deps, input) {
|
|
|
798
891
|
const seal = await sealPdf({
|
|
799
892
|
sourcePdf: src.bytes,
|
|
800
893
|
signaturePng: input.signaturePng,
|
|
801
|
-
|
|
894
|
+
fields: version.placements,
|
|
895
|
+
dateValues,
|
|
802
896
|
signerName: input.signerName,
|
|
803
897
|
signedAt: nowIso,
|
|
804
898
|
signerIp: input.ip,
|
|
@@ -919,6 +1013,7 @@ function createSignActionsRoute(deps) {
|
|
|
919
1013
|
token,
|
|
920
1014
|
signaturePng: pngFromDataUrl(body.signaturePng),
|
|
921
1015
|
signerName: body.signerName,
|
|
1016
|
+
dateValues: body.dateValues,
|
|
922
1017
|
ip: clientIp(req),
|
|
923
1018
|
userAgent: req.headers.get("user-agent")
|
|
924
1019
|
});
|
|
@@ -1011,6 +1106,6 @@ function registerSubjectTypes(types) {
|
|
|
1011
1106
|
};
|
|
1012
1107
|
}
|
|
1013
1108
|
|
|
1014
|
-
export { ACTIVE_STATUSES, ESIGN_CHANNELS, ESIGN_STATUSES, EsignError, TERMINAL_STATUSES, addVersion, appendEvent, assertTransition, buildChain, canTransition, canonicalize, clientIp, configureEsign, createCampaign, createDocument, createSignActionsRoute, createSignServeRoute, declineRequest, declineSchema, deleteDocument, documentCoverage, esignChannelSchema, getSigningView, isActive, isTerminal, linkHash, newSigningToken, ok, parseBody, placementSchema, placementsSchema, recordEvent, recordView, registerSubjectTypes, renderAuditCertificatePage, sealPdf, sha256Hex, signatureFieldSchema, subjectSelectionSchema, submitSignature, submitSignatureSchema, toErrorResponse, uid, verifyChain };
|
|
1109
|
+
export { ACTIVE_STATUSES, DATE_FORMATS, DATE_FORMAT_LABELS, DEFAULT_DATE_FORMAT, ESIGN_CHANNELS, ESIGN_STATUSES, EsignError, TERMINAL_STATUSES, addVersion, appendEvent, assertTransition, buildChain, canTransition, canonicalize, clientIp, configureEsign, createCampaign, createDocument, createSignActionsRoute, createSignServeRoute, declineRequest, declineSchema, deleteDocument, documentCoverage, documentFieldSchema, esignChannelSchema, formatPickedDate, getSigningView, isActive, isTerminal, linkHash, newSigningToken, ok, parseBody, placementSchema, placementsSchema, recordEvent, recordView, registerSubjectTypes, renderAuditCertificatePage, sealPdf, sha256Hex, signatureFieldSchema, subjectSelectionSchema, submitSignature, submitSignatureSchema, toErrorResponse, uid, verifyChain };
|
|
1015
1110
|
//# sourceMappingURL=index.js.map
|
|
1016
1111
|
//# sourceMappingURL=index.js.map
|