@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.cjs
CHANGED
|
@@ -113,6 +113,58 @@ function toErrorResponse(err) {
|
|
|
113
113
|
{ status: 500 }
|
|
114
114
|
);
|
|
115
115
|
}
|
|
116
|
+
|
|
117
|
+
// src/shared/fields.ts
|
|
118
|
+
var DATE_FORMATS = [
|
|
119
|
+
"DD/MM/YYYY",
|
|
120
|
+
"MM/DD/YYYY",
|
|
121
|
+
"YYYY-MM-DD",
|
|
122
|
+
"D MMM YYYY",
|
|
123
|
+
"Do MMM YYYY",
|
|
124
|
+
"Do MMMM YYYY",
|
|
125
|
+
"MMMM D, YYYY"
|
|
126
|
+
];
|
|
127
|
+
var DEFAULT_DATE_FORMAT = "Do MMMM YYYY";
|
|
128
|
+
var MONTHS_SHORT = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
129
|
+
var MONTHS_LONG = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
|
|
130
|
+
function ordinal(n) {
|
|
131
|
+
const s = ["th", "st", "nd", "rd"];
|
|
132
|
+
const v = n % 100;
|
|
133
|
+
return `${n}${s[(v - 20) % 10] ?? s[v] ?? s[0]}`;
|
|
134
|
+
}
|
|
135
|
+
function formatPickedDate(iso, format) {
|
|
136
|
+
const [y, m, d] = iso.split("-").map(Number);
|
|
137
|
+
if (!y || !m || !d) return iso;
|
|
138
|
+
const dd = String(d).padStart(2, "0");
|
|
139
|
+
const mm = String(m).padStart(2, "0");
|
|
140
|
+
switch (format) {
|
|
141
|
+
case "DD/MM/YYYY":
|
|
142
|
+
return `${dd}/${mm}/${y}`;
|
|
143
|
+
case "MM/DD/YYYY":
|
|
144
|
+
return `${mm}/${dd}/${y}`;
|
|
145
|
+
case "YYYY-MM-DD":
|
|
146
|
+
return `${y}-${mm}-${dd}`;
|
|
147
|
+
case "D MMM YYYY":
|
|
148
|
+
return `${d} ${MONTHS_SHORT[m - 1]} ${y}`;
|
|
149
|
+
case "Do MMM YYYY":
|
|
150
|
+
return `${ordinal(d)} ${MONTHS_SHORT[m - 1]} ${y}`;
|
|
151
|
+
case "Do MMMM YYYY":
|
|
152
|
+
return `${ordinal(d)} ${MONTHS_LONG[m - 1]} ${y}`;
|
|
153
|
+
case "MMMM D, YYYY":
|
|
154
|
+
return `${MONTHS_LONG[m - 1]} ${d}, ${y}`;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
var DATE_FORMAT_LABELS = {
|
|
158
|
+
"DD/MM/YYYY": "02/10/2026",
|
|
159
|
+
"MM/DD/YYYY": "10/02/2026",
|
|
160
|
+
"YYYY-MM-DD": "2026-10-02",
|
|
161
|
+
"D MMM YYYY": "2 Oct 2026",
|
|
162
|
+
"Do MMM YYYY": "2nd Oct 2026",
|
|
163
|
+
"Do MMMM YYYY": "2nd October 2026",
|
|
164
|
+
"MMMM D, YYYY": "October 2, 2026"
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
// src/server/types.ts
|
|
116
168
|
var ESIGN_CHANNELS = [
|
|
117
169
|
"EMAIL",
|
|
118
170
|
"TELEGRAM",
|
|
@@ -128,11 +180,18 @@ var placementSchema = zod.z.object({
|
|
|
128
180
|
w: zod.z.number().min(0.01).max(1),
|
|
129
181
|
h: zod.z.number().min(0.01).max(1)
|
|
130
182
|
}).strict();
|
|
131
|
-
var
|
|
183
|
+
var isoDate = zod.z.string().regex(/^\d{4}-\d{2}-\d{2}$/);
|
|
184
|
+
var documentFieldSchema = placementSchema.extend({
|
|
132
185
|
id: zod.z.string().min(1).max(64),
|
|
133
|
-
label: zod.z.string().trim().max(80)
|
|
186
|
+
label: zod.z.string().trim().max(80),
|
|
187
|
+
type: zod.z.enum(["signature", "date"]).default("signature"),
|
|
188
|
+
dateFormat: zod.z.enum(DATE_FORMATS).optional(),
|
|
189
|
+
minDate: isoDate.nullable().optional()
|
|
190
|
+
}).strict();
|
|
191
|
+
var signatureFieldSchema = documentFieldSchema;
|
|
192
|
+
var placementsSchema = zod.z.array(documentFieldSchema).min(1).max(20).refine((fields) => fields.some((f) => f.type === "signature"), {
|
|
193
|
+
message: "A document needs at least one signature field."
|
|
134
194
|
});
|
|
135
|
-
var placementsSchema = zod.z.array(signatureFieldSchema).min(1).max(20);
|
|
136
195
|
function addressFor(recipient, channel) {
|
|
137
196
|
switch (channel) {
|
|
138
197
|
case "EMAIL":
|
|
@@ -162,7 +221,8 @@ var submitSignatureSchema = zod.z.object({
|
|
|
162
221
|
signaturePng: zod.z.string().min(1).max(15e5),
|
|
163
222
|
// ~1MB cap on the data URL
|
|
164
223
|
signerName: zod.z.string().trim().min(1).max(200),
|
|
165
|
-
consent: zod.z.literal(true)
|
|
224
|
+
consent: zod.z.literal(true),
|
|
225
|
+
dateValues: zod.z.record(zod.z.string().min(1).max(64), isoDate).optional()
|
|
166
226
|
}).strict();
|
|
167
227
|
var declineSchema = zod.z.object({ reason: zod.z.string().trim().max(500).optional() }).strict();
|
|
168
228
|
var ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
@@ -652,15 +712,35 @@ async function sealPdf(input) {
|
|
|
652
712
|
const png = await pdf.embedPng(input.signaturePng);
|
|
653
713
|
const font = await pdf.embedFont(pdfLib.StandardFonts.Helvetica);
|
|
654
714
|
const caption = `Signed by ${input.signerName} \xB7 ${input.signedAt}${input.signerIp ? ` \xB7 IP ${input.signerIp}` : ""}`;
|
|
655
|
-
|
|
656
|
-
|
|
715
|
+
let captionDrawn = false;
|
|
716
|
+
input.fields.forEach((field) => {
|
|
717
|
+
const pageIndex = Math.min(Math.max(field.page - 1, 0), pages.length - 1);
|
|
657
718
|
const page = pages[pageIndex];
|
|
658
719
|
const pageW = page.getWidth();
|
|
659
720
|
const pageH = page.getHeight();
|
|
660
|
-
const boxX =
|
|
661
|
-
const boxW =
|
|
662
|
-
const boxH =
|
|
663
|
-
const boxBottomY = pageH * (1 -
|
|
721
|
+
const boxX = field.x * pageW;
|
|
722
|
+
const boxW = field.w * pageW;
|
|
723
|
+
const boxH = field.h * pageH;
|
|
724
|
+
const boxBottomY = pageH * (1 - field.y - field.h);
|
|
725
|
+
if (field.type === "date") {
|
|
726
|
+
const iso = input.dateValues[field.id];
|
|
727
|
+
if (!iso) return;
|
|
728
|
+
const text = formatPickedDate(iso, field.dateFormat ?? DEFAULT_DATE_FORMAT);
|
|
729
|
+
let size = Math.min(boxH * 0.62, 15);
|
|
730
|
+
let textW = font.widthOfTextAtSize(text, size);
|
|
731
|
+
if (textW > boxW && textW > 0) {
|
|
732
|
+
size *= boxW / textW;
|
|
733
|
+
textW = font.widthOfTextAtSize(text, size);
|
|
734
|
+
}
|
|
735
|
+
page.drawText(text, {
|
|
736
|
+
x: boxX + Math.max(0, (boxW - textW) / 2),
|
|
737
|
+
y: boxBottomY + (boxH - size) / 2 + size * 0.22,
|
|
738
|
+
size,
|
|
739
|
+
font,
|
|
740
|
+
color: pdfLib.rgb(0.1, 0.1, 0.12)
|
|
741
|
+
});
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
664
744
|
const scale = Math.min(boxW / png.width, boxH / png.height);
|
|
665
745
|
const drawW = png.width * scale;
|
|
666
746
|
const drawH = png.height * scale;
|
|
@@ -670,7 +750,8 @@ async function sealPdf(input) {
|
|
|
670
750
|
width: drawW,
|
|
671
751
|
height: drawH
|
|
672
752
|
});
|
|
673
|
-
if (
|
|
753
|
+
if (!captionDrawn) {
|
|
754
|
+
captionDrawn = true;
|
|
674
755
|
page.drawText(caption, {
|
|
675
756
|
x: boxX,
|
|
676
757
|
y: Math.max(boxBottomY - 11, 4),
|
|
@@ -786,6 +867,18 @@ async function submitSignature(deps, input) {
|
|
|
786
867
|
const src = await storage.get(version.sourceObjectKey);
|
|
787
868
|
if (!src) throw new EsignError("INTERNAL", "Source document missing.");
|
|
788
869
|
const document = await persistence.getDocument(request.documentId);
|
|
870
|
+
const dateValues = input.dateValues ?? {};
|
|
871
|
+
const createdFloor = document?.createdAt ? document.createdAt.slice(0, 10) : null;
|
|
872
|
+
for (const field of version.placements) {
|
|
873
|
+
if (field.type !== "date") continue;
|
|
874
|
+
const value = dateValues[field.id];
|
|
875
|
+
const name = field.label || "date";
|
|
876
|
+
if (!value) throw new EsignError("INVALID_INPUT", `Please fill in the "${name}" date.`);
|
|
877
|
+
const floor = field.minDate ?? createdFloor;
|
|
878
|
+
if (floor && value < floor) {
|
|
879
|
+
throw new EsignError("INVALID_INPUT", `The "${name}" date can't be before ${floor}.`);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
789
882
|
const existing = (await persistence.listAuditEvents(request.id)).map(toLink);
|
|
790
883
|
const nowIso = now.toISOString();
|
|
791
884
|
const consented = await recordEvent(persistence, request.id, existing.at(-1) ?? null, {
|
|
@@ -800,7 +893,8 @@ async function submitSignature(deps, input) {
|
|
|
800
893
|
const seal = await sealPdf({
|
|
801
894
|
sourcePdf: src.bytes,
|
|
802
895
|
signaturePng: input.signaturePng,
|
|
803
|
-
|
|
896
|
+
fields: version.placements,
|
|
897
|
+
dateValues,
|
|
804
898
|
signerName: input.signerName,
|
|
805
899
|
signedAt: nowIso,
|
|
806
900
|
signerIp: input.ip,
|
|
@@ -921,6 +1015,7 @@ function createSignActionsRoute(deps) {
|
|
|
921
1015
|
token,
|
|
922
1016
|
signaturePng: pngFromDataUrl(body.signaturePng),
|
|
923
1017
|
signerName: body.signerName,
|
|
1018
|
+
dateValues: body.dateValues,
|
|
924
1019
|
ip: clientIp(req),
|
|
925
1020
|
userAgent: req.headers.get("user-agent")
|
|
926
1021
|
});
|
|
@@ -1014,6 +1109,9 @@ function registerSubjectTypes(types) {
|
|
|
1014
1109
|
}
|
|
1015
1110
|
|
|
1016
1111
|
exports.ACTIVE_STATUSES = ACTIVE_STATUSES;
|
|
1112
|
+
exports.DATE_FORMATS = DATE_FORMATS;
|
|
1113
|
+
exports.DATE_FORMAT_LABELS = DATE_FORMAT_LABELS;
|
|
1114
|
+
exports.DEFAULT_DATE_FORMAT = DEFAULT_DATE_FORMAT;
|
|
1017
1115
|
exports.ESIGN_CHANNELS = ESIGN_CHANNELS;
|
|
1018
1116
|
exports.ESIGN_STATUSES = ESIGN_STATUSES;
|
|
1019
1117
|
exports.EsignError = EsignError;
|
|
@@ -1034,7 +1132,9 @@ exports.declineRequest = declineRequest;
|
|
|
1034
1132
|
exports.declineSchema = declineSchema;
|
|
1035
1133
|
exports.deleteDocument = deleteDocument;
|
|
1036
1134
|
exports.documentCoverage = documentCoverage;
|
|
1135
|
+
exports.documentFieldSchema = documentFieldSchema;
|
|
1037
1136
|
exports.esignChannelSchema = esignChannelSchema;
|
|
1137
|
+
exports.formatPickedDate = formatPickedDate;
|
|
1038
1138
|
exports.getSigningView = getSigningView;
|
|
1039
1139
|
exports.isActive = isActive;
|
|
1040
1140
|
exports.isTerminal = isTerminal;
|