@promptbook/cli 0.104.0-1 → 0.104.0-10
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/apps/agents-server/config.ts +1 -3
- package/apps/agents-server/next.config.ts +2 -2
- package/apps/agents-server/package.json +7 -3
- package/apps/agents-server/public/fonts/OpenMoji-color-cbdt.woff2 +0 -0
- package/apps/agents-server/public/swagger.json +115 -0
- package/apps/agents-server/scripts/generate-reserved-paths/generate-reserved-paths.ts +54 -0
- package/apps/agents-server/scripts/generate-reserved-paths/tsconfig.json +19 -0
- package/apps/agents-server/src/app/AddAgentButton.tsx +47 -21
- package/apps/agents-server/src/app/actions.ts +22 -5
- package/apps/agents-server/src/app/admin/browser-test/BrowserTestClient.tsx +211 -0
- package/apps/agents-server/src/app/admin/browser-test/page.tsx +13 -0
- package/apps/agents-server/src/app/admin/chat-feedback/ChatFeedbackClient.tsx +221 -274
- package/apps/agents-server/src/app/admin/chat-history/ChatHistoryClient.tsx +94 -137
- package/apps/agents-server/src/app/admin/messages/MessagesClient.tsx +294 -0
- package/apps/agents-server/src/app/admin/messages/page.tsx +13 -0
- package/apps/agents-server/src/app/admin/messages/send-email/SendEmailClient.tsx +104 -0
- package/apps/agents-server/src/app/admin/messages/send-email/actions.ts +35 -0
- package/apps/agents-server/src/app/admin/messages/send-email/page.tsx +13 -0
- package/apps/agents-server/src/app/admin/metadata/MetadataClient.tsx +23 -19
- package/apps/agents-server/src/app/agents/[agentName]/AgentChatWrapper.tsx +15 -1
- package/apps/agents-server/src/app/agents/[agentName]/AgentOptionsMenu.tsx +51 -9
- package/apps/agents-server/src/app/agents/[agentName]/AgentProfileChat.tsx +47 -4
- package/apps/agents-server/src/app/agents/[agentName]/AgentProfileWrapper.tsx +53 -11
- package/apps/agents-server/src/app/agents/[agentName]/_utils.ts +23 -3
- package/apps/agents-server/src/app/agents/[agentName]/agentLinks.tsx +8 -8
- package/apps/agents-server/src/app/agents/[agentName]/api/agents/route.ts +17 -26
- package/apps/agents-server/src/app/agents/[agentName]/api/book/route.ts +4 -2
- package/apps/agents-server/src/app/agents/[agentName]/api/chat/route.ts +20 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/mcp/route.ts +6 -11
- package/apps/agents-server/src/app/agents/[agentName]/api/profile/route.ts +5 -1
- package/apps/agents-server/src/app/agents/[agentName]/api/voice/route.ts +5 -2
- package/apps/agents-server/src/app/agents/[agentName]/book/BookEditorWrapper.tsx +20 -16
- package/apps/agents-server/src/app/agents/[agentName]/book/page.tsx +15 -2
- package/apps/agents-server/src/app/agents/[agentName]/book+chat/page.tsx +15 -2
- package/apps/agents-server/src/app/agents/[agentName]/chat/page.tsx +12 -0
- package/apps/agents-server/src/app/agents/[agentName]/code/api/route.ts +68 -0
- package/apps/agents-server/src/app/agents/[agentName]/code/page.tsx +223 -0
- package/apps/agents-server/src/app/agents/[agentName]/generateAgentMetadata.ts +5 -0
- package/apps/agents-server/src/app/agents/[agentName]/history/actions.ts +2 -2
- package/apps/agents-server/src/app/agents/[agentName]/history/page.tsx +10 -3
- package/apps/agents-server/src/app/agents/[agentName]/images/default-avatar.png/getAgentDefaultAvatarPrompt.ts +31 -0
- package/apps/agents-server/src/app/agents/[agentName]/images/default-avatar.png/route.ts +194 -0
- package/apps/agents-server/src/app/agents/[agentName]/images/icon-256.png/route.tsx +14 -2
- package/apps/agents-server/src/app/agents/[agentName]/images/page.tsx +200 -0
- package/apps/agents-server/src/app/agents/[agentName]/images/screenshot-fullhd.png/route.tsx +4 -3
- package/apps/agents-server/src/app/agents/[agentName]/images/screenshot-phone.png/route.tsx +4 -3
- package/apps/agents-server/src/app/agents/[agentName]/integration/page.tsx +10 -3
- package/apps/agents-server/src/app/agents/[agentName]/links/page.tsx +11 -4
- package/apps/agents-server/src/app/agents/[agentName]/opengraph-image.tsx +11 -2
- package/apps/agents-server/src/app/agents/[agentName]/page.tsx +18 -10
- package/apps/agents-server/src/app/agents/[agentName]/system-message/page.tsx +100 -0
- package/apps/agents-server/src/app/api/admin-email/route.ts +12 -0
- package/apps/agents-server/src/app/api/agents/[agentName]/clone/route.ts +13 -14
- package/apps/agents-server/src/app/api/agents/[agentName]/restore/route.ts +20 -0
- package/apps/agents-server/src/app/api/agents/[agentName]/route.ts +43 -1
- package/apps/agents-server/src/app/api/agents/route.ts +28 -3
- package/apps/agents-server/src/app/api/api-tokens/route.ts +6 -7
- package/apps/agents-server/src/app/api/browser-test/act/route.ts +141 -0
- package/apps/agents-server/src/app/api/browser-test/screenshot/route.ts +30 -0
- package/apps/agents-server/src/app/api/browser-test/scroll-facebook/route.ts +62 -0
- package/apps/agents-server/src/app/api/docs/book.md/route.ts +61 -0
- package/apps/agents-server/src/app/api/emails/incoming/sendgrid/route.ts +48 -0
- package/apps/agents-server/src/app/api/federated-agents/route.ts +12 -0
- package/apps/agents-server/src/app/api/images/[filename]/route.ts +107 -0
- package/apps/agents-server/src/app/api/messages/route.ts +102 -0
- package/apps/agents-server/src/app/api/metadata/route.ts +5 -6
- package/apps/agents-server/src/app/api/upload/route.ts +128 -45
- package/apps/agents-server/src/app/docs/[docId]/page.tsx +2 -3
- package/apps/agents-server/src/app/docs/page.tsx +12 -12
- package/apps/agents-server/src/app/globals.css +140 -33
- package/apps/agents-server/src/app/humans.txt/route.ts +1 -1
- package/apps/agents-server/src/app/layout.tsx +27 -22
- package/apps/agents-server/src/app/page.tsx +54 -6
- package/apps/agents-server/src/app/recycle-bin/actions.ts +20 -14
- package/apps/agents-server/src/app/recycle-bin/page.tsx +27 -41
- package/apps/agents-server/src/app/robots.txt/route.ts +1 -1
- package/apps/agents-server/src/app/security.txt/route.ts +1 -1
- package/apps/agents-server/src/app/sitemap.xml/route.ts +9 -7
- package/apps/agents-server/src/app/swagger/page.tsx +14 -0
- package/apps/agents-server/src/components/AgentProfile/AgentProfile.tsx +41 -116
- package/apps/agents-server/src/components/AgentProfile/AgentProfileImage.tsx +92 -0
- package/apps/agents-server/src/components/AgentProfile/QrCodeModal.tsx +0 -1
- package/apps/agents-server/src/components/AgentProfile/useAgentBackground.ts +97 -0
- package/apps/agents-server/src/components/Auth/AuthControls.tsx +5 -4
- package/apps/agents-server/src/components/DeletedAgentBanner.tsx +26 -0
- package/apps/agents-server/src/components/DocsToolbar/DocsToolbar.tsx +38 -0
- package/apps/agents-server/src/components/DocumentationContent/DocumentationContent.tsx +11 -9
- package/apps/agents-server/src/components/Footer/Footer.tsx +5 -5
- package/apps/agents-server/src/components/ForgottenPasswordDialog/ForgottenPasswordDialog.tsx +61 -0
- package/apps/agents-server/src/components/Header/Header.tsx +114 -40
- package/apps/agents-server/src/components/Homepage/AgentCard.tsx +145 -23
- package/apps/agents-server/src/components/Homepage/AgentsList.tsx +93 -15
- package/apps/agents-server/src/components/Homepage/DeletedAgentsList.tsx +66 -0
- package/apps/agents-server/src/components/Homepage/ExternalAgentsSection.tsx +12 -3
- package/apps/agents-server/src/components/Homepage/ExternalAgentsSectionClient.tsx +19 -10
- package/apps/agents-server/src/components/LayoutWrapper/LayoutWrapper.tsx +3 -2
- package/apps/agents-server/src/components/LoginForm/LoginForm.tsx +50 -1
- package/apps/agents-server/src/components/NewAgentDialog/NewAgentDialog.tsx +88 -0
- package/apps/agents-server/src/components/NotFoundPage/NotFoundPage.tsx +7 -2
- package/apps/agents-server/src/components/OpenMojiIcon/OpenMojiIcon.tsx +16 -7
- package/apps/agents-server/src/components/PrintHeader/PrintHeader.tsx +4 -4
- package/apps/agents-server/src/components/RegisterUserDialog/RegisterUserDialog.tsx +61 -0
- package/apps/agents-server/src/components/VercelDeploymentCard/VercelDeploymentCard.tsx +2 -0
- package/apps/agents-server/src/components/_utils/generateMetaTxt.ts +12 -10
- package/apps/agents-server/src/components/_utils/headlessParam.tsx +7 -3
- package/apps/agents-server/src/database/$provideSupabaseForBrowser.ts +3 -3
- package/apps/agents-server/src/database/$provideSupabaseForServer.ts +1 -1
- package/apps/agents-server/src/database/$provideSupabaseForWorker.ts +3 -3
- package/apps/agents-server/src/database/metadataDefaults.ts +19 -1
- package/apps/agents-server/src/database/migrate.ts +34 -1
- package/apps/agents-server/src/database/migrations/2025-11-0001-initial-schema.sql +1 -3
- package/apps/agents-server/src/database/migrations/2025-11-0002-metadata-table.sql +1 -3
- package/apps/agents-server/src/database/migrations/2025-12-0240-agent-public-id.sql +3 -0
- package/apps/agents-server/src/database/migrations/2025-12-0360-agent-deleted-at.sql +1 -0
- package/apps/agents-server/src/database/migrations/2025-12-0370-image-table.sql +19 -0
- package/apps/agents-server/src/database/migrations/2025-12-0380-agent-visibility.sql +1 -0
- package/apps/agents-server/src/database/migrations/2025-12-0390-upload-tracking.sql +20 -0
- package/apps/agents-server/src/database/migrations/2025-12-0401-file-upload-status.sql +13 -0
- package/apps/agents-server/src/database/migrations/2025-12-0402-message-table.sql +42 -0
- package/apps/agents-server/src/database/migrations/2025-12-0403-generation-lock-table.sql +15 -0
- package/apps/agents-server/src/database/migrations/2025-12-0640-openai-assistant-cache.sql +12 -0
- package/apps/agents-server/src/database/migrations/2025-12-0820-agent-history-permanent-id.sql +29 -0
- package/apps/agents-server/src/database/schema.ts +231 -4
- package/apps/agents-server/src/generated/reservedPaths.ts +32 -0
- package/apps/agents-server/src/message-providers/email/_common/Email.ts +73 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/TODO.txt +1 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddress.test.ts.todo +108 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddress.ts +62 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddresses.test.ts.todo +117 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddresses.ts +19 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddress.test.ts.todo +119 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddress.ts +19 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddresses.test.ts.todo +74 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddresses.ts +14 -0
- package/apps/agents-server/src/message-providers/email/sendgrid/SendgridMessageProvider.ts +44 -0
- package/apps/agents-server/src/message-providers/email/sendgrid/parseInboundSendgridEmail.ts +49 -0
- package/apps/agents-server/src/message-providers/email/zeptomail/ZeptomailMessageProvider.ts +51 -0
- package/apps/agents-server/src/message-providers/index.ts +13 -0
- package/apps/agents-server/src/message-providers/interfaces/MessageProvider.ts +11 -0
- package/apps/agents-server/src/middleware.ts +19 -23
- package/apps/agents-server/src/tools/$provideBrowserForServer.ts +32 -0
- package/apps/agents-server/src/tools/$provideCdnForServer.ts +7 -2
- package/apps/agents-server/src/utils/auth.ts +117 -17
- package/apps/agents-server/src/utils/cdn/classes/TrackedFilesStorage.ts +57 -0
- package/apps/agents-server/src/utils/cdn/classes/VercelBlobStorage.ts +4 -0
- package/apps/agents-server/src/utils/cdn/interfaces/IFilesStorage.ts +18 -0
- package/apps/agents-server/src/utils/content/extractBodyContentFromHtml.ts +19 -0
- package/apps/agents-server/src/utils/getUserIdFromRequest.ts +35 -0
- package/apps/agents-server/src/utils/handleChatCompletion.ts +65 -5
- package/apps/agents-server/src/utils/messages/sendMessage.ts +91 -0
- package/apps/agents-server/src/utils/messagesAdmin.ts +72 -0
- package/apps/agents-server/src/utils/normalization/filenameToPrompt.test.ts +36 -0
- package/apps/agents-server/src/utils/normalization/filenameToPrompt.ts +25 -0
- package/apps/agents-server/src/utils/validateApiKey.ts +7 -11
- package/esm/index.es.js +2890 -2737
- package/esm/index.es.js.map +1 -1
- package/esm/typings/servers.d.ts +8 -0
- package/esm/typings/src/_packages/core.index.d.ts +2 -0
- package/esm/typings/src/_packages/types.index.d.ts +10 -2
- package/esm/typings/src/book-2.0/agent-source/AgentBasicInformation.d.ts +6 -1
- package/esm/typings/src/book-2.0/agent-source/createAgentModelRequirements.d.ts +6 -6
- package/esm/typings/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments.closed.test.d.ts +1 -0
- package/esm/typings/src/book-2.0/utils/generatePlaceholderAgentProfileImageUrl.d.ts +3 -3
- package/esm/typings/src/book-components/Chat/Chat/ChatMessageItem.d.ts +5 -1
- package/esm/typings/src/book-components/Chat/Chat/ChatProps.d.ts +5 -0
- package/esm/typings/src/book-components/Chat/CodeBlock/CodeBlock.d.ts +13 -0
- package/esm/typings/src/book-components/Chat/MarkdownContent/MarkdownContent.d.ts +1 -0
- package/esm/typings/src/book-components/Chat/types/ChatMessage.d.ts +7 -11
- package/esm/typings/src/book-components/_common/Dropdown/Dropdown.d.ts +2 -2
- package/esm/typings/src/book-components/_common/MenuHoisting/MenuHoistingContext.d.ts +56 -0
- package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +21 -11
- package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentsDatabaseSchema.d.ts +80 -14
- package/esm/typings/src/commitments/DICTIONARY/DICTIONARY.d.ts +46 -0
- package/esm/typings/src/commitments/index.d.ts +2 -1
- package/esm/typings/src/llm-providers/_multiple/MultipleLlmExecutionTools.d.ts +6 -2
- package/esm/typings/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +1 -1
- package/esm/typings/src/llm-providers/ollama/OllamaExecutionTools.d.ts +1 -1
- package/esm/typings/src/llm-providers/openai/createOpenAiCompatibleExecutionTools.d.ts +1 -1
- package/esm/typings/src/llm-providers/remote/RemoteLlmExecutionTools.d.ts +1 -0
- package/esm/typings/src/types/Message.d.ts +49 -0
- package/esm/typings/src/types/ModelRequirements.d.ts +38 -14
- package/esm/typings/src/types/typeAliases.d.ts +23 -1
- package/esm/typings/src/utils/color/utils/colorToDataUrl.d.ts +2 -1
- package/esm/typings/src/utils/environment/$detectRuntimeEnvironment.d.ts +4 -4
- package/esm/typings/src/utils/environment/$isRunningInBrowser.d.ts +1 -1
- package/esm/typings/src/utils/environment/$isRunningInJest.d.ts +1 -1
- package/esm/typings/src/utils/environment/$isRunningInNode.d.ts +1 -1
- package/esm/typings/src/utils/environment/$isRunningInWebWorker.d.ts +1 -1
- package/esm/typings/src/utils/markdown/extractAllBlocksFromMarkdown.d.ts +2 -2
- package/esm/typings/src/utils/markdown/extractOneBlockFromMarkdown.d.ts +2 -2
- package/esm/typings/src/utils/random/$randomBase58.d.ts +12 -0
- package/esm/typings/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/umd/index.umd.js +4018 -3865
- package/umd/index.umd.js.map +1 -1
- package/apps/agents-server/package-lock.json +0 -27
- package/apps/agents-server/public/fonts/download-font.js +0 -22
- package/apps/agents-server/src/components/PrintButton/PrintButton.tsx +0 -18
- package/esm/typings/src/book-2.0/utils/generateGravatarUrl.d.ts +0 -10
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { Message, string_email, string_person_fullname } from '@promptbook-local/types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Single email which was received by the application
|
|
5
|
+
*/
|
|
6
|
+
export type InboundEmail = Email & { direction: 'INBOUND' };
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Single email which was sended from the application
|
|
10
|
+
*/
|
|
11
|
+
export type OutboundEmail = Email & { direction: 'OUTBOUND' };
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Single email
|
|
15
|
+
*/
|
|
16
|
+
type Email = Message<string_email> & {
|
|
17
|
+
/**
|
|
18
|
+
* Channel of the message
|
|
19
|
+
*
|
|
20
|
+
* @default 'EMAIL'
|
|
21
|
+
*/
|
|
22
|
+
readonly channel?: 'EMAIL';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Carbon copy email addresses
|
|
26
|
+
*
|
|
27
|
+
* Note: Not working with BCC (Blind Carbon Copy) because we want to have all emails in the same thread
|
|
28
|
+
* and for hidden emails we can just call $sendEmail multiple times
|
|
29
|
+
*/
|
|
30
|
+
readonly cc: Array<EmailAddress>;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Email subject
|
|
34
|
+
*/
|
|
35
|
+
readonly subject: string;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Email attachments
|
|
39
|
+
*/
|
|
40
|
+
readonly attachments: Array<File>;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export type EmailAddress = {
|
|
44
|
+
/**
|
|
45
|
+
* Everything outside of `<>` in email address
|
|
46
|
+
*
|
|
47
|
+
* @example "Pavol Hejný <pavol@hejny.cz>" -> "Pavol Hejný"
|
|
48
|
+
* @example "\"Pavol Hejný\" <pavol@hejny.cz>" -> "Pavol Hejný"
|
|
49
|
+
*/
|
|
50
|
+
fullName: string_person_fullname | string | null;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Everything after `+` in email address
|
|
54
|
+
*
|
|
55
|
+
* @example "pavol+spam@webgpt.cz" -> ["spam"]
|
|
56
|
+
* @example "pavol+spam+debug@webgpt.cz" -> ["spam","debug"]
|
|
57
|
+
*/
|
|
58
|
+
plus: Array<string>;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Pure email address
|
|
62
|
+
*
|
|
63
|
+
* @example "pavol@webgpt.cz"
|
|
64
|
+
*/
|
|
65
|
+
baseEmail: string_email;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Full email address without the name but with +
|
|
69
|
+
*
|
|
70
|
+
* @example "pavol+test@webgpt.cz"
|
|
71
|
+
*/
|
|
72
|
+
fullEmail: string_email;
|
|
73
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
TODO: [🧠][🏰] Maybe move all of theese functions into `@promptbook/utils`
|
package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddress.test.ts.todo
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { describe, expect, it } from '@jest/globals';
|
|
2
|
+
import { parseEmailAddress } from './parseEmailAddress';
|
|
3
|
+
|
|
4
|
+
describe('how parseEmailAddress works', () => {
|
|
5
|
+
it('should work with simple email', () => {
|
|
6
|
+
expect(parseEmailAddress('pavol@webgpt.cz')).toEqual({
|
|
7
|
+
fullName: null,
|
|
8
|
+
baseEmail: 'pavol@webgpt.cz',
|
|
9
|
+
fullEmail: 'pavol@webgpt.cz',
|
|
10
|
+
plus: [],
|
|
11
|
+
});
|
|
12
|
+
expect(parseEmailAddress('jirka@webgpt.cz')).toEqual({
|
|
13
|
+
fullName: null,
|
|
14
|
+
baseEmail: 'jirka@webgpt.cz',
|
|
15
|
+
fullEmail: 'jirka@webgpt.cz',
|
|
16
|
+
plus: [],
|
|
17
|
+
});
|
|
18
|
+
expect(parseEmailAddress('tomas@webgpt.cz')).toEqual({
|
|
19
|
+
fullName: null,
|
|
20
|
+
baseEmail: 'tomas@webgpt.cz',
|
|
21
|
+
fullEmail: 'tomas@webgpt.cz',
|
|
22
|
+
plus: [],
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should work with fullname', () => {
|
|
27
|
+
expect(parseEmailAddress('Pavol Hejný <pavol@webgpt.cz>')).toEqual({
|
|
28
|
+
fullName: 'Pavol Hejný',
|
|
29
|
+
baseEmail: 'pavol@webgpt.cz',
|
|
30
|
+
fullEmail: 'pavol@webgpt.cz',
|
|
31
|
+
plus: [],
|
|
32
|
+
});
|
|
33
|
+
expect(parseEmailAddress('Jirka <jirka@webgpt.cz>')).toEqual({
|
|
34
|
+
fullName: 'Jirka',
|
|
35
|
+
baseEmail: 'jirka@webgpt.cz',
|
|
36
|
+
fullEmail: 'jirka@webgpt.cz',
|
|
37
|
+
plus: [],
|
|
38
|
+
});
|
|
39
|
+
expect(parseEmailAddress('"Tomáš Studeník" <tomas@webgpt.cz>')).toEqual({
|
|
40
|
+
fullName: 'Tomáš Studeník',
|
|
41
|
+
baseEmail: 'tomas@webgpt.cz',
|
|
42
|
+
fullEmail: 'tomas@webgpt.cz',
|
|
43
|
+
plus: [],
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should work with plus', () => {
|
|
48
|
+
expect(parseEmailAddress('pavol+test@webgpt.cz')).toEqual({
|
|
49
|
+
fullName: null,
|
|
50
|
+
baseEmail: 'pavol@webgpt.cz',
|
|
51
|
+
fullEmail: 'pavol+test@webgpt.cz',
|
|
52
|
+
plus: ['test'],
|
|
53
|
+
});
|
|
54
|
+
expect(parseEmailAddress('jirka+test@webgpt.cz')).toEqual({
|
|
55
|
+
fullName: null,
|
|
56
|
+
baseEmail: 'jirka@webgpt.cz',
|
|
57
|
+
fullEmail: 'jirka+test@webgpt.cz',
|
|
58
|
+
plus: ['test'],
|
|
59
|
+
});
|
|
60
|
+
expect(parseEmailAddress('tomas+test+ainautes@webgpt.cz')).toEqual({
|
|
61
|
+
fullName: null,
|
|
62
|
+
baseEmail: 'tomas@webgpt.cz',
|
|
63
|
+
fullEmail: 'tomas+test+ainautes@webgpt.cz',
|
|
64
|
+
plus: ['test', 'ainautes'],
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should work with both fullname and plus', () => {
|
|
69
|
+
expect(parseEmailAddress('Pavol Hejný <pavol+foo@webgpt.cz>')).toEqual({
|
|
70
|
+
fullName: 'Pavol Hejný',
|
|
71
|
+
baseEmail: 'pavol@webgpt.cz',
|
|
72
|
+
fullEmail: 'pavol+foo@webgpt.cz',
|
|
73
|
+
plus: ['foo'],
|
|
74
|
+
});
|
|
75
|
+
expect(parseEmailAddress('Jirka <jirka+test@webgpt.cz>')).toEqual({
|
|
76
|
+
fullName: 'Jirka',
|
|
77
|
+
baseEmail: 'jirka@webgpt.cz',
|
|
78
|
+
fullEmail: 'jirka+test@webgpt.cz',
|
|
79
|
+
plus: ['test'],
|
|
80
|
+
});
|
|
81
|
+
expect(parseEmailAddress('"Tomáš Studeník" <tomas+test+ainautes@webgpt.cz>')).toEqual({
|
|
82
|
+
fullName: 'Tomáš Studeník',
|
|
83
|
+
baseEmail: 'tomas@webgpt.cz',
|
|
84
|
+
fullEmail: 'tomas+test+ainautes@webgpt.cz',
|
|
85
|
+
plus: ['test', 'ainautes'],
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('throws on multiple adresses', () => {
|
|
90
|
+
expect(() => parseEmailAddress('Pavol <pavol@webgpt.cz>, Jirka <jirka@webgpt.cz>')).toThrowError(
|
|
91
|
+
/Seems like you are trying to parse multiple email addresses/,
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('throws on invalid email adresses', () => {
|
|
96
|
+
expect(() => parseEmailAddress('')).toThrowError(/Invalid email address/);
|
|
97
|
+
expect(() => parseEmailAddress('Pavol Hejný')).toThrowError(/Invalid email address/);
|
|
98
|
+
expect(() => parseEmailAddress('Pavol Hejný <>')).toThrowError(/Invalid email address/);
|
|
99
|
+
expect(() => parseEmailAddress('Pavol Hejný <@webgpt.cz>')).toThrowError(/Invalid email address/);
|
|
100
|
+
expect(() => parseEmailAddress('Pavol Hejný <webgpt.cz>')).toThrowError(/Invalid email address/);
|
|
101
|
+
expect(() => parseEmailAddress('Pavol Hejný <pavol@>')).toThrowError(/Invalid email address/);
|
|
102
|
+
expect(() => parseEmailAddress('Pavol Hejný <a@b>')).toThrowError(/Invalid email address/);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* TODO: [🐫] This test fails because of aliased imports `import type { string_emails } from '@promptbook-local/types';`, fix it
|
|
108
|
+
*/
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { string_email } from '@promptbook-local/types';
|
|
2
|
+
import { isValidEmail, spaceTrim } from '@promptbook-local/utils';
|
|
3
|
+
import { EmailAddress } from '../Email';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Parses the email address into its components
|
|
7
|
+
*/
|
|
8
|
+
export function parseEmailAddress(value: string_email): EmailAddress {
|
|
9
|
+
if (value.includes(',')) {
|
|
10
|
+
throw new Error('Seems like you are trying to parse multiple email addresses, use parseEmailAddresses instead');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let fullName = value.match(/^(?:"?([^"]+)"?|[^<]+)\s*</)?.[1] ?? null;
|
|
14
|
+
|
|
15
|
+
if (fullName !== null) {
|
|
16
|
+
fullName = fullName.trim();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const fullEmail = value.match(/<([^>]+)>/)?.[1] ?? value;
|
|
20
|
+
const plus: Array<string> = [];
|
|
21
|
+
|
|
22
|
+
if (!isValidEmail(fullEmail)) {
|
|
23
|
+
throw new Error(
|
|
24
|
+
spaceTrim(
|
|
25
|
+
(block) => `
|
|
26
|
+
Invalid email address "${fullEmail}"
|
|
27
|
+
|
|
28
|
+
Parsed:
|
|
29
|
+
${block(JSON.stringify({ fullName, fullEmail, plus }, null, 4))}
|
|
30
|
+
|
|
31
|
+
`,
|
|
32
|
+
),
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (fullEmail.includes('+')) {
|
|
37
|
+
const [user, domain] = fullEmail.split('@');
|
|
38
|
+
|
|
39
|
+
if (!user || !domain) {
|
|
40
|
+
throw new Error('Can not parse email address');
|
|
41
|
+
// <- TODO: ShouldNeverHappenError
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const userParts = user.split('+');
|
|
45
|
+
userParts.shift();
|
|
46
|
+
|
|
47
|
+
plus.push(...userParts);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let baseEmail = fullEmail;
|
|
51
|
+
|
|
52
|
+
for (const plusItem of plus) {
|
|
53
|
+
baseEmail = baseEmail.replace(`+${plusItem}`, '');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
fullName,
|
|
58
|
+
baseEmail,
|
|
59
|
+
fullEmail,
|
|
60
|
+
plus,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { describe, expect, it } from '@jest/globals';
|
|
2
|
+
import { parseEmailAddresses } from './parseEmailAddresses';
|
|
3
|
+
|
|
4
|
+
describe('how parseEmailAddresses works', () => {
|
|
5
|
+
it('should work with single email', () => {
|
|
6
|
+
expect(parseEmailAddresses('pavol@webgpt.cz')).toEqual([
|
|
7
|
+
{
|
|
8
|
+
fullName: null,
|
|
9
|
+
baseEmail: 'pavol@webgpt.cz',
|
|
10
|
+
fullEmail: 'pavol@webgpt.cz',
|
|
11
|
+
plus: [],
|
|
12
|
+
},
|
|
13
|
+
]);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('should work with simple emails', () => {
|
|
17
|
+
expect(parseEmailAddresses('pavol@webgpt.cz, jirka@webgpt.cz, tomas@webgpt.cz')).toEqual([
|
|
18
|
+
{
|
|
19
|
+
fullName: null,
|
|
20
|
+
baseEmail: 'pavol@webgpt.cz',
|
|
21
|
+
fullEmail: 'pavol@webgpt.cz',
|
|
22
|
+
plus: [],
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
fullName: null,
|
|
26
|
+
baseEmail: 'jirka@webgpt.cz',
|
|
27
|
+
fullEmail: 'jirka@webgpt.cz',
|
|
28
|
+
plus: [],
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
fullName: null,
|
|
32
|
+
baseEmail: 'tomas@webgpt.cz',
|
|
33
|
+
fullEmail: 'tomas@webgpt.cz',
|
|
34
|
+
plus: [],
|
|
35
|
+
},
|
|
36
|
+
]);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should work with fullname', () => {
|
|
40
|
+
expect(
|
|
41
|
+
parseEmailAddresses(
|
|
42
|
+
'Pavol Hejný <pavol@webgpt.cz>, Jirka <jirka@webgpt.cz>, "Tomáš Studeník" <tomas@webgpt.cz>',
|
|
43
|
+
),
|
|
44
|
+
).toEqual([
|
|
45
|
+
{
|
|
46
|
+
fullName: 'Pavol Hejný',
|
|
47
|
+
baseEmail: 'pavol@webgpt.cz',
|
|
48
|
+
fullEmail: 'pavol@webgpt.cz',
|
|
49
|
+
plus: [],
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
fullName: 'Jirka',
|
|
53
|
+
baseEmail: 'jirka@webgpt.cz',
|
|
54
|
+
fullEmail: 'jirka@webgpt.cz',
|
|
55
|
+
plus: [],
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
fullName: 'Tomáš Studeník',
|
|
59
|
+
baseEmail: 'tomas@webgpt.cz',
|
|
60
|
+
fullEmail: 'tomas@webgpt.cz',
|
|
61
|
+
plus: [],
|
|
62
|
+
},
|
|
63
|
+
]);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('not confused by comma', () => {
|
|
67
|
+
expect(parseEmailAddresses(', pavol@webgpt.cz, ')).toEqual([
|
|
68
|
+
{
|
|
69
|
+
fullName: null,
|
|
70
|
+
fullEmail: 'pavol@webgpt.cz',
|
|
71
|
+
baseEmail: 'pavol@webgpt.cz',
|
|
72
|
+
plus: [],
|
|
73
|
+
},
|
|
74
|
+
]);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('works on real-life example', () => {
|
|
78
|
+
expect(
|
|
79
|
+
parseEmailAddresses(
|
|
80
|
+
'"bob" <bob@bot.webgpt.cz>, "pavolto" <pavol+to@ptbk.io>, "Pavol" <pavol@collboard.com>',
|
|
81
|
+
),
|
|
82
|
+
).toEqual([
|
|
83
|
+
{
|
|
84
|
+
fullName: 'bob',
|
|
85
|
+
fullEmail: 'bob@bot.webgpt.cz',
|
|
86
|
+
baseEmail: 'bob@bot.webgpt.cz',
|
|
87
|
+
plus: [],
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
fullName: 'pavolto',
|
|
91
|
+
fullEmail: 'pavol+to@ptbk.io',
|
|
92
|
+
baseEmail: 'pavol@ptbk.io',
|
|
93
|
+
plus: ['to'],
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
fullName: 'Pavol',
|
|
97
|
+
fullEmail: 'pavol@collboard.com',
|
|
98
|
+
baseEmail: 'pavol@collboard.com',
|
|
99
|
+
plus: [],
|
|
100
|
+
},
|
|
101
|
+
]);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('throws on invalid email adresses', () => {
|
|
105
|
+
expect(() => parseEmailAddresses('Pavol, Hejný')).toThrowError(/Invalid email address/);
|
|
106
|
+
expect(() => parseEmailAddresses('Pavol Hejný <>')).toThrowError(/Invalid email address/);
|
|
107
|
+
expect(() => parseEmailAddresses('Pavol Hejný, <@webgpt.cz>')).toThrowError(/Invalid email address/);
|
|
108
|
+
expect(() => parseEmailAddresses('Pavol Hejný <webgpt.cz>')).toThrowError(/Invalid email address/);
|
|
109
|
+
expect(() => parseEmailAddresses('Pavol Hejný <pavol@>')).toThrowError(/Invalid email address/);
|
|
110
|
+
expect(() => parseEmailAddresses('Pavol Hejný <a@b>,')).toThrowError(/Invalid email address/);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* TODO: [🐫] This test fails because of aliased imports `import type { string_emails } from '@promptbook-local/types';`, fix it
|
|
117
|
+
*/
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { string_emails } from '@promptbook-local/types';
|
|
2
|
+
import { spaceTrim } from 'spacetrim';
|
|
3
|
+
import type { EmailAddress } from '../Email';
|
|
4
|
+
import { parseEmailAddress } from './parseEmailAddress';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Parses the email addresses into its components
|
|
8
|
+
*/
|
|
9
|
+
export function parseEmailAddresses(value: string_emails): Array<EmailAddress> {
|
|
10
|
+
const emailAddresses = value
|
|
11
|
+
.split(',')
|
|
12
|
+
.map((email) => spaceTrim(email))
|
|
13
|
+
.filter((email) => email !== '')
|
|
14
|
+
.map((email) => parseEmailAddress(email));
|
|
15
|
+
|
|
16
|
+
// console.log('parseEmailAddresses', value, '->', emailAddresses);
|
|
17
|
+
|
|
18
|
+
return emailAddresses;
|
|
19
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { describe, expect, it } from '@jest/globals';
|
|
2
|
+
import { stringifyEmailAddress } from './stringifyEmailAddress';
|
|
3
|
+
|
|
4
|
+
describe('how stringifyEmailAddress works', () => {
|
|
5
|
+
it('should work with simple email', () => {
|
|
6
|
+
expect(
|
|
7
|
+
stringifyEmailAddress({
|
|
8
|
+
fullName: null,
|
|
9
|
+
baseEmail: 'pavol@webgpt.cz',
|
|
10
|
+
fullEmail: 'pavol@webgpt.cz',
|
|
11
|
+
plus: [],
|
|
12
|
+
}),
|
|
13
|
+
).toBe('pavol@webgpt.cz');
|
|
14
|
+
expect(
|
|
15
|
+
stringifyEmailAddress({
|
|
16
|
+
fullName: null,
|
|
17
|
+
baseEmail: 'jirka@webgpt.cz',
|
|
18
|
+
fullEmail: 'jirka@webgpt.cz',
|
|
19
|
+
plus: [],
|
|
20
|
+
}),
|
|
21
|
+
).toBe('jirka@webgpt.cz');
|
|
22
|
+
expect(
|
|
23
|
+
stringifyEmailAddress({
|
|
24
|
+
fullName: null,
|
|
25
|
+
baseEmail: 'tomas@webgpt.cz',
|
|
26
|
+
fullEmail: 'tomas@webgpt.cz',
|
|
27
|
+
plus: [],
|
|
28
|
+
}),
|
|
29
|
+
).toBe('tomas@webgpt.cz');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should work with fullname', () => {
|
|
33
|
+
expect(
|
|
34
|
+
stringifyEmailAddress({
|
|
35
|
+
fullName: 'Pavol Hejný',
|
|
36
|
+
baseEmail: 'pavol@webgpt.cz',
|
|
37
|
+
fullEmail: 'pavol@webgpt.cz',
|
|
38
|
+
plus: [],
|
|
39
|
+
}),
|
|
40
|
+
).toBe('"Pavol Hejný" <pavol@webgpt.cz>');
|
|
41
|
+
expect(
|
|
42
|
+
stringifyEmailAddress({
|
|
43
|
+
fullName: 'Jirka',
|
|
44
|
+
baseEmail: 'jirka@webgpt.cz',
|
|
45
|
+
fullEmail: 'jirka@webgpt.cz',
|
|
46
|
+
plus: [],
|
|
47
|
+
}),
|
|
48
|
+
).toBe('"Jirka" <jirka@webgpt.cz>');
|
|
49
|
+
expect(
|
|
50
|
+
stringifyEmailAddress({
|
|
51
|
+
fullName: 'Tomáš Studeník',
|
|
52
|
+
baseEmail: 'tomas@webgpt.cz',
|
|
53
|
+
fullEmail: 'tomas@webgpt.cz',
|
|
54
|
+
plus: [],
|
|
55
|
+
}),
|
|
56
|
+
).toBe('"Tomáš Studeník" <tomas@webgpt.cz>');
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should work with plus', () => {
|
|
60
|
+
expect(
|
|
61
|
+
stringifyEmailAddress({
|
|
62
|
+
fullName: null,
|
|
63
|
+
baseEmail: 'pavol@webgpt.cz',
|
|
64
|
+
fullEmail: 'pavol+test@webgpt.cz',
|
|
65
|
+
plus: ['test'],
|
|
66
|
+
}),
|
|
67
|
+
).toBe('pavol+test@webgpt.cz');
|
|
68
|
+
expect(
|
|
69
|
+
stringifyEmailAddress({
|
|
70
|
+
fullName: null,
|
|
71
|
+
baseEmail: 'jirka@webgpt.cz',
|
|
72
|
+
fullEmail: 'jirka+test@webgpt.cz',
|
|
73
|
+
plus: ['test'],
|
|
74
|
+
}),
|
|
75
|
+
).toBe('jirka+test@webgpt.cz');
|
|
76
|
+
expect(
|
|
77
|
+
stringifyEmailAddress({
|
|
78
|
+
fullName: null,
|
|
79
|
+
baseEmail: 'tomas@webgpt.cz',
|
|
80
|
+
fullEmail: 'tomas+test+ainautes@webgpt.cz',
|
|
81
|
+
plus: ['test', 'ainautes'],
|
|
82
|
+
}),
|
|
83
|
+
).toBe('tomas+test+ainautes@webgpt.cz');
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should work with both fullname and plus', () => {
|
|
87
|
+
expect(
|
|
88
|
+
stringifyEmailAddress({
|
|
89
|
+
fullName: 'Pavol Hejný',
|
|
90
|
+
baseEmail: 'pavol@webgpt.cz',
|
|
91
|
+
fullEmail: 'pavol+test@webgpt.cz',
|
|
92
|
+
plus: ['test'],
|
|
93
|
+
}),
|
|
94
|
+
).toBe('"Pavol Hejný" <pavol+test@webgpt.cz>');
|
|
95
|
+
expect(
|
|
96
|
+
stringifyEmailAddress({
|
|
97
|
+
fullName: 'Jirka',
|
|
98
|
+
baseEmail: 'jirka@webgpt.cz',
|
|
99
|
+
fullEmail: 'jirka+test@webgpt.cz',
|
|
100
|
+
plus: ['test'],
|
|
101
|
+
}),
|
|
102
|
+
).toBe('"Jirka" <jirka+test@webgpt.cz>');
|
|
103
|
+
expect(
|
|
104
|
+
stringifyEmailAddress({
|
|
105
|
+
fullName: 'Tomáš Studeník',
|
|
106
|
+
baseEmail: 'tomas@webgpt.cz',
|
|
107
|
+
fullEmail: 'tomas+test+ainautes@webgpt.cz',
|
|
108
|
+
plus: ['test', 'ainautes'],
|
|
109
|
+
}),
|
|
110
|
+
).toBe('"Tomáš Studeník" <tomas+test+ainautes@webgpt.cz>');
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// TODO: [🎾] Implement and test here escaping
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* TODO: [🐫] This test fails because of aliased imports `import type { string_emails } from '@promptbook-local/types';`, fix it
|
|
119
|
+
*/
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { string_email } from '@promptbook-local/types';
|
|
2
|
+
import type { EmailAddress } from '../Email';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Makes string email from EmailAddress
|
|
6
|
+
*/
|
|
7
|
+
export function stringifyEmailAddress(emailAddress: EmailAddress): string_email {
|
|
8
|
+
const { fullEmail, fullName } = emailAddress;
|
|
9
|
+
|
|
10
|
+
if (fullName !== null) {
|
|
11
|
+
return `"${fullName}" <${fullEmail}>`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return fullEmail;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* TODO: [🎾] Implement and test here escaping
|
|
19
|
+
*/
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { describe, expect, it } from '@jest/globals';
|
|
2
|
+
import { stringifyEmailAddresses } from './stringifyEmailAddresses';
|
|
3
|
+
|
|
4
|
+
describe('how stringifyEmailAddresses works', () => {
|
|
5
|
+
it('should work with single email', () => {
|
|
6
|
+
expect(
|
|
7
|
+
stringifyEmailAddresses([
|
|
8
|
+
{
|
|
9
|
+
fullName: null,
|
|
10
|
+
baseEmail: 'pavol@webgpt.cz',
|
|
11
|
+
fullEmail: 'pavol@webgpt.cz',
|
|
12
|
+
plus: [],
|
|
13
|
+
},
|
|
14
|
+
]),
|
|
15
|
+
).toEqual('pavol@webgpt.cz');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should work with simple emails', () => {
|
|
19
|
+
expect(
|
|
20
|
+
stringifyEmailAddresses([
|
|
21
|
+
{
|
|
22
|
+
fullName: null,
|
|
23
|
+
baseEmail: 'pavol@webgpt.cz',
|
|
24
|
+
fullEmail: 'pavol@webgpt.cz',
|
|
25
|
+
plus: [],
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
fullName: null,
|
|
29
|
+
baseEmail: 'jirka@webgpt.cz',
|
|
30
|
+
fullEmail: 'jirka@webgpt.cz',
|
|
31
|
+
plus: [],
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
fullName: null,
|
|
35
|
+
baseEmail: 'tomas@webgpt.cz',
|
|
36
|
+
fullEmail: 'tomas@webgpt.cz',
|
|
37
|
+
plus: [],
|
|
38
|
+
},
|
|
39
|
+
]),
|
|
40
|
+
).toEqual('pavol@webgpt.cz, jirka@webgpt.cz, tomas@webgpt.cz');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should work with fullname', () => {
|
|
44
|
+
expect(
|
|
45
|
+
stringifyEmailAddresses([
|
|
46
|
+
{
|
|
47
|
+
fullName: 'Pavol Hejný',
|
|
48
|
+
baseEmail: 'pavol@webgpt.cz',
|
|
49
|
+
fullEmail: 'pavol@webgpt.cz',
|
|
50
|
+
plus: [],
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
fullName: 'Jiří Jahn',
|
|
54
|
+
baseEmail: 'jirka@webgpt.cz',
|
|
55
|
+
fullEmail: 'jirka@webgpt.cz',
|
|
56
|
+
plus: [],
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
fullName: 'Tomáš Studeník',
|
|
60
|
+
baseEmail: 'tomas@webgpt.cz',
|
|
61
|
+
fullEmail: 'tomas@webgpt.cz',
|
|
62
|
+
plus: [],
|
|
63
|
+
},
|
|
64
|
+
]),
|
|
65
|
+
).toEqual('"Pavol Hejný" <pavol@webgpt.cz>, "Jiří Jahn" <jirka@webgpt.cz>, "Tomáš Studeník" <tomas@webgpt.cz>');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// TODO: [🎾] Implement and test here escaping
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* TODO: [🐫] This test fails because of aliased imports `import type { string_emails } from '@promptbook-local/types';`, fix it
|
|
74
|
+
*/
|
package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddresses.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { string_emails } from '@promptbook-local/types';
|
|
2
|
+
import type { EmailAddress } from '../Email';
|
|
3
|
+
import { stringifyEmailAddress } from './stringifyEmailAddress';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Makes string email from multiple EmailAddress
|
|
7
|
+
*/
|
|
8
|
+
export function stringifyEmailAddresses(emailAddresses: Array<EmailAddress>): string_emails {
|
|
9
|
+
return emailAddresses.map((emailAddress) => stringifyEmailAddress(emailAddress)).join(', ');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* TODO: [🎾] Implement and test here escaping
|
|
14
|
+
*/
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { removeMarkdownFormatting } from '@promptbook-local/markdown-utils';
|
|
2
|
+
import type { really_any } from '@promptbook-local/types';
|
|
3
|
+
import sendgridEmailClient from '@sendgrid/mail';
|
|
4
|
+
import { marked } from 'marked';
|
|
5
|
+
import { MessageProvider } from '../../interfaces/MessageProvider';
|
|
6
|
+
import { OutboundEmail } from '../_common/Email';
|
|
7
|
+
import { parseEmailAddress } from '../_common/utils/parseEmailAddress';
|
|
8
|
+
|
|
9
|
+
export class SendgridMessageProvider implements MessageProvider {
|
|
10
|
+
constructor(private readonly apiKey: string) {
|
|
11
|
+
sendgridEmailClient.setApiKey(this.apiKey);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
public async send(message: OutboundEmail): Promise<really_any> {
|
|
15
|
+
const sender = message.sender;
|
|
16
|
+
const recipients = (Array.isArray(message.recipients) ? message.recipients : [message.recipients]).filter(
|
|
17
|
+
Boolean,
|
|
18
|
+
) as really_any[];
|
|
19
|
+
|
|
20
|
+
const text = removeMarkdownFormatting(message.content);
|
|
21
|
+
const html = await marked.parse(message.content);
|
|
22
|
+
|
|
23
|
+
const { fullEmail, fullName } = parseEmailAddress(sender);
|
|
24
|
+
|
|
25
|
+
const response = await sendgridEmailClient.send({
|
|
26
|
+
from: {
|
|
27
|
+
email: fullEmail,
|
|
28
|
+
name: fullName || undefined,
|
|
29
|
+
},
|
|
30
|
+
to: recipients.map((r) => {
|
|
31
|
+
const { fullEmail, fullName } = parseEmailAddress(r.email || r.baseEmail || r);
|
|
32
|
+
return {
|
|
33
|
+
email: fullEmail,
|
|
34
|
+
name: r.name || fullName || undefined,
|
|
35
|
+
};
|
|
36
|
+
}),
|
|
37
|
+
subject: message.metadata?.subject || 'No Subject',
|
|
38
|
+
text,
|
|
39
|
+
html,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return response;
|
|
43
|
+
}
|
|
44
|
+
}
|