fix: merge conflicts

This commit is contained in:
Ephraim Atta-Duncan
2025-02-06 11:47:44 +00:00
783 changed files with 20849 additions and 22470 deletions

View File

@ -8,7 +8,7 @@ import { z } from 'zod';
import { DocumentSource, FieldType } from '@documenso/prisma/client';
import { ZRecipientActionAuthTypesSchema } from './document-auth';
import { ZRecipientAccessAuthTypesSchema, ZRecipientActionAuthTypesSchema } from './document-auth';
export const ZDocumentAuditLogTypeSchema = z.enum([
// Document actions.
@ -29,6 +29,7 @@ export const ZDocumentAuditLogTypeSchema = z.enum([
'DOCUMENT_DELETED', // When the document is soft deleted.
'DOCUMENT_FIELD_INSERTED', // When a field is inserted (signed/approved/etc) by a recipient.
'DOCUMENT_FIELD_UNINSERTED', // When a field is uninserted by a recipient.
'DOCUMENT_FIELD_PREFILLED', // When a field is prefilled by an assistant.
'DOCUMENT_VISIBILITY_UPDATED', // When the document visibility scope is updated
'DOCUMENT_GLOBAL_AUTH_ACCESS_UPDATED', // When the global access authentication is updated.
'DOCUMENT_GLOBAL_AUTH_ACTION_UPDATED', // When the global action authentication is updated.
@ -46,6 +47,7 @@ export const ZDocumentAuditLogEmailTypeSchema = z.enum([
'SIGNING_REQUEST',
'VIEW_REQUEST',
'APPROVE_REQUEST',
'ASSISTING_REQUEST',
'CC',
'DOCUMENT_COMPLETED',
]);
@ -128,11 +130,11 @@ export const ZGenericFromToSchema = z.object({
});
export const ZRecipientDiffActionAuthSchema = ZGenericFromToSchema.extend({
type: z.literal(RECIPIENT_DIFF_TYPE.ACCESS_AUTH),
type: z.literal(RECIPIENT_DIFF_TYPE.ACTION_AUTH),
});
export const ZRecipientDiffAccessAuthSchema = ZGenericFromToSchema.extend({
type: z.literal(RECIPIENT_DIFF_TYPE.ACTION_AUTH),
type: z.literal(RECIPIENT_DIFF_TYPE.ACCESS_AUTH),
});
export const ZRecipientDiffNameSchema = ZGenericFromToSchema.extend({
@ -322,6 +324,83 @@ export const ZDocumentAuditLogEventDocumentFieldUninsertedSchema = z.object({
}),
});
/**
* Event: Document field prefilled by assistant.
*/
export const ZDocumentAuditLogEventDocumentFieldPrefilledSchema = z.object({
type: z.literal(DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FIELD_PREFILLED),
data: ZBaseRecipientDataSchema.extend({
fieldId: z.string(),
// Organised into union to allow us to extend each field if required.
field: z.union([
z.object({
type: z.literal(FieldType.INITIALS),
data: z.string(),
}),
z.object({
type: z.literal(FieldType.EMAIL),
data: z.string(),
}),
z.object({
type: z.literal(FieldType.DATE),
data: z.string(),
}),
z.object({
type: z.literal(FieldType.NAME),
data: z.string(),
}),
z.object({
type: z.literal(FieldType.TEXT),
data: z.string(),
}),
z.object({
type: z.union([z.literal(FieldType.SIGNATURE), z.literal(FieldType.FREE_SIGNATURE)]),
data: z.string(),
}),
z.object({
type: z.literal(FieldType.RADIO),
data: z.string(),
}),
z.object({
type: z.literal(FieldType.CHECKBOX),
data: z.string(),
}),
z.object({
type: z.literal(FieldType.DROPDOWN),
data: z.string(),
}),
z.object({
type: z.literal(FieldType.NUMBER),
data: z.string(),
}),
]),
fieldSecurity: z.preprocess(
(input) => {
const legacyNoneSecurityType = JSON.stringify({
type: 'NONE',
});
// Replace legacy 'NONE' field security type with undefined.
if (
typeof input === 'object' &&
input !== null &&
JSON.stringify(input) === legacyNoneSecurityType
) {
return undefined;
}
return input;
},
z
.object({
type: ZRecipientActionAuthTypesSchema,
})
.optional(),
),
}),
});
export const ZDocumentAuditLogEventDocumentVisibilitySchema = z.object({
type: z.literal(DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_VISIBILITY_UPDATED),
data: ZGenericFromToSchema,
@ -447,6 +526,7 @@ export const ZDocumentAuditLogEventFieldUpdatedSchema = z.object({
export const ZDocumentAuditLogEventRecipientAddedSchema = z.object({
type: z.literal(DOCUMENT_AUDIT_LOG_TYPE.RECIPIENT_CREATED),
data: ZBaseRecipientDataSchema.extend({
accessAuth: ZRecipientAccessAuthTypesSchema.optional(),
actionAuth: ZRecipientActionAuthTypesSchema.optional(),
}),
});
@ -502,6 +582,7 @@ export const ZDocumentAuditLogSchema = ZDocumentAuditLogBaseSchema.and(
ZDocumentAuditLogEventDocumentMovedToTeamSchema,
ZDocumentAuditLogEventDocumentFieldInsertedSchema,
ZDocumentAuditLogEventDocumentFieldUninsertedSchema,
ZDocumentAuditLogEventDocumentFieldPrefilledSchema,
ZDocumentAuditLogEventDocumentVisibilitySchema,
ZDocumentAuditLogEventDocumentGlobalAuthAccessUpdatedSchema,
ZDocumentAuditLogEventDocumentGlobalAuthActionUpdatedSchema,

View File

@ -48,7 +48,9 @@ export const ZDocumentAuthMethodsSchema = z.discriminatedUnion('type', [
* Must keep these two in sync.
*/
export const ZDocumentAccessAuthSchema = z.discriminatedUnion('type', [ZDocumentAuthAccountSchema]);
export const ZDocumentAccessAuthTypesSchema = z.enum([DocumentAuth.ACCOUNT]);
export const ZDocumentAccessAuthTypesSchema = z
.enum([DocumentAuth.ACCOUNT])
.describe('The type of authentication required for the recipient to access the document.');
/**
* The global document action auth methods.
@ -60,11 +62,11 @@ export const ZDocumentActionAuthSchema = z.discriminatedUnion('type', [
ZDocumentAuthPasskeySchema,
ZDocumentAuth2FASchema,
]);
export const ZDocumentActionAuthTypesSchema = z.enum([
DocumentAuth.ACCOUNT,
DocumentAuth.PASSKEY,
DocumentAuth.TWO_FACTOR_AUTH,
]);
export const ZDocumentActionAuthTypesSchema = z
.enum([DocumentAuth.ACCOUNT, DocumentAuth.PASSKEY, DocumentAuth.TWO_FACTOR_AUTH])
.describe(
'The type of authentication required for the recipient to sign the document. This field is restricted to Enterprise plan users only.',
);
/**
* The recipient access auth methods.
@ -74,7 +76,9 @@ export const ZDocumentActionAuthTypesSchema = z.enum([
export const ZRecipientAccessAuthSchema = z.discriminatedUnion('type', [
ZDocumentAuthAccountSchema,
]);
export const ZRecipientAccessAuthTypesSchema = z.enum([DocumentAuth.ACCOUNT]);
export const ZRecipientAccessAuthTypesSchema = z
.enum([DocumentAuth.ACCOUNT])
.describe('The type of authentication required for the recipient to access the document.');
/**
* The recipient action auth methods.
@ -87,12 +91,14 @@ export const ZRecipientActionAuthSchema = z.discriminatedUnion('type', [
ZDocumentAuth2FASchema,
ZDocumentAuthExplicitNoneSchema,
]);
export const ZRecipientActionAuthTypesSchema = z.enum([
DocumentAuth.ACCOUNT,
DocumentAuth.PASSKEY,
DocumentAuth.TWO_FACTOR_AUTH,
DocumentAuth.EXPLICIT_NONE,
]);
export const ZRecipientActionAuthTypesSchema = z
.enum([
DocumentAuth.ACCOUNT,
DocumentAuth.PASSKEY,
DocumentAuth.TWO_FACTOR_AUTH,
DocumentAuth.EXPLICIT_NONE,
])
.describe('The type of authentication required for the recipient to sign the document.');
export const DocumentAccessAuth = ZDocumentAccessAuthTypesSchema.Enum;
export const DocumentActionAuth = ZDocumentActionAuthTypesSchema.Enum;

View File

@ -6,26 +6,63 @@ import { DocumentDistributionMethod } from '@documenso/prisma/client';
export enum DocumentEmailEvents {
RecipientSigningRequest = 'recipientSigningRequest',
RecipientRemoved = 'recipientRemoved',
RecipientSigned = 'recipientSigned',
DocumentPending = 'documentPending',
DocumentCompleted = 'documentCompleted',
DocumentDeleted = 'documentDeleted',
OwnerDocumentCompleted = 'ownerDocumentCompleted',
}
export const ZDocumentEmailSettingsSchema = z
.object({
recipientSigningRequest: z.boolean().default(true),
recipientRemoved: z.boolean().default(true),
documentPending: z.boolean().default(true),
documentCompleted: z.boolean().default(true),
documentDeleted: z.boolean().default(true),
recipientSigningRequest: z
.boolean()
.describe(
'Whether to send an email to all recipients that the document is ready for them to sign.',
)
.default(true),
recipientRemoved: z
.boolean()
.describe(
'Whether to send an email to the recipient who was removed from a pending document.',
)
.default(true),
recipientSigned: z
.boolean()
.describe(
'Whether to send an email to the document owner when a recipient has signed the document.',
)
.default(true),
documentPending: z
.boolean()
.describe(
'Whether to send an email to the recipient who has just signed the document indicating that there are still other recipients who need to sign the document. This will only be sent if the document is still pending after the recipient has signed.',
)
.default(true),
documentCompleted: z
.boolean()
.describe('Whether to send an email to all recipients when the document is complete.')
.default(true),
documentDeleted: z
.boolean()
.describe(
'Whether to send an email to all recipients if a pending document has been deleted.',
)
.default(true),
ownerDocumentCompleted: z
.boolean()
.describe('Whether to send an email to the document owner when the document is complete.')
.default(true),
})
.strip()
.catch(() => ({
recipientSigningRequest: true,
recipientRemoved: true,
recipientSigned: true,
documentPending: true,
documentCompleted: true,
documentDeleted: true,
ownerDocumentCompleted: true,
}));
export type TDocumentEmailSettings = z.infer<typeof ZDocumentEmailSettingsSchema>;
@ -45,8 +82,10 @@ export const extractDerivedDocumentEmailSettings = (
return {
recipientSigningRequest: false,
recipientRemoved: false,
recipientSigned: false,
documentPending: false,
documentCompleted: false,
documentDeleted: false,
ownerDocumentCompleted: emailSettings.ownerDocumentCompleted,
};
};

View File

@ -0,0 +1,8 @@
import { z } from 'zod';
export const ZDocumentFormValuesSchema = z.record(
z.string(),
z.union([z.string(), z.boolean(), z.number()]),
);
export type TDocumentFormValues = z.infer<typeof ZDocumentFormValuesSchema>;

View File

@ -0,0 +1,118 @@
import type { z } from 'zod';
import {
DocumentDataSchema,
DocumentMetaSchema,
DocumentSchema,
TeamSchema,
UserSchema,
} from '@documenso/prisma/generated/zod';
import { ZFieldSchema } from './field';
import { ZRecipientLiteSchema } from './recipient';
/**
* The full document response schema.
*
* Mainly used for returning a single document from the API.
*/
export const ZDocumentSchema = DocumentSchema.pick({
visibility: true,
status: true,
source: true,
id: true,
externalId: true,
userId: true,
authOptions: true,
formValues: true,
title: true,
documentDataId: true,
createdAt: true,
updatedAt: true,
completedAt: true,
deletedAt: true,
teamId: true,
templateId: true,
}).extend({
// Todo: Maybe we want to alter this a bit since this returns a lot of data.
documentData: DocumentDataSchema.pick({
type: true,
id: true,
data: true,
initialData: true,
}),
documentMeta: DocumentMetaSchema.pick({
signingOrder: true,
distributionMethod: true,
id: true,
subject: true,
message: true,
timezone: true,
password: true,
dateFormat: true,
documentId: true,
redirectUrl: true,
typedSignatureEnabled: true,
language: true,
emailSettings: true,
}).nullable(),
recipients: ZRecipientLiteSchema.array(),
fields: ZFieldSchema.array(),
});
export type TDocument = z.infer<typeof ZDocumentSchema>;
/**
* A lite version of the document response schema without relations.
*/
export const ZDocumentLiteSchema = DocumentSchema.pick({
visibility: true,
status: true,
source: true,
id: true,
externalId: true,
userId: true,
authOptions: true,
formValues: true,
title: true,
documentDataId: true,
createdAt: true,
updatedAt: true,
completedAt: true,
deletedAt: true,
teamId: true,
templateId: true,
});
/**
* A version of the document response schema when returning multiple documents at once from a single API endpoint.
*/
export const ZDocumentManySchema = DocumentSchema.pick({
visibility: true,
status: true,
source: true,
id: true,
externalId: true,
userId: true,
authOptions: true,
formValues: true,
title: true,
documentDataId: true,
createdAt: true,
updatedAt: true,
completedAt: true,
deletedAt: true,
teamId: true,
templateId: true,
}).extend({
user: UserSchema.pick({
id: true,
name: true,
email: true,
}),
recipients: ZRecipientLiteSchema.array(),
team: TeamSchema.pick({
id: true,
url: true,
}).nullable(),
});

View File

@ -1,5 +1,7 @@
import { z } from 'zod';
import { FieldType } from '@documenso/prisma/client';
export const ZBaseFieldMeta = z.object({
label: z.string().optional(),
placeholder: z.string().optional(),
@ -9,56 +11,66 @@ export const ZBaseFieldMeta = z.object({
export type TBaseFieldMeta = z.infer<typeof ZBaseFieldMeta>;
export const ZInitialsFieldMeta = z.object({
type: z.literal('initials').default('initials'),
export const ZFieldTextAlignSchema = z.enum(['left', 'center', 'right']);
export type TFieldTextAlignSchema = z.infer<typeof ZFieldTextAlignSchema>;
export const ZInitialsFieldMeta = ZBaseFieldMeta.extend({
type: z.literal('initials'),
fontSize: z.number().min(8).max(96).optional(),
textAlign: ZFieldTextAlignSchema.optional(),
});
export type TInitialsFieldMeta = z.infer<typeof ZInitialsFieldMeta>;
export const ZNameFieldMeta = z.object({
type: z.literal('name').default('name'),
export const ZNameFieldMeta = ZBaseFieldMeta.extend({
type: z.literal('name'),
fontSize: z.number().min(8).max(96).optional(),
textAlign: ZFieldTextAlignSchema.optional(),
});
export type TNameFieldMeta = z.infer<typeof ZNameFieldMeta>;
export const ZEmailFieldMeta = z.object({
type: z.literal('email').default('email'),
export const ZEmailFieldMeta = ZBaseFieldMeta.extend({
type: z.literal('email'),
fontSize: z.number().min(8).max(96).optional(),
textAlign: ZFieldTextAlignSchema.optional(),
});
export type TEmailFieldMeta = z.infer<typeof ZEmailFieldMeta>;
export const ZDateFieldMeta = z.object({
type: z.literal('date').default('date'),
export const ZDateFieldMeta = ZBaseFieldMeta.extend({
type: z.literal('date'),
fontSize: z.number().min(8).max(96).optional(),
textAlign: ZFieldTextAlignSchema.optional(),
});
export type TDateFieldMeta = z.infer<typeof ZDateFieldMeta>;
export const ZTextFieldMeta = ZBaseFieldMeta.extend({
type: z.literal('text').default('text'),
type: z.literal('text'),
text: z.string().optional(),
characterLimit: z.number().optional(),
fontSize: z.number().min(8).max(96).optional(),
textAlign: ZFieldTextAlignSchema.optional(),
});
export type TTextFieldMeta = z.infer<typeof ZTextFieldMeta>;
export const ZNumberFieldMeta = ZBaseFieldMeta.extend({
type: z.literal('number').default('number'),
type: z.literal('number'),
numberFormat: z.string().optional(),
value: z.string().optional(),
minValue: z.number().optional(),
maxValue: z.number().optional(),
fontSize: z.number().min(8).max(96).optional(),
textAlign: ZFieldTextAlignSchema.optional(),
});
export type TNumberFieldMeta = z.infer<typeof ZNumberFieldMeta>;
export const ZRadioFieldMeta = ZBaseFieldMeta.extend({
type: z.literal('radio').default('radio'),
type: z.literal('radio'),
values: z
.array(
z.object({
@ -73,7 +85,7 @@ export const ZRadioFieldMeta = ZBaseFieldMeta.extend({
export type TRadioFieldMeta = z.infer<typeof ZRadioFieldMeta>;
export const ZCheckboxFieldMeta = ZBaseFieldMeta.extend({
type: z.literal('checkbox').default('checkbox'),
type: z.literal('checkbox'),
values: z
.array(
z.object({
@ -90,25 +102,85 @@ export const ZCheckboxFieldMeta = ZBaseFieldMeta.extend({
export type TCheckboxFieldMeta = z.infer<typeof ZCheckboxFieldMeta>;
export const ZDropdownFieldMeta = ZBaseFieldMeta.extend({
type: z.literal('dropdown').default('dropdown'),
type: z.literal('dropdown'),
values: z.array(z.object({ value: z.string() })).optional(),
defaultValue: z.string().optional(),
});
export type TDropdownFieldMeta = z.infer<typeof ZDropdownFieldMeta>;
export const ZFieldMetaNotOptionalSchema = z.discriminatedUnion('type', [
ZInitialsFieldMeta,
ZNameFieldMeta,
ZEmailFieldMeta,
ZDateFieldMeta,
ZTextFieldMeta,
ZNumberFieldMeta,
ZRadioFieldMeta,
ZCheckboxFieldMeta,
ZDropdownFieldMeta,
]);
export type TFieldMetaNotOptionalSchema = z.infer<typeof ZFieldMetaNotOptionalSchema>;
export const ZFieldMetaSchema = z
.union([
ZBaseFieldMeta.extend(ZInitialsFieldMeta.shape),
ZBaseFieldMeta.extend(ZNameFieldMeta.shape),
ZBaseFieldMeta.extend(ZEmailFieldMeta.shape),
ZBaseFieldMeta.extend(ZDateFieldMeta.shape),
ZTextFieldMeta,
ZNumberFieldMeta,
ZRadioFieldMeta,
ZCheckboxFieldMeta,
ZDropdownFieldMeta,
// Handles an empty object being provided as fieldMeta.
z
.object({})
.strict()
.transform(() => undefined),
ZFieldMetaNotOptionalSchema,
])
.optional();
export type TFieldMetaSchema = z.infer<typeof ZFieldMetaSchema>;
export const ZFieldAndMetaSchema = z.discriminatedUnion('type', [
z.object({
type: z.literal(FieldType.SIGNATURE),
fieldMeta: z.undefined(),
}),
z.object({
type: z.literal(FieldType.FREE_SIGNATURE),
fieldMeta: z.undefined(),
}),
z.object({
type: z.literal(FieldType.INITIALS),
fieldMeta: ZInitialsFieldMeta.optional(),
}),
z.object({
type: z.literal(FieldType.NAME),
fieldMeta: ZNameFieldMeta.optional(),
}),
z.object({
type: z.literal(FieldType.EMAIL),
fieldMeta: ZEmailFieldMeta.optional(),
}),
z.object({
type: z.literal(FieldType.DATE),
fieldMeta: ZDateFieldMeta.optional(),
}),
z.object({
type: z.literal(FieldType.TEXT),
fieldMeta: ZTextFieldMeta.optional(),
}),
z.object({
type: z.literal(FieldType.NUMBER),
fieldMeta: ZNumberFieldMeta.optional(),
}),
z.object({
type: z.literal(FieldType.RADIO),
fieldMeta: ZRadioFieldMeta.optional(),
}),
z.object({
type: z.literal(FieldType.CHECKBOX),
fieldMeta: ZCheckboxFieldMeta.optional(),
}),
z.object({
type: z.literal(FieldType.DROPDOWN),
fieldMeta: ZDropdownFieldMeta.optional(),
}),
]);
export type TFieldAndMeta = z.infer<typeof ZFieldAndMetaSchema>;

View File

@ -0,0 +1,51 @@
import { z } from 'zod';
import { FieldSchema } from '@documenso/prisma/generated/zod';
/**
* The full field response schema.
*
* If you need to return something different, adjust this file to utilise the:
* - ZFieldSchema
* - ZFieldLiteSchema
* - ZFieldManySchema
*
* Setup similar to:
* - ./documents.ts
* - ./templates.ts
*/
export const ZFieldSchema = FieldSchema.pick({
type: true,
id: true,
secondaryId: true,
documentId: true,
templateId: true,
recipientId: true,
page: true,
positionX: true,
positionY: true,
width: true,
height: true,
customText: true,
inserted: true,
fieldMeta: true,
});
export const ZFieldPageNumberSchema = z
.number()
.min(1)
.describe('The page number the field will be on.');
export const ZFieldPageXSchema = z
.number()
.min(0)
.describe('The X coordinate of where the field will be placed.');
export const ZFieldPageYSchema = z
.number()
.min(0)
.describe('The Y coordinate of where the field will be placed.');
export const ZFieldWidthSchema = z.number().min(1).describe('The width of the field.');
export const ZFieldHeightSchema = z.number().min(1).describe('The height of the field.');

View File

@ -1,7 +0,0 @@
export type FindResultSet<T> = {
data: T extends Array<unknown> ? T : T[];
count: number;
currentPage: number;
perPage: number;
totalPages: number;
};

View File

@ -0,0 +1,85 @@
import { TeamSchema, UserSchema } from '@documenso/prisma/generated/zod';
import RecipientSchema from '@documenso/prisma/generated/zod/modelSchema/RecipientSchema';
import { ZFieldSchema } from './field';
/**
* The full recipient response schema.
*
* Mainly used for returning a single recipient from the API.
*/
export const ZRecipientSchema = RecipientSchema.pick({
role: true,
readStatus: true,
signingStatus: true,
sendStatus: true,
id: true,
documentId: true,
templateId: true,
email: true,
name: true,
token: true,
documentDeletedAt: true,
expired: true,
signedAt: true,
authOptions: true,
signingOrder: true,
rejectionReason: true,
}).extend({
fields: ZFieldSchema.array(),
});
/**
* A lite version of the recipient response schema without relations.
*/
export const ZRecipientLiteSchema = RecipientSchema.pick({
role: true,
readStatus: true,
signingStatus: true,
sendStatus: true,
id: true,
documentId: true,
templateId: true,
email: true,
name: true,
token: true,
documentDeletedAt: true,
expired: true,
signedAt: true,
authOptions: true,
signingOrder: true,
rejectionReason: true,
});
/**
* A version of the recipient response schema when returning multiple recipients at once from a single API endpoint.
*/
export const ZRecipientManySchema = RecipientSchema.pick({
role: true,
readStatus: true,
signingStatus: true,
sendStatus: true,
id: true,
documentId: true,
templateId: true,
email: true,
name: true,
token: true,
documentDeletedAt: true,
expired: true,
signedAt: true,
authOptions: true,
signingOrder: true,
rejectionReason: true,
}).extend({
user: UserSchema.pick({
id: true,
name: true,
email: true,
}),
recipients: RecipientSchema.array(),
team: TeamSchema.pick({
id: true,
url: true,
}).nullable(),
});

View File

@ -1,6 +1,24 @@
import { z } from 'zod';
export const ZBaseTableSearchParamsSchema = z.object({
/**
* Backend only schema is used for find search params.
*
* Does not catch, because TRPC Open API won't allow catches as a type.
*
* Keep this and `ZUrlSearchParamsSchema` in sync.
*/
export const ZFindSearchParamsSchema = z.object({
query: z.string().describe('The search query.').optional(),
page: z.coerce.number().min(1).describe('The pagination page number, starts at 1.').optional(),
perPage: z.coerce.number().min(1).describe('The number of items per page.').max(100).optional(),
});
/**
* Frontend schema used to parse search params from URL.
*
* Keep this and `ZFindSearchParamsSchema` in sync.
*/
export const ZUrlSearchParamsSchema = z.object({
query: z
.string()
.optional()
@ -13,8 +31,24 @@ export const ZBaseTableSearchParamsSchema = z.object({
perPage: z.coerce
.number()
.min(1)
.max(100)
.optional()
.catch(() => undefined),
});
export type TBaseTableSearchParamsSchema = z.infer<typeof ZBaseTableSearchParamsSchema>;
export const ZFindResultResponse = z.object({
data: z.union([z.array(z.unknown()), z.unknown()]).describe('The results from the search.'),
count: z.number().describe('The total number of items.'),
currentPage: z.number().describe('The current page number, starts at 1.'),
perPage: z.number().describe('The number of items per page.'),
totalPages: z.number().describe('The total number of pages.'),
});
// Can't infer generics from Zod.
export type FindResultResponse<T> = {
data: T extends Array<unknown> ? T : T[];
count: number;
currentPage: number;
perPage: number;
totalPages: number;
};

View File

@ -0,0 +1,119 @@
import type { z } from 'zod';
import {
DocumentDataSchema,
TeamSchema,
TemplateDirectLinkSchema,
TemplateMetaSchema,
TemplateSchema,
UserSchema,
} from '@documenso/prisma/generated/zod';
import { ZFieldSchema } from './field';
import { ZRecipientLiteSchema } from './recipient';
/**
* The full template response schema.
*
* Mainly used for returning a single template from the API.
*/
export const ZTemplateSchema = TemplateSchema.pick({
type: true,
visibility: true,
id: true,
externalId: true,
title: true,
userId: true,
teamId: true,
authOptions: true,
templateDocumentDataId: true,
createdAt: true,
updatedAt: true,
publicTitle: true,
publicDescription: true,
}).extend({
// Todo: Maybe we want to alter this a bit since this returns a lot of data.
templateDocumentData: DocumentDataSchema.pick({
type: true,
id: true,
data: true,
initialData: true,
}),
templateMeta: TemplateMetaSchema.pick({
id: true,
subject: true,
message: true,
timezone: true,
dateFormat: true,
signingOrder: true,
typedSignatureEnabled: true,
distributionMethod: true,
templateId: true,
redirectUrl: true,
language: true,
emailSettings: true,
}).nullable(),
directLink: TemplateDirectLinkSchema.nullable(),
user: UserSchema.pick({
id: true,
name: true,
email: true,
}),
recipients: ZRecipientLiteSchema.array(),
fields: ZFieldSchema.array(),
});
export type TTemplate = z.infer<typeof ZTemplateSchema>;
/**
* A lite version of the template response schema without relations.
*/
export const ZTemplateLiteSchema = TemplateSchema.pick({
type: true,
visibility: true,
id: true,
externalId: true,
title: true,
userId: true,
teamId: true,
authOptions: true,
templateDocumentDataId: true,
createdAt: true,
updatedAt: true,
publicTitle: true,
publicDescription: true,
});
/**
* A version of the template response schema when returning multiple template at once from a single API endpoint.
*/
export const ZTemplateManySchema = TemplateSchema.pick({
type: true,
visibility: true,
id: true,
externalId: true,
title: true,
userId: true,
teamId: true,
authOptions: true,
templateDocumentDataId: true,
createdAt: true,
updatedAt: true,
publicTitle: true,
publicDescription: true,
}).extend({
team: TeamSchema.pick({
id: true,
url: true,
}).nullable(),
fields: ZFieldSchema.array(),
recipients: ZRecipientLiteSchema.array(),
templateMeta: TemplateMetaSchema.pick({
signingOrder: true,
distributionMethod: true,
}).nullable(),
directLink: TemplateDirectLinkSchema.pick({
token: true,
enabled: true,
}).nullable(),
});

View File

@ -0,0 +1,109 @@
import { z } from 'zod';
import type { Document, DocumentMeta, Recipient } from '@documenso/prisma/client';
import {
DocumentDistributionMethod,
DocumentSigningOrder,
DocumentSource,
DocumentStatus,
DocumentVisibility,
ReadStatus,
RecipientRole,
SendStatus,
SigningStatus,
} from '@documenso/prisma/client';
/**
* Schema for recipient data in webhook payloads.
*/
export const ZWebhookRecipientSchema = z.object({
id: z.number(),
documentId: z.number().nullable(),
templateId: z.number().nullable(),
email: z.string(),
name: z.string(),
token: z.string(),
documentDeletedAt: z.date().nullable(),
expired: z.date().nullable(),
signedAt: z.date().nullable(),
authOptions: z.any().nullable(),
signingOrder: z.number().nullable(),
rejectionReason: z.string().nullable(),
role: z.nativeEnum(RecipientRole),
readStatus: z.nativeEnum(ReadStatus),
signingStatus: z.nativeEnum(SigningStatus),
sendStatus: z.nativeEnum(SendStatus),
});
/**
* Schema for document meta in webhook payloads.
*/
export const ZWebhookDocumentMetaSchema = z.object({
id: z.string(),
subject: z.string().nullable(),
message: z.string().nullable(),
timezone: z.string(),
password: z.string().nullable(),
dateFormat: z.string(),
redirectUrl: z.string().nullable(),
signingOrder: z.nativeEnum(DocumentSigningOrder),
typedSignatureEnabled: z.boolean(),
language: z.string(),
distributionMethod: z.nativeEnum(DocumentDistributionMethod),
emailSettings: z.any().nullable(),
});
/**
* Schema for document data in webhook payloads.
*/
export const ZWebhookDocumentSchema = z.object({
id: z.number(),
externalId: z.string().nullable(),
userId: z.number(),
authOptions: z.any().nullable(),
formValues: z.any().nullable(),
visibility: z.nativeEnum(DocumentVisibility),
title: z.string(),
status: z.nativeEnum(DocumentStatus),
documentDataId: z.string(),
createdAt: z.date(),
updatedAt: z.date(),
completedAt: z.date().nullable(),
deletedAt: z.date().nullable(),
teamId: z.number().nullable(),
templateId: z.number().nullable(),
source: z.nativeEnum(DocumentSource),
documentMeta: ZWebhookDocumentMetaSchema.nullable(),
recipients: z.array(ZWebhookRecipientSchema),
/**
* Legacy field for backwards compatibility.
*/
Recipient: z.array(ZWebhookRecipientSchema),
});
export type TWebhookRecipient = z.infer<typeof ZWebhookRecipientSchema>;
export type TWebhookDocument = z.infer<typeof ZWebhookDocumentSchema>;
export const mapDocumentToWebhookDocumentPayload = (
document: Document & {
recipients: Recipient[];
documentMeta: DocumentMeta | null;
},
): TWebhookDocument => {
const { recipients, documentMeta, ...trimmedDocument } = document;
return {
...trimmedDocument,
documentMeta: documentMeta
? {
...documentMeta,
// Not sure why is optional in the prisma schema.
timezone: 'Etc/UTC',
dateFormat: 'yyyy-MM-dd hh:mm a',
}
: null,
Recipient: recipients,
recipients,
};
};