chore: merge main, resolve biome formatting conflicts

Merge origin/main into feat/document-file-conversion. Conflicts were
format-only (Tailwind class ordering, single-line vs multi-line) plus
two semantic merges:

- files.helpers.ts: combine main's pending-PDF download path with the
  branch's original-source-file (DOCX/PNG/JPEG) download path
- download-pdf.ts: combine main's versionToFilenameSuffix helper with
  the branch's server-provided Content-Disposition filename support
This commit is contained in:
ephraimduncan
2026-05-12 11:28:47 +00:00
1495 changed files with 22068 additions and 33465 deletions
+2 -5
View File
@@ -75,9 +75,7 @@ export const prismaWithReplicas = remember('prismaWithReplicas', () => {
return prisma;
}
const replicaUrls = process.env.NEXT_PRIVATE_DATABASE_REPLICA_URLS.split(',').map((url) =>
url.trim(),
);
const replicaUrls = process.env.NEXT_PRIVATE_DATABASE_REPLICA_URLS.split(',').map((url) => url.trim());
// !: Nasty hack, means we can't do any fancy $primary/$replica queries
// !: but it is acceptable since not all setups will have replicas anyway.
@@ -89,6 +87,5 @@ export const prismaWithReplicas = remember('prismaWithReplicas', () => {
) as unknown as typeof prisma;
});
export { prismaWithReplicas as prisma };
export { sql } from 'kysely';
export { prismaWithReplicas as prisma };
@@ -0,0 +1,10 @@
-- AlterTable
-- Add the column with a temporary default of `true` so that all existing rows
-- (representing organisations created before this feature) are backfilled to
-- `true` — preserving the historical behaviour of creating personal
-- organisations for SSO-provisioned users.
ALTER TABLE "OrganisationAuthenticationPortal" ADD COLUMN "allowPersonalOrganisations" BOOLEAN NOT NULL DEFAULT true;
-- Switch the column default to `false` so that any organisations created from
-- now on opt out of personal-organisation creation by default.
ALTER TABLE "OrganisationAuthenticationPortal" ALTER COLUMN "allowPersonalOrganisations" SET DEFAULT false;
@@ -0,0 +1,7 @@
-- AlterTable
ALTER TABLE "OrganisationGlobalSettings" ADD COLUMN "brandingColors" JSONB,
ADD COLUMN "brandingCss" TEXT NOT NULL DEFAULT '';
-- AlterTable
ALTER TABLE "TeamGlobalSettings" ADD COLUMN "brandingColors" JSONB,
ADD COLUMN "brandingCss" TEXT;
+1 -1
View File
@@ -22,7 +22,7 @@
},
"dependencies": {
"@prisma/client": "^6.19.0",
"kysely": "0.28.8",
"kysely": "0.28.16",
"nanoid": "^5.1.6",
"prisma": "^6.19.0",
"prisma-extension-kysely": "^3.0.0",
+10 -5
View File
@@ -833,7 +833,7 @@ enum OrganisationMemberInviteStatus {
DECLINED
}
/// @zod.import(["import { ZDocumentEmailSettingsSchema } from '@documenso/lib/types/document-email';", "import { ZDefaultRecipientsSchema } from '@documenso/lib/types/default-recipients';", "import { ZEnvelopeExpirationPeriod as ZEnvelopeExpirationPeriodSchema } from '@documenso/lib/constants/envelope-expiration';", "import { ZEnvelopeReminderSettings as ZEnvelopeReminderSettingsSchema } from '@documenso/lib/constants/envelope-reminder';"])
/// @zod.import(["import { ZDocumentEmailSettingsSchema } from '@documenso/lib/types/document-email';", "import { ZDefaultRecipientsSchema } from '@documenso/lib/types/default-recipients';", "import { ZEnvelopeExpirationPeriod as ZEnvelopeExpirationPeriodSchema } from '@documenso/lib/constants/envelope-expiration';", "import { ZEnvelopeReminderSettings as ZEnvelopeReminderSettingsSchema } from '@documenso/lib/constants/envelope-reminder';", "import { ZCssVarsSchema } from '@documenso/lib/types/css-vars';"])
model OrganisationGlobalSettings {
id String @id
organisation Organisation?
@@ -864,6 +864,8 @@ model OrganisationGlobalSettings {
brandingLogo String @default("")
brandingUrl String @default("")
brandingCompanyDetails String @default("")
brandingColors Json? /// [TCssVarsSchema] @zod.custom.use(ZCssVarsSchema)
brandingCss String @default("")
envelopeExpirationPeriod Json? /// [EnvelopeExpirationPeriod] @zod.custom.use(ZEnvelopeExpirationPeriodSchema)
@@ -873,7 +875,7 @@ model OrganisationGlobalSettings {
aiFeaturesEnabled Boolean @default(false)
}
/// @zod.import(["import { ZDocumentEmailSettingsSchema } from '@documenso/lib/types/document-email';", "import { ZDefaultRecipientsSchema } from '@documenso/lib/types/default-recipients';", "import { ZEnvelopeExpirationPeriod as ZEnvelopeExpirationPeriodSchema } from '@documenso/lib/constants/envelope-expiration';", "import { ZEnvelopeReminderSettings as ZEnvelopeReminderSettingsSchema } from '@documenso/lib/constants/envelope-reminder';"])
/// @zod.import(["import { ZDocumentEmailSettingsSchema } from '@documenso/lib/types/document-email';", "import { ZDefaultRecipientsSchema } from '@documenso/lib/types/default-recipients';", "import { ZEnvelopeExpirationPeriod as ZEnvelopeExpirationPeriodSchema } from '@documenso/lib/constants/envelope-expiration';", "import { ZEnvelopeReminderSettings as ZEnvelopeReminderSettingsSchema } from '@documenso/lib/constants/envelope-reminder';", "import { ZCssVarsSchema } from '@documenso/lib/types/css-vars';"])
model TeamGlobalSettings {
id String @id
team Team?
@@ -905,6 +907,8 @@ model TeamGlobalSettings {
brandingLogo String?
brandingUrl String?
brandingCompanyDetails String?
brandingColors Json? /// [TCssVarsSchema] @zod.custom.use(ZCssVarsSchema)
brandingCss String?
envelopeExpirationPeriod Json? /// [EnvelopeExpirationPeriod] @zod.custom.use(ZEnvelopeExpirationPeriodSchema)
@@ -1102,9 +1106,10 @@ model OrganisationAuthenticationPortal {
clientSecret String @default("")
wellKnownUrl String @default("")
defaultOrganisationRole OrganisationMemberRole @default(MEMBER)
autoProvisionUsers Boolean @default(true)
allowedDomains String[] @default([])
defaultOrganisationRole OrganisationMemberRole @default(MEMBER)
autoProvisionUsers Boolean @default(true)
allowedDomains String[] @default([])
allowPersonalOrganisations Boolean @default(false)
}
model Counter {
+5 -12
View File
@@ -1,9 +1,5 @@
import type { Team, User } from '@prisma/client';
import { nanoid } from 'nanoid';
import fs from 'node:fs';
import path from 'node:path';
import { match } from 'ts-pattern';
import { createEnvelope } from '@documenso/lib/server-only/envelope/create-envelope';
import { incrementDocumentId } from '@documenso/lib/server-only/envelope/increment-id';
import {
@@ -19,6 +15,9 @@ import {
FIELD_TEXT_META_DEFAULT_VALUES,
} from '@documenso/lib/types/field-meta';
import { prefixedId } from '@documenso/lib/universal/id';
import type { Team, User } from '@prisma/client';
import { nanoid } from 'nanoid';
import { match } from 'ts-pattern';
import { prisma } from '..';
import {
@@ -35,9 +34,7 @@ import {
import { seedTeam } from './teams';
import { seedUser } from './users';
const examplePdf = fs
.readFileSync(path.join(__dirname, '../../../assets/example.pdf'))
.toString('base64');
const examplePdf = fs.readFileSync(path.join(__dirname, '../../../assets/example.pdf')).toString('base64');
type DocumentToSeed = {
sender: User;
@@ -74,11 +71,7 @@ export const seedDocuments = async (documents: DocumentToSeed[]) => {
);
};
export const seedBlankDocument = async (
owner: User,
teamId: number,
options: CreateDocumentOptions = {},
) => {
export const seedBlankDocument = async (owner: User, teamId: number, options: CreateDocumentOptions = {}) => {
const { key, createDocumentOptions = {}, internalVersion = 1 } = options;
const documentData = await prisma.documentData.create({
+1 -5
View File
@@ -9,11 +9,7 @@ type CreateFolderOptions = {
createFolderOptions?: Partial<Prisma.FolderUncheckedCreateInput>;
};
export const seedBlankFolder = async (
user: User,
teamId: number,
options: CreateFolderOptions = {},
) => {
export const seedBlankFolder = async (user: User, teamId: number, options: CreateFolderOptions = {}) => {
return await prisma.folder.create({
data: {
name: 'My folder',
+141 -24
View File
@@ -3,18 +3,12 @@ import path from 'node:path';
import { ALIGNMENT_TEST_FIELDS } from '@documenso/app-tests/constants/field-alignment-pdf';
import { FIELD_META_TEST_FIELDS } from '@documenso/app-tests/constants/field-meta-pdf';
import { OVERFLOW_TEST_FIELDS } from '@documenso/app-tests/constants/field-overflow-pdf';
import { isBase64Image } from '@documenso/lib/constants/signatures';
import {
incrementDocumentId,
incrementTemplateId,
} from '@documenso/lib/server-only/envelope/increment-id';
import { incrementDocumentId, incrementTemplateId } from '@documenso/lib/server-only/envelope/increment-id';
import { nanoid, prefixedId } from '@documenso/lib/universal/id';
import { DIRECT_TEMPLATE_RECIPIENT_EMAIL, DIRECT_TEMPLATE_RECIPIENT_NAME } from '../../lib/constants/direct-templates';
import { prisma } from '..';
import {
DIRECT_TEMPLATE_RECIPIENT_EMAIL,
DIRECT_TEMPLATE_RECIPIENT_NAME,
} from '../../lib/constants/direct-templates';
import {
DocumentDataType,
DocumentSource,
@@ -39,9 +33,7 @@ const createDocumentData = async ({ documentData }: { documentData: string }) =>
};
export const seedDatabase = async () => {
const examplePdf = fs
.readFileSync(path.join(__dirname, '../../../assets/example.pdf'))
.toString('base64');
const examplePdf = fs.readFileSync(path.join(__dirname, '../../../assets/example.pdf')).toString('base64');
const exampleUserExists = await prisma.user.findFirst({
where: {
@@ -183,6 +175,22 @@ export const seedDatabase = async () => {
userId: adminUser.user.id,
teamId: adminUser.team.id,
}),
seedOverflowTestDocument({
userId: exampleUser.user.id,
teamId: exampleUser.team.id,
recipientName: exampleUser.user.name || '',
recipientEmail: exampleUser.user.email,
insertFields: false,
status: DocumentStatus.DRAFT,
}),
seedOverflowTestDocument({
userId: adminUser.user.id,
teamId: adminUser.team.id,
recipientName: adminUser.user.name || '',
recipientEmail: adminUser.user.email,
insertFields: false,
status: DocumentStatus.DRAFT,
}),
seedAlignmentTestDocument({
userId: exampleUser.user.id,
teamId: exampleUser.team.id,
@@ -285,9 +293,7 @@ export const seedAlignmentTestDocument = async ({
.readFileSync(path.join(__dirname, '../../../assets/field-font-alignment.pdf'))
.toString('base64');
const fieldMetaPdf = fs
.readFileSync(path.join(__dirname, '../../../assets/field-meta.pdf'))
.toString('base64');
const fieldMetaPdf = fs.readFileSync(path.join(__dirname, '../../../assets/field-meta.pdf')).toString('base64');
const alignmentDocumentData = await createDocumentData({ documentData: alignmentPdf });
const fieldMetaDocumentData = await createDocumentData({ documentData: fieldMetaPdf });
@@ -351,9 +357,7 @@ export const seedAlignmentTestDocument = async ({
const { id, recipients, envelopeItems } = createdEnvelope;
if (isDirectTemplate) {
const directTemplateRecpient = recipients.find(
(recipient) => recipient.email === DIRECT_TEMPLATE_RECIPIENT_EMAIL,
);
const directTemplateRecpient = recipients.find((recipient) => recipient.email === DIRECT_TEMPLATE_RECIPIENT_EMAIL);
if (!directTemplateRecpient) {
throw new Error('Need to create a direct template recipient');
@@ -387,9 +391,7 @@ export const seedAlignmentTestDocument = async ({
envelopeId: id,
customText: insertFields ? field.customText : '',
inserted:
insertFields &&
((!field?.fieldMeta?.readOnly && Boolean(field.customText)) ||
field.type === 'SIGNATURE'),
insertFields && ((!field?.fieldMeta?.readOnly && Boolean(field.customText)) || field.type === 'SIGNATURE'),
signature:
field.signature && insertFields
? {
@@ -415,9 +417,124 @@ export const seedAlignmentTestDocument = async ({
envelopeId: id,
customText: insertFields ? field.customText : '',
inserted:
insertFields &&
((!field?.fieldMeta?.readOnly && Boolean(field.customText)) ||
field.type === 'SIGNATURE'),
insertFields && ((!field?.fieldMeta?.readOnly && Boolean(field.customText)) || field.type === 'SIGNATURE'),
signature:
field.signature && insertFields
? {
create: {
recipientId,
signatureImageAsBase64: isBase64Image(field.signature) ? field.signature : null,
typedSignature: isBase64Image(field.signature) ? null : field.signature,
},
}
: undefined,
},
});
}),
);
return await prisma.envelope.findFirstOrThrow({
where: {
id: createdEnvelope.id,
},
include: {
recipients: true,
envelopeItems: true,
},
});
};
export const seedOverflowTestDocument = async ({
userId,
teamId,
recipientName,
recipientEmail,
insertFields,
status,
}: {
userId: number;
teamId: number;
recipientName: string;
recipientEmail: string;
insertFields: boolean;
status: DocumentStatus;
}) => {
const overflowPdf = fs.readFileSync(path.join(__dirname, '../../../assets/field-overflow.pdf')).toString('base64');
const overflowDocumentData = await createDocumentData({ documentData: overflowPdf });
const secondaryId = await incrementDocumentId().then((v) => v.formattedDocumentId);
const documentMeta = await prisma.documentMeta.create({
data: {},
});
const createdEnvelope = await prisma.envelope.create({
data: {
id: prefixedId('envelope'),
secondaryId,
internalVersion: 2,
type: EnvelopeType.DOCUMENT,
documentMetaId: documentMeta.id,
source: DocumentSource.DOCUMENT,
title: 'Overflow Test',
status,
envelopeItems: {
createMany: {
data: [
{
id: prefixedId('envelope_item'),
title: 'field-overflow',
documentDataId: overflowDocumentData.id,
order: 1,
},
],
},
},
userId,
teamId,
recipients: {
create: {
name: recipientName,
email: recipientEmail,
token: nanoid(),
sendStatus: status === 'DRAFT' ? SendStatus.NOT_SENT : SendStatus.SENT,
signingStatus: status === 'COMPLETED' ? SigningStatus.SIGNED : SigningStatus.NOT_SIGNED,
readStatus: status !== 'DRAFT' ? ReadStatus.OPENED : ReadStatus.NOT_OPENED,
},
},
},
include: {
recipients: true,
envelopeItems: true,
},
});
const { id, recipients, envelopeItems } = createdEnvelope;
const recipientId = recipients[0].id;
const envelopeItemId = envelopeItems.find((item) => item.order === 1)?.id;
if (!envelopeItemId) {
throw new Error('Envelope item not found');
}
await Promise.all(
OVERFLOW_TEST_FIELDS.map(async (field) => {
// Use seedFieldMeta (full meta with all defaults) instead of fieldMeta
// (minimal meta for API testing) since the seed bypasses API validation.
const { fieldMeta: _fieldMeta, seedFieldMeta, ...fieldData } = field;
await prisma.field.create({
data: {
...fieldData,
fieldMeta: seedFieldMeta,
recipientId,
envelopeItemId,
envelopeId: id,
customText: insertFields ? field.customText : '',
inserted:
insertFields && ((!seedFieldMeta?.readOnly && Boolean(field.customText)) || field.type === 'SIGNATURE'),
signature:
field.signature && insertFields
? {
+66
View File
@@ -0,0 +1,66 @@
import { createTeamMembers } from '@documenso/trpc/server/team-router/create-team-members';
import { TeamMemberRole } from '@prisma/client';
import { prisma } from '..';
import { seedTeam } from './teams';
/**
* One-off seed script: creates a team with a large number of members.
*
* Run via:
* npm run with:env -- tsx packages/prisma/seed/large-team-seed.ts
*
* Produces:
* - 1 owner
* - ORG_MEMBER_COUNT organisation members
* - TEAM_MEMBER_COUNT of those org members are also added to the team's role group
*/
const ORG_MEMBER_COUNT = 200;
const TEAM_MEMBER_COUNT = 50;
const seedLargeTeam = async () => {
console.log(`[SEEDING]: Creating team with ${ORG_MEMBER_COUNT} organisation members...`);
const { owner, team, organisation } = await seedTeam({
createTeamMembers: ORG_MEMBER_COUNT,
});
// Exclude the owner — they're already a team member by default.
const nonOwnerOrgMembers = organisation.members.filter((member) => member.userId !== owner.id);
const membersToAttachToTeam = nonOwnerOrgMembers.slice(0, TEAM_MEMBER_COUNT);
console.log(`[SEEDING]: Attaching ${membersToAttachToTeam.length} org members to the team's role group...`);
await createTeamMembers({
userId: owner.id,
teamId: team.id,
membersToCreate: membersToAttachToTeam.map((member) => ({
organisationMemberId: member.id,
teamRole: TeamMemberRole.MEMBER,
})),
});
console.log(`[SEEDING]: Done.`);
console.log(` Owner email: ${owner.email}`);
console.log(` Owner password: password`);
console.log(` Organisation: ${organisation.url} (id ${organisation.id})`);
console.log(` Team URL: ${team.url} (id ${team.id})`);
console.log(` Org members: ${ORG_MEMBER_COUNT}`);
console.log(` Team-group members: ${membersToAttachToTeam.length}`);
};
const main = async () => {
try {
await seedLargeTeam();
} catch (err) {
console.error('[SEEDING]: Failed to seed large team.');
console.error(err);
process.exitCode = 1;
} finally {
await prisma.$disconnect();
}
};
void main();
+2 -3
View File
@@ -1,8 +1,7 @@
import type { OrganisationMemberRole, OrganisationType } from '@prisma/client';
import { OrganisationGroupType, type User } from '@prisma/client';
import { hashSync } from '@documenso/lib/server-only/auth/hash';
import { addUserToOrganisation } from '@documenso/lib/server-only/organisation/accept-organisation-invitation';
import type { OrganisationMemberRole, OrganisationType } from '@prisma/client';
import { OrganisationGroupType, type User } from '@prisma/client';
import { prisma } from '..';
import { seedTestEmail } from './users';
+3 -14
View File
@@ -1,6 +1,5 @@
import { customAlphabet } from 'nanoid';
import { createTeamMembers } from '@documenso/trpc/server/team-router/create-team-members';
import { customAlphabet } from 'nanoid';
import { prisma } from '..';
import type { Prisma } from '../client';
@@ -96,11 +95,7 @@ type SeedTeamMemberOptions = {
name?: string;
};
export const seedTeamMember = async ({
teamId,
name,
role = TeamMemberRole.ADMIN,
}: SeedTeamMemberOptions) => {
export const seedTeamMember = async ({ teamId, name, role = TeamMemberRole.ADMIN }: SeedTeamMemberOptions) => {
const { user } = await seedUser({ name });
const team = await prisma.team.findFirstOrThrow({
@@ -162,13 +157,7 @@ export const unseedTeamEmail = async ({ teamId }: { teamId: number }) => {
});
};
export const seedTeamEmailVerification = async ({
email,
teamId,
}: {
email: string;
teamId: number;
}) => {
export const seedTeamEmailVerification = async ({ email, teamId }: { email: string; teamId: number }) => {
return await prisma.teamEmailVerification.create({
data: {
teamId,
+2 -8
View File
@@ -20,9 +20,7 @@ import {
SigningStatus,
} from '../client';
const examplePdf = fs
.readFileSync(path.join(__dirname, '../../../assets/example.pdf'))
.toString('base64');
const examplePdf = fs.readFileSync(path.join(__dirname, '../../../assets/example.pdf')).toString('base64');
type SeedTemplateOptions = {
title?: string;
@@ -37,11 +35,7 @@ type CreateTemplateOptions = {
createTemplateOptions?: Partial<Prisma.EnvelopeUncheckedCreateInput>;
};
export const seedBlankTemplate = async (
owner: User,
teamId: number,
options: CreateTemplateOptions = {},
) => {
export const seedBlankTemplate = async (owner: User, teamId: number, options: CreateTemplateOptions = {}) => {
const { key, createTemplateOptions = {} } = options;
const documentData = await prisma.documentData.create({
+2 -3
View File
@@ -1,8 +1,7 @@
import { OrganisationType, Role } from '@prisma/client';
import { customAlphabet } from 'nanoid';
import { hashSync } from '@documenso/lib/server-only/auth/hash';
import { createPersonalOrganisation } from '@documenso/lib/server-only/organisation/create-organisation';
import { OrganisationType, Role } from '@prisma/client';
import { customAlphabet } from 'nanoid';
import { prisma } from '..';
import { setOrganisationType } from './organisations';
@@ -2,11 +2,11 @@
* Legacy Document schema to confirm backwards API compatibility since
* we migrated Documents to Envelopes.
*/
import { DocumentSource } from '@prisma/client';
import { z } from 'zod';
import { ZDocumentAuthOptionsSchema } from '@documenso/lib/types/document-auth';
import { ZDocumentFormValuesSchema } from '@documenso/lib/types/document-form-values';
import { DocumentSource } from '@prisma/client';
import { z } from 'zod';
import DocumentStatusSchema from '../generated/zod/inputTypeSchemas/DocumentStatusSchema';
import DocumentVisibilitySchema from '../generated/zod/inputTypeSchemas/DocumentVisibilitySchema';
@@ -20,14 +20,8 @@ export const LegacyDocumentSchema = z.object({
status: DocumentStatusSchema,
source: z.nativeEnum(DocumentSource),
id: z.number(),
qrToken: z
.string()
.describe('The token for viewing the document using the QR code on the certificate.')
.nullable(),
externalId: z
.string()
.describe('A custom external ID you can use to identify the document.')
.nullable(),
qrToken: z.string().describe('The token for viewing the document using the QR code on the certificate.').nullable(),
externalId: z.string().describe('A custom external ID you can use to identify the document.').nullable(),
userId: z.number().describe('The ID of the user that created this document.'),
teamId: z.number(),
/**
@@ -6,5 +6,4 @@ export const ExtendedDocumentStatus = {
ALL: 'ALL',
} as const;
export type ExtendedDocumentStatus =
(typeof ExtendedDocumentStatus)[keyof typeof ExtendedDocumentStatus];
export type ExtendedDocumentStatus = (typeof ExtendedDocumentStatus)[keyof typeof ExtendedDocumentStatus];
@@ -1,7 +1,6 @@
import type { TFieldMetaSchema as FieldMeta } from '@documenso/lib/types/field-meta';
import type { Field, Signature } from '@prisma/client';
import { type TFieldMetaSchema as FieldMeta } from '@documenso/lib/types/field-meta';
export type FieldWithSignatureAndFieldMeta = Field & {
signature?: Signature | null;
fieldMeta: FieldMeta | null;
@@ -2,10 +2,10 @@
* Legacy Template schema to confirm backwards API compatibility since
* we removed the "Template" prisma schema model.
*/
import { TemplateType } from '@prisma/client';
import { z } from 'zod';
import { ZDocumentAuthOptionsSchema } from '@documenso/lib/types/document-auth';
import { TemplateType } from '@prisma/client';
import { z } from 'zod';
import { DocumentVisibilitySchema } from '../generated/zod/inputTypeSchemas/DocumentVisibilitySchema';
import TemplateDirectLinkSchema from '../generated/zod/modelSchema/TemplateDirectLinkSchema';
+1 -6
View File
@@ -1,9 +1,6 @@
/* eslint-disable @typescript-eslint/no-namespace */
import type { TDefaultRecipient } from '@documenso/lib/types/default-recipients';
import type {
TDocumentAuthOptions,
TRecipientAuthOptions,
} from '@documenso/lib/types/document-auth';
import type { TDocumentAuthOptions, TRecipientAuthOptions } from '@documenso/lib/types/document-auth';
import type { TDocumentEmailSettings } from '@documenso/lib/types/document-email';
import type { TDocumentFormValues } from '@documenso/lib/types/document-form-values';
import type { TEnvelopeAttachmentType } from '@documenso/lib/types/envelope-attachment';
@@ -31,5 +28,3 @@ declare global {
type DefaultRecipient = TDefaultRecipient;
}
}
export {};