chore: merge main, resolve biome formatting conflicts

Merge origin/main into feat/external-2fa-codes. Resolve formatting
conflicts caused by biome rollout; preserve both feature streams:
PR's external 2FA token + signing-session 2FA proof additions plus
main's RateLimit/RecipientExpired/signingReminders/date-auto-insert.

In complete-document-with-token.ts, drop the duplicate early
field-fetching block introduced when main moved that logic later
with date auto-insert support; keep the EXTERNAL_TWO_FACTOR_AUTH
check using derivedRecipientActionAuth.
This commit is contained in:
ephraimduncan
2026-05-12 11:46:11 +00:00
parent 9194884fbe
commit 138d663c25
1959 changed files with 93488 additions and 47038 deletions
+2 -3
View File
@@ -1,6 +1,3 @@
import { TsRestHttpError, fetchRequestHandler } from '@ts-rest/serverless/fetch';
import { Hono } from 'hono';
import { ApiContractV1 } from '@documenso/api/v1/contract';
import { ApiContractV1Implementation } from '@documenso/api/v1/implementation';
import { OpenAPIV1 } from '@documenso/api/v1/openapi';
@@ -10,6 +7,8 @@ import { subscribeHandler } from '@documenso/lib/server-only/webhooks/zapier/sub
import { unsubscribeHandler } from '@documenso/lib/server-only/webhooks/zapier/unsubscribe';
// This is a bit nasty. Todo: Extract
import type { HonoEnv } from '@documenso/remix/server/router';
import { fetchRequestHandler, TsRestHttpError } from '@ts-rest/serverless/fetch';
import { Hono } from 'hono';
// This is bad, ts-router will be created on each request.
// But don't really have a choice here.
+1 -3
View File
@@ -5,8 +5,6 @@
"types": "./index.ts",
"license": "MIT",
"scripts": {
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"clean": "rimraf node_modules"
},
"files": [
@@ -26,4 +24,4 @@
"ts-pattern": "^5.9.0",
"zod": "^3.25.76"
}
}
}
+1 -1
View File
@@ -3,6 +3,6 @@
"include": ["."],
"exclude": ["dist", "build", "node_modules"],
"compilerOptions": {
"strict": true,
"strict": true
}
}
+1 -2
View File
@@ -1,9 +1,8 @@
import { initContract } from '@ts-rest/core';
import {
ZCreateTemplateV2RequestSchema,
ZCreateTemplateV2ResponseSchema,
} from '@documenso/trpc/server/template-router/schema';
import { initContract } from '@ts-rest/core';
import {
ZAuthorizationHeadersSchema,
+199 -224
View File
@@ -1,24 +1,20 @@
import { DocumentDataType, EnvelopeType, SigningStatus } from '@prisma/client';
import { tsr } from '@ts-rest/serverless/fetch';
import { match } from 'ts-pattern';
import { getServerLimits } from '@documenso/ee/server-only/limits/server';
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
import { DATE_FORMATS, DEFAULT_DOCUMENT_DATE_FORMAT } from '@documenso/lib/constants/date-formats';
import { DocumentDataType, EnvelopeType, SigningStatus } from '@prisma/client';
import { tsr } from '@ts-rest/serverless/fetch';
import { match } from 'ts-pattern';
import '@documenso/lib/constants/time-zones';
import { DEFAULT_DOCUMENT_TIME_ZONE, TIME_ZONES } from '@documenso/lib/constants/time-zones';
import { AppError } from '@documenso/lib/errors/app-error';
import { createDocumentData } from '@documenso/lib/server-only/document-data/create-document-data';
import { updateDocumentMeta } from '@documenso/lib/server-only/document-meta/upsert-document-meta';
import { deleteDocument } from '@documenso/lib/server-only/document/delete-document';
import { findDocuments } from '@documenso/lib/server-only/document/find-documents';
import { resendDocument } from '@documenso/lib/server-only/document/resend-document';
import { sendDocument } from '@documenso/lib/server-only/document/send-document';
import { createDocumentData } from '@documenso/lib/server-only/document-data/create-document-data';
import { updateDocumentMeta } from '@documenso/lib/server-only/document-meta/upsert-document-meta';
import { createEnvelope } from '@documenso/lib/server-only/envelope/create-envelope';
import {
getEnvelopeById,
getEnvelopeWhereInput,
} from '@documenso/lib/server-only/envelope/get-envelope-by-id';
import { getEnvelopeById, getEnvelopeWhereInput } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
import { deleteDocumentField } from '@documenso/lib/server-only/field/delete-document-field';
import { updateEnvelopeFields } from '@documenso/lib/server-only/field/update-envelope-fields';
import { insertFormValuesInPdf } from '@documenso/lib/server-only/pdf/insert-form-values-in-pdf';
@@ -42,16 +38,10 @@ import {
} from '@documenso/lib/types/field-meta';
import { getFileServerSide } from '@documenso/lib/universal/upload/get-file.server';
import { putNormalizedPdfFileServerSide } from '@documenso/lib/universal/upload/put-file.server';
import {
getPresignGetUrl,
getPresignPostUrl,
} from '@documenso/lib/universal/upload/server-actions';
import { getPresignGetUrl, getPresignPostUrl } from '@documenso/lib/universal/upload/server-actions';
import { isDocumentCompleted } from '@documenso/lib/utils/document';
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
import {
mapSecondaryIdToDocumentId,
mapSecondaryIdToTemplateId,
} from '@documenso/lib/utils/envelope';
import { mapSecondaryIdToDocumentId, mapSecondaryIdToTemplateId } from '@documenso/lib/utils/envelope';
import { prisma } from '@documenso/prisma';
import { ApiContractV1 } from './contract';
@@ -67,6 +57,7 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
perPage,
userId: user.id,
teamId: team.id,
folderId: args.query.folderId,
});
return {
@@ -77,6 +68,7 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
externalId: document.externalId,
userId: document.userId,
teamId: document.teamId,
folderId: document.folderId,
title: document.title,
status: document.status,
createdAt: document.createdAt,
@@ -164,6 +156,7 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
externalId: envelope.externalId,
userId: envelope.userId,
teamId: envelope.teamId,
folderId: envelope.folderId,
title: envelope.title,
status: envelope.status,
createdAt: envelope.createdAt,
@@ -524,9 +517,7 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
};
}
const timezone = meta?.timezone
? TIME_ZONES.find((tz) => tz === meta?.timezone)
: DEFAULT_DOCUMENT_TIME_ZONE;
const timezone = meta?.timezone ? TIME_ZONES.find((tz) => tz === meta?.timezone) : DEFAULT_DOCUMENT_TIME_ZONE;
const isTimeZoneValid = meta?.timezone ? TIME_ZONES.includes(String(timezone)) : true;
@@ -725,64 +716,176 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
}
}),
createDocumentFromTemplate: authenticatedMiddleware(
async (args, user, team, { logger, metadata }) => {
const { body, params } = args;
createDocumentFromTemplate: authenticatedMiddleware(async (args, user, team, { logger, metadata }) => {
const { body, params } = args;
logger.info({
input: {
templateId: params.templateId,
logger.info({
input: {
templateId: params.templateId,
},
});
const { remaining } = await getServerLimits({ userId: user.id, teamId: team.id });
if (remaining.documents <= 0) {
return {
status: 400,
body: {
message: 'You have reached the maximum number of documents allowed for this month',
},
});
};
}
const { remaining } = await getServerLimits({ userId: user.id, teamId: team.id });
const templateId = Number(params.templateId);
if (remaining.documents <= 0) {
return {
status: 400,
body: {
message: 'You have reached the maximum number of documents allowed for this month',
},
};
const fileName = body.title.endsWith('.pdf') ? body.title : `${body.title}.pdf`;
const template = await getEnvelopeById({
id: {
type: 'templateId',
id: templateId,
},
type: EnvelopeType.TEMPLATE,
userId: user.id,
teamId: team.id,
});
if (template.envelopeItems.length !== 1) {
throw new Error('API V1 does not support templates with multiple documents');
}
// V1 API request schema uses indices for recipients
// So we remap the recipients to attach the IDs
const mappedRecipients = body.recipients.map((recipient, index) => {
const existingRecipient = template.recipients.at(index);
if (!existingRecipient) {
throw new Error('Recipient not found.');
}
const templateId = Number(params.templateId);
return {
id: existingRecipient.id,
name: recipient.name,
email: recipient.email,
signingOrder: recipient.signingOrder,
role: recipient.role, // You probably shouldn't be able to change the role.
};
});
const fileName = body.title.endsWith('.pdf') ? body.title : `${body.title}.pdf`;
const createdEnvelope = await createDocumentFromTemplate({
id: {
type: 'templateId',
id: templateId,
},
externalId: body.externalId || null,
userId: user.id,
teamId: team.id,
recipients: mappedRecipients,
override: {
...body.meta,
title: body.title,
},
attachments: body.attachments,
formValues: body.formValues,
requestMetadata: metadata,
});
const template = await getEnvelopeById({
id: {
type: 'templateId',
id: templateId,
},
type: EnvelopeType.TEMPLATE,
userId: user.id,
teamId: team.id,
const envelopeItems = await prisma.envelopeItem.findMany({
where: {
envelopeId: createdEnvelope.id,
},
include: {
documentData: true,
},
});
const firstEnvelopeItemData = envelopeItems[0].documentData;
if (!firstEnvelopeItemData) {
throw new Error('Document data not found.');
}
if (body.formValues) {
const pdf = await getFileServerSide(firstEnvelopeItemData);
const prefilled = await insertFormValuesInPdf({
pdf: Buffer.from(pdf),
formValues: body.formValues,
});
if (template.envelopeItems.length !== 1) {
throw new Error('API V1 does not support templates with multiple documents');
}
const newDocumentData = await putNormalizedPdfFileServerSide({
name: fileName,
type: 'application/pdf',
arrayBuffer: async () => Promise.resolve(prefilled),
});
// V1 API request schema uses indices for recipients
// So we remap the recipients to attach the IDs
const mappedRecipients = body.recipients.map((recipient, index) => {
const existingRecipient = template.recipients.at(index);
await prisma.envelopeItem.update({
where: {
id: firstEnvelopeItemData.id,
},
data: {
title: body.title || fileName,
documentDataId: newDocumentData.id,
},
});
}
if (!existingRecipient) {
throw new Error('Recipient not found.');
}
if (body.authOptions || body.formValues) {
await prisma.envelope.update({
where: {
id: createdEnvelope.id,
},
data: {
formValues: body.formValues,
authOptions: body.authOptions,
},
});
}
return {
id: existingRecipient.id,
return {
status: 200,
body: {
documentId: mapSecondaryIdToDocumentId(createdEnvelope.secondaryId),
recipients: createdEnvelope.recipients.map((recipient) => ({
recipientId: recipient.id,
name: recipient.name,
email: recipient.email,
token: recipient.token,
role: recipient.role,
signingOrder: recipient.signingOrder,
role: recipient.role, // You probably shouldn't be able to change the role.
};
});
const createdEnvelope = await createDocumentFromTemplate({
signingUrl: `${NEXT_PUBLIC_WEBAPP_URL()}/sign/${recipient.token}`,
})),
},
};
}),
generateDocumentFromTemplate: authenticatedMiddleware(async (args, user, team, { logger, metadata }) => {
const { body, params } = args;
logger.info({
input: {
templateId: params.templateId,
},
});
const { remaining } = await getServerLimits({ userId: user.id, teamId: team.id });
if (remaining.documents <= 0) {
return {
status: 400,
body: {
message: 'You have reached the maximum number of documents allowed for this month',
},
};
}
const templateId = Number(params.templateId);
let envelope: Awaited<ReturnType<typeof createDocumentFromTemplate>> | null = null;
try {
envelope = await createDocumentFromTemplate({
id: {
type: 'templateId',
id: templateId,
@@ -790,165 +893,49 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
externalId: body.externalId || null,
userId: user.id,
teamId: team.id,
recipients: mappedRecipients,
recipients: body.recipients,
prefillFields: body.prefillFields,
folderId: body.folderId,
override: {
...body.meta,
title: body.title,
...body.meta,
},
attachments: body.attachments,
formValues: body.formValues,
requestMetadata: metadata,
});
} catch (err) {
return AppError.toRestAPIError(err);
}
const envelopeItems = await prisma.envelopeItem.findMany({
if (body.authOptions) {
await prisma.envelope.update({
where: {
envelopeId: createdEnvelope.id,
id: envelope.id,
},
include: {
documentData: true,
data: {
authOptions: body.authOptions,
},
});
}
const firstEnvelopeItemData = envelopeItems[0].documentData;
const legacyDocumentId = mapSecondaryIdToDocumentId(envelope.secondaryId);
if (!firstEnvelopeItemData) {
throw new Error('Document data not found.');
}
if (body.formValues) {
const pdf = await getFileServerSide(firstEnvelopeItemData);
const prefilled = await insertFormValuesInPdf({
pdf: Buffer.from(pdf),
formValues: body.formValues,
});
const newDocumentData = await putNormalizedPdfFileServerSide({
name: fileName,
type: 'application/pdf',
arrayBuffer: async () => Promise.resolve(prefilled),
});
await prisma.envelopeItem.update({
where: {
id: firstEnvelopeItemData.id,
},
data: {
title: body.title || fileName,
documentDataId: newDocumentData.id,
},
});
}
if (body.authOptions || body.formValues) {
await prisma.envelope.update({
where: {
id: createdEnvelope.id,
},
data: {
formValues: body.formValues,
authOptions: body.authOptions,
},
});
}
return {
status: 200,
body: {
documentId: mapSecondaryIdToDocumentId(createdEnvelope.secondaryId),
recipients: createdEnvelope.recipients.map((recipient) => ({
recipientId: recipient.id,
name: recipient.name,
email: recipient.email,
token: recipient.token,
role: recipient.role,
signingOrder: recipient.signingOrder,
signingUrl: `${NEXT_PUBLIC_WEBAPP_URL()}/sign/${recipient.token}`,
})),
},
};
},
),
generateDocumentFromTemplate: authenticatedMiddleware(
async (args, user, team, { logger, metadata }) => {
const { body, params } = args;
logger.info({
input: {
templateId: params.templateId,
},
});
const { remaining } = await getServerLimits({ userId: user.id, teamId: team.id });
if (remaining.documents <= 0) {
return {
status: 400,
body: {
message: 'You have reached the maximum number of documents allowed for this month',
},
};
}
const templateId = Number(params.templateId);
let envelope: Awaited<ReturnType<typeof createDocumentFromTemplate>> | null = null;
try {
envelope = await createDocumentFromTemplate({
id: {
type: 'templateId',
id: templateId,
},
externalId: body.externalId || null,
userId: user.id,
teamId: team.id,
recipients: body.recipients,
prefillFields: body.prefillFields,
folderId: body.folderId,
override: {
title: body.title,
...body.meta,
},
formValues: body.formValues,
requestMetadata: metadata,
});
} catch (err) {
return AppError.toRestAPIError(err);
}
if (body.authOptions) {
await prisma.envelope.update({
where: {
id: envelope.id,
},
data: {
authOptions: body.authOptions,
},
});
}
const legacyDocumentId = mapSecondaryIdToDocumentId(envelope.secondaryId);
return {
status: 200,
body: {
documentId: legacyDocumentId,
recipients: envelope.recipients.map((recipient) => ({
recipientId: recipient.id,
name: recipient.name,
email: recipient.email,
token: recipient.token,
role: recipient.role,
signingOrder: recipient.signingOrder,
signingUrl: `${NEXT_PUBLIC_WEBAPP_URL()}/sign/${recipient.token}`,
})),
},
};
},
),
return {
status: 200,
body: {
documentId: legacyDocumentId,
recipients: envelope.recipients.map((recipient) => ({
recipientId: recipient.id,
name: recipient.name,
email: recipient.email,
token: recipient.token,
role: recipient.role,
signingOrder: recipient.signingOrder,
signingUrl: `${NEXT_PUBLIC_WEBAPP_URL()}/sign/${recipient.token}`,
})),
},
};
}),
sendDocument: authenticatedMiddleware(async (args, user, team, { logger, metadata }) => {
const { id: documentId } = args.params;
@@ -1371,22 +1358,13 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
const createdFields = await prisma.$transaction(async (tx) => {
return Promise.all(
fields.map(async (fieldData) => {
const {
recipientId,
type,
pageNumber,
pageWidth,
pageHeight,
pageX,
pageY,
fieldMeta,
} = fieldData;
const { recipientId, type, pageNumber, pageWidth, pageHeight, pageX, pageY, fieldMeta } = fieldData;
if (pageNumber <= 0) {
throw new Error('Invalid page number');
}
const recipient = await prisma.recipient.findFirst({
const recipient = await tx.recipient.findFirst({
where: {
id: Number(recipientId),
envelopeId: envelope.id,
@@ -1401,9 +1379,7 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
throw new Error('Recipient has already signed the document');
}
const advancedField = ['NUMBER', 'RADIO', 'CHECKBOX', 'DROPDOWN', 'TEXT'].includes(
type,
);
const advancedField = ['NUMBER', 'RADIO', 'CHECKBOX', 'DROPDOWN', 'TEXT'].includes(type);
if (advancedField && !fieldMeta) {
throw new Error(
@@ -1507,8 +1483,7 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
updateField: authenticatedMiddleware(async (args, user, team, { logger, metadata }) => {
const { id: documentId, fieldId } = args.params;
const { recipientId, type, pageNumber, pageWidth, pageHeight, pageX, pageY, fieldMeta } =
args.body;
const { recipientId, type, pageNumber, pageWidth, pageHeight, pageX, pageY, fieldMeta } = args.body;
logger.info({
input: {
+3 -4
View File
@@ -1,7 +1,3 @@
import type { Team, User } from '@prisma/client';
import type { TsRestRequest } from '@ts-rest/serverless';
import type { Logger } from 'pino';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { getApiTokenByToken } from '@documenso/lib/server-only/public-api/get-api-token-by-token';
import type { BaseApiLog, RootApiLog } from '@documenso/lib/types/api-logs';
@@ -9,6 +5,9 @@ import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-reques
import { extractRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { nanoid } from '@documenso/lib/universal/id';
import { logger } from '@documenso/lib/utils/logger';
import type { Team, User } from '@prisma/client';
import type { TsRestRequest } from '@ts-rest/serverless';
import type { Logger } from 'pino';
type B = {
// appRoute: any;
+1 -2
View File
@@ -1,6 +1,5 @@
import { generateOpenApi } from '@ts-rest/open-api';
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
import { generateOpenApi } from '@ts-rest/open-api';
import { ApiContractV1 } from './contract';
+36 -48
View File
@@ -1,17 +1,4 @@
import { extendZodWithOpenApi } from '@anatine/zod-openapi';
import {
DocumentDataType,
DocumentDistributionMethod,
DocumentSigningOrder,
FieldType,
ReadStatus,
RecipientRole,
SendStatus,
SigningStatus,
} from '@prisma/client';
import { TemplateType } from '@prisma/client';
import { z } from 'zod';
import { DATE_FORMATS, DEFAULT_DOCUMENT_DATE_FORMAT } from '@documenso/lib/constants/date-formats';
import { SUPPORTED_LANGUAGE_CODES } from '@documenso/lib/constants/i18n';
import { DEFAULT_DOCUMENT_TIME_ZONE, TIME_ZONES } from '@documenso/lib/constants/time-zones';
@@ -24,6 +11,19 @@ import {
import { ZDocumentEmailSettingsSchema } from '@documenso/lib/types/document-email';
import { ZEnvelopeAttachmentTypeSchema } from '@documenso/lib/types/envelope-attachment';
import { ZFieldMetaPrefillFieldsSchema, ZFieldMetaSchema } from '@documenso/lib/types/field-meta';
import { zEmail } from '@documenso/lib/utils/zod';
import {
DocumentDataType,
DocumentDistributionMethod,
DocumentSigningOrder,
FieldType,
ReadStatus,
RecipientRole,
SendStatus,
SigningStatus,
TemplateType,
} from '@prisma/client';
import { z } from 'zod';
extendZodWithOpenApi(z);
@@ -35,6 +35,7 @@ export const ZNoBodyMutationSchema = null;
export const ZGetDocumentsQuerySchema = z.object({
page: z.coerce.number().min(1).optional().default(1),
perPage: z.coerce.number().min(1).optional().default(10),
folderId: z.string().describe('Filter documents by folder ID. When omitted, returns root documents.').optional(),
});
export type TGetDocumentsQuerySchema = z.infer<typeof ZGetDocumentsQuerySchema>;
@@ -48,6 +49,7 @@ export const ZSuccessfulDocumentResponseSchema = z.object({
externalId: z.string().nullish(),
userId: z.number(),
teamId: z.number().nullish(),
folderId: z.string().nullish(),
title: z.string(),
status: z.string(),
createdAt: z.date(),
@@ -78,9 +80,7 @@ export const ZSuccessfulGetDocumentResponseSchema = ZSuccessfulDocumentResponseS
),
});
export type TSuccessfulGetDocumentResponseSchema = z.infer<
typeof ZSuccessfulGetDocumentResponseSchema
>;
export type TSuccessfulGetDocumentResponseSchema = z.infer<typeof ZSuccessfulGetDocumentResponseSchema>;
export type TSuccessfulDocumentResponseSchema = z.infer<typeof ZSuccessfulDocumentResponseSchema>;
@@ -103,9 +103,7 @@ export const ZResendDocumentForSigningMutationSchema = z.object({
recipients: z.array(z.number()),
});
export type TResendDocumentForSigningMutationSchema = z.infer<
typeof ZResendDocumentForSigningMutationSchema
>;
export type TResendDocumentForSigningMutationSchema = z.infer<typeof ZResendDocumentForSigningMutationSchema>;
export const ZSuccessfulResendDocumentResponseSchema = z.object({
message: z.string(),
@@ -145,7 +143,7 @@ export const ZCreateDocumentMutationSchema = z.object({
recipients: z.array(
z.object({
name: z.string().min(1),
email: z.string().email().min(1),
email: zEmail().min(1),
role: z.nativeEnum(RecipientRole).optional().default(RecipientRole.SIGNER),
signingOrder: z.number().nullish(),
}),
@@ -155,16 +153,14 @@ export const ZCreateDocumentMutationSchema = z.object({
subject: z.string(),
message: z.string(),
timezone: z.string().default(DEFAULT_DOCUMENT_TIME_ZONE).openapi({
description:
'The timezone of the date. Must be one of the options listed in the list below.',
description: 'The timezone of the date. Must be one of the options listed in the list below.',
enum: TIME_ZONES,
}),
dateFormat: z
.string()
.default(DEFAULT_DOCUMENT_DATE_FORMAT)
.openapi({
description:
'The format of the date. Must be one of the options listed in the list below.',
description: 'The format of the date. Must be one of the options listed in the list below.',
enum: DATE_FORMATS.map((format) => format.value),
}),
redirectUrl: z.string(),
@@ -219,7 +215,7 @@ export const ZCreateDocumentMutationResponseSchema = z.object({
z.object({
recipientId: z.number(),
name: z.string(),
email: z.string().email().min(1),
email: zEmail().min(1),
token: z.string(),
role: z.nativeEnum(RecipientRole),
signingOrder: z.number().nullish(),
@@ -229,9 +225,7 @@ export const ZCreateDocumentMutationResponseSchema = z.object({
),
});
export type TCreateDocumentMutationResponseSchema = z.infer<
typeof ZCreateDocumentMutationResponseSchema
>;
export type TCreateDocumentMutationResponseSchema = z.infer<typeof ZCreateDocumentMutationResponseSchema>;
export const ZCreateDocumentFromTemplateMutationSchema = z.object({
title: z.string().min(1),
@@ -239,7 +233,7 @@ export const ZCreateDocumentFromTemplateMutationSchema = z.object({
recipients: z.array(
z.object({
name: z.string().min(1),
email: z.string().email().min(1),
email: zEmail().min(1),
role: z.nativeEnum(RecipientRole).optional().default(RecipientRole.SIGNER),
signingOrder: z.number().nullish(),
}),
@@ -283,9 +277,7 @@ export const ZCreateDocumentFromTemplateMutationSchema = z.object({
.optional(),
});
export type TCreateDocumentFromTemplateMutationSchema = z.infer<
typeof ZCreateDocumentFromTemplateMutationSchema
>;
export type TCreateDocumentFromTemplateMutationSchema = z.infer<typeof ZCreateDocumentFromTemplateMutationSchema>;
export const ZCreateDocumentFromTemplateMutationResponseSchema = z.object({
documentId: z.number(),
@@ -294,7 +286,7 @@ export const ZCreateDocumentFromTemplateMutationResponseSchema = z.object({
z.object({
recipientId: z.number(),
name: z.string(),
email: z.string().email().min(1),
email: zEmail().min(1),
token: z.string(),
role: z.nativeEnum(RecipientRole).optional().default(RecipientRole.SIGNER),
signingOrder: z.number().nullish(),
@@ -321,7 +313,7 @@ export const ZGenerateDocumentFromTemplateMutationSchema = z.object({
.array(
z.object({
id: z.number(),
email: z.string().email(),
email: zEmail(),
name: z.string().optional(),
signingOrder: z.number().optional(),
}),
@@ -370,9 +362,7 @@ export const ZGenerateDocumentFromTemplateMutationSchema = z.object({
prefillFields: z.array(ZFieldMetaPrefillFieldsSchema).optional(),
});
export type TGenerateDocumentFromTemplateMutationSchema = z.infer<
typeof ZGenerateDocumentFromTemplateMutationSchema
>;
export type TGenerateDocumentFromTemplateMutationSchema = z.infer<typeof ZGenerateDocumentFromTemplateMutationSchema>;
export const ZGenerateDocumentFromTemplateMutationResponseSchema = z.object({
documentId: z.number(),
@@ -381,7 +371,7 @@ export const ZGenerateDocumentFromTemplateMutationResponseSchema = z.object({
z.object({
recipientId: z.number(),
name: z.string(),
email: z.string().email().min(1),
email: zEmail().min(1),
token: z.string(),
role: z.nativeEnum(RecipientRole),
signingOrder: z.number().nullish(),
@@ -397,7 +387,7 @@ export type TGenerateDocumentFromTemplateMutationResponseSchema = z.infer<
export const ZCreateRecipientMutationSchema = z.object({
name: z.string().min(1),
email: z.string().email().min(1),
email: zEmail().min(1),
role: z.nativeEnum(RecipientRole).optional().default(RecipientRole.SIGNER),
signingOrder: z.number().nullish(),
authOptions: z
@@ -432,13 +422,13 @@ export const ZSuccessfulRecipientResponseSchema = z.object({
// !: This handles the fact that we have null documentId's for templates
// !: while we won't need the default we must add it to satisfy typescript
documentId: z.number().nullish().default(-1),
email: z.string().email().min(1),
email: zEmail().min(1),
name: z.string(),
role: z.nativeEnum(RecipientRole),
signingOrder: z.number().nullish(),
token: z.string(),
// !: Not used for now
// expired: z.string(),
expiresAt: z.date().nullish(),
expirationNotifiedAt: z.date().nullish(),
signedAt: z.date().nullable(),
readStatus: z.nativeEnum(ReadStatus),
signingStatus: z.nativeEnum(SigningStatus),
@@ -463,10 +453,7 @@ const ZCreateFieldSchema = z.object({
fieldMeta: ZFieldMetaSchema.openapi({}),
});
export const ZCreateFieldMutationSchema = z.union([
ZCreateFieldSchema,
z.array(ZCreateFieldSchema).min(1),
]);
export const ZCreateFieldMutationSchema = z.union([ZCreateFieldSchema, z.array(ZCreateFieldSchema).min(1)]);
export type TCreateFieldMutationSchema = z.infer<typeof ZCreateFieldMutationSchema>;
@@ -571,12 +558,13 @@ export const ZRecipientSchema = z.object({
id: z.number(),
documentId: z.number().nullish(),
templateId: z.number().nullish(),
email: z.string().email().min(1),
email: zEmail().min(1),
name: z.string(),
token: z.string(),
signingOrder: z.number().nullish(),
documentDeletedAt: z.date().nullish(),
expired: z.date().nullish(),
expiresAt: z.date().nullish(),
expirationNotifiedAt: z.date().nullish(),
signedAt: z.date().nullish(),
authOptions: z.unknown(),
role: z.nativeEnum(RecipientRole),