@open-mercato/channel-resend 0.7.1-develop.7151.1.00d0391847

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.
Files changed (46) hide show
  1. package/.turbo/turbo-build.log +2 -0
  2. package/build.mjs +7 -0
  3. package/dist/index.js +5 -0
  4. package/dist/index.js.map +7 -0
  5. package/dist/modules/channel_resend/acl.js +10 -0
  6. package/dist/modules/channel_resend/acl.js.map +7 -0
  7. package/dist/modules/channel_resend/capabilities.js +10 -0
  8. package/dist/modules/channel_resend/capabilities.js.map +7 -0
  9. package/dist/modules/channel_resend/di.js +22 -0
  10. package/dist/modules/channel_resend/di.js.map +7 -0
  11. package/dist/modules/channel_resend/index.js +13 -0
  12. package/dist/modules/channel_resend/index.js.map +7 -0
  13. package/dist/modules/channel_resend/integration.js +61 -0
  14. package/dist/modules/channel_resend/integration.js.map +7 -0
  15. package/dist/modules/channel_resend/lib/adapter.js +109 -0
  16. package/dist/modules/channel_resend/lib/adapter.js.map +7 -0
  17. package/dist/modules/channel_resend/lib/credentials.js +9 -0
  18. package/dist/modules/channel_resend/lib/credentials.js.map +7 -0
  19. package/dist/modules/channel_resend/lib/health.js +43 -0
  20. package/dist/modules/channel_resend/lib/health.js.map +7 -0
  21. package/dist/modules/channel_resend/lib/preset.js +61 -0
  22. package/dist/modules/channel_resend/lib/preset.js.map +7 -0
  23. package/dist/modules/channel_resend/lib/system-email-config.js +13 -0
  24. package/dist/modules/channel_resend/lib/system-email-config.js.map +7 -0
  25. package/dist/modules/channel_resend/setup.js +30 -0
  26. package/dist/modules/channel_resend/setup.js.map +7 -0
  27. package/jest.config.cjs +33 -0
  28. package/package.json +87 -0
  29. package/src/index.ts +1 -0
  30. package/src/modules/channel_resend/__tests__/contracts.test.ts +27 -0
  31. package/src/modules/channel_resend/acl.ts +6 -0
  32. package/src/modules/channel_resend/capabilities.ts +8 -0
  33. package/src/modules/channel_resend/di.ts +20 -0
  34. package/src/modules/channel_resend/index.ts +9 -0
  35. package/src/modules/channel_resend/integration.ts +56 -0
  36. package/src/modules/channel_resend/lib/__tests__/adapter.test.ts +70 -0
  37. package/src/modules/channel_resend/lib/__tests__/health.test.ts +38 -0
  38. package/src/modules/channel_resend/lib/__tests__/preset.test.ts +179 -0
  39. package/src/modules/channel_resend/lib/adapter.ts +139 -0
  40. package/src/modules/channel_resend/lib/credentials.ts +8 -0
  41. package/src/modules/channel_resend/lib/health.ts +51 -0
  42. package/src/modules/channel_resend/lib/preset.ts +95 -0
  43. package/src/modules/channel_resend/lib/system-email-config.ts +10 -0
  44. package/src/modules/channel_resend/setup.ts +30 -0
  45. package/tsconfig.json +9 -0
  46. package/watch.mjs +7 -0
@@ -0,0 +1,2 @@
1
+ [build:channel-resend] found 12 entry points
2
+ [build:channel-resend] built successfully
package/build.mjs ADDED
@@ -0,0 +1,7 @@
1
+ import { dirname } from 'node:path'
2
+ import { fileURLToPath } from 'node:url'
3
+ import { buildPackage } from '../../scripts/build-package.mjs'
4
+
5
+ const packageDir = dirname(fileURLToPath(import.meta.url))
6
+
7
+ await buildPackage(packageDir, { name: 'channel-resend' })
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ import { metadata } from "./modules/channel_resend/index.js";
2
+ export {
3
+ metadata
4
+ };
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/index.ts"],
4
+ "sourcesContent": ["export { metadata } from './modules/channel_resend/index'\n"],
5
+ "mappings": "AAAA,SAAS,gBAAgB;",
6
+ "names": []
7
+ }
@@ -0,0 +1,10 @@
1
+ const features = [
2
+ { id: "channel_resend.view", title: "View Resend channel configuration", module: "channel_resend" },
3
+ { id: "channel_resend.configure", title: "Configure Resend channel defaults", module: "channel_resend" }
4
+ ];
5
+ var acl_default = features;
6
+ export {
7
+ acl_default as default,
8
+ features
9
+ };
10
+ //# sourceMappingURL=acl.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/modules/channel_resend/acl.ts"],
4
+ "sourcesContent": ["export const features = [\n { id: 'channel_resend.view', title: 'View Resend channel configuration', module: 'channel_resend' },\n { id: 'channel_resend.configure', title: 'Configure Resend channel defaults', module: 'channel_resend' },\n]\n\nexport default features\n"],
5
+ "mappings": "AAAO,MAAM,WAAW;AAAA,EACtB,EAAE,IAAI,uBAAuB,OAAO,qCAAqC,QAAQ,iBAAiB;AAAA,EAClG,EAAE,IAAI,4BAA4B,OAAO,qCAAqC,QAAQ,iBAAiB;AACzG;AAEA,IAAO,cAAQ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,10 @@
1
+ import { baseEmailCapabilities } from "@open-mercato/core/modules/communication_channels/lib/email-capabilities";
2
+ const resendCapabilities = {
3
+ ...baseEmailCapabilities,
4
+ fileSharing: false,
5
+ conversationHistory: false
6
+ };
7
+ export {
8
+ resendCapabilities
9
+ };
10
+ //# sourceMappingURL=capabilities.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/modules/channel_resend/capabilities.ts"],
4
+ "sourcesContent": ["import { baseEmailCapabilities } from '@open-mercato/core/modules/communication_channels/lib/email-capabilities'\nimport type { ChannelCapabilities } from '@open-mercato/core/modules/communication_channels/lib/adapter'\n\nexport const resendCapabilities: ChannelCapabilities = {\n ...baseEmailCapabilities,\n fileSharing: false,\n conversationHistory: false,\n}\n"],
5
+ "mappings": "AAAA,SAAS,6BAA6B;AAG/B,MAAM,qBAA0C;AAAA,EACrD,GAAG;AAAA,EACH,aAAa;AAAA,EACb,qBAAqB;AACvB;",
6
+ "names": []
7
+ }
@@ -0,0 +1,22 @@
1
+ import { asValue } from "awilix";
2
+ import {
3
+ hasChannelAdapter,
4
+ registerChannelAdapter
5
+ } from "@open-mercato/core/modules/communication_channels/lib/adapter-registry-singleton";
6
+ import { getResendChannelAdapter } from "./lib/adapter.js";
7
+ import { registerResendSystemEmailConfigResolver } from "./lib/system-email-config.js";
8
+ import { channelResendHealthCheck } from "./lib/health.js";
9
+ function register(container) {
10
+ registerResendSystemEmailConfigResolver();
11
+ if (!hasChannelAdapter("resend")) {
12
+ registerChannelAdapter(getResendChannelAdapter());
13
+ }
14
+ container.register({
15
+ channelResendAdapter: asValue(getResendChannelAdapter()),
16
+ channelResendHealthCheck: asValue(channelResendHealthCheck)
17
+ });
18
+ }
19
+ export {
20
+ register
21
+ };
22
+ //# sourceMappingURL=di.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/modules/channel_resend/di.ts"],
4
+ "sourcesContent": ["import { asValue } from 'awilix'\nimport type { AppContainer } from '@open-mercato/shared/lib/di/container'\nimport {\n hasChannelAdapter,\n registerChannelAdapter,\n} from '@open-mercato/core/modules/communication_channels/lib/adapter-registry-singleton'\nimport { getResendChannelAdapter } from './lib/adapter'\nimport { registerResendSystemEmailConfigResolver } from './lib/system-email-config'\nimport { channelResendHealthCheck } from './lib/health'\n\nexport function register(container: AppContainer): void {\n registerResendSystemEmailConfigResolver()\n if (!hasChannelAdapter('resend')) {\n registerChannelAdapter(getResendChannelAdapter())\n }\n container.register({\n channelResendAdapter: asValue(getResendChannelAdapter()),\n channelResendHealthCheck: asValue(channelResendHealthCheck),\n })\n}\n"],
5
+ "mappings": "AAAA,SAAS,eAAe;AAExB;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,+BAA+B;AACxC,SAAS,+CAA+C;AACxD,SAAS,gCAAgC;AAElC,SAAS,SAAS,WAA+B;AACtD,0CAAwC;AACxC,MAAI,CAAC,kBAAkB,QAAQ,GAAG;AAChC,2BAAuB,wBAAwB,CAAC;AAAA,EAClD;AACA,YAAU,SAAS;AAAA,IACjB,sBAAsB,QAAQ,wBAAwB,CAAC;AAAA,IACvD,0BAA0B,QAAQ,wBAAwB;AAAA,EAC5D,CAAC;AACH;",
6
+ "names": []
7
+ }
@@ -0,0 +1,13 @@
1
+ const metadata = {
2
+ id: "channel_resend",
3
+ name: "Resend Email Channel",
4
+ description: "Outbound Resend email channel provider for the Communications Hub.",
5
+ version: "0.1.0",
6
+ requires: ["communication_channels", "integrations"]
7
+ };
8
+ var channel_resend_default = metadata;
9
+ export {
10
+ channel_resend_default as default,
11
+ metadata
12
+ };
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/modules/channel_resend/index.ts"],
4
+ "sourcesContent": ["export const metadata = {\n id: 'channel_resend',\n name: 'Resend Email Channel',\n description: 'Outbound Resend email channel provider for the Communications Hub.',\n version: '0.1.0',\n requires: ['communication_channels', 'integrations'],\n}\n\nexport default metadata\n"],
5
+ "mappings": "AAAO,MAAM,WAAW;AAAA,EACtB,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,aAAa;AAAA,EACb,SAAS;AAAA,EACT,UAAU,CAAC,0BAA0B,cAAc;AACrD;AAEA,IAAO,yBAAQ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,61 @@
1
+ import { buildIntegrationDetailWidgetSpotId } from "@open-mercato/shared/modules/integrations/types";
2
+ const channelResendDetailWidgetSpotId = buildIntegrationDetailWidgetSpotId("channel_resend");
3
+ const integration = {
4
+ id: "channel_resend",
5
+ title: "Resend Email",
6
+ description: "Send transactional email through Resend using the Communications Hub.",
7
+ category: "communication",
8
+ hub: "communication_channels",
9
+ providerKey: "resend",
10
+ icon: "mail",
11
+ docsUrl: "https://resend.com/docs",
12
+ package: "@open-mercato/channel-resend",
13
+ version: "0.1.0",
14
+ author: "Open Mercato Team",
15
+ company: "Open Mercato",
16
+ license: "MIT",
17
+ tags: ["email", "resend", "transactional", "communication"],
18
+ detailPage: {
19
+ widgetSpotId: channelResendDetailWidgetSpotId
20
+ },
21
+ apiVersions: [
22
+ {
23
+ id: "resend-v2",
24
+ label: "Resend Email API",
25
+ status: "stable",
26
+ default: true,
27
+ changelog: "Initial outbound transactional email adapter."
28
+ }
29
+ ],
30
+ healthCheck: { service: "channelResendHealthCheck" },
31
+ credentials: {
32
+ fields: [
33
+ {
34
+ key: "apiKey",
35
+ label: "API key",
36
+ type: "secret",
37
+ required: true,
38
+ helpText: "Resend API key used for outbound transactional email."
39
+ },
40
+ {
41
+ key: "fromAddress",
42
+ label: "From address",
43
+ type: "text",
44
+ required: true,
45
+ placeholder: "no-reply@example.com",
46
+ helpText: "Verified sender address or domain identity in Resend."
47
+ }
48
+ ]
49
+ }
50
+ };
51
+ const integrations = [integration];
52
+ const bundles = [];
53
+ const bundle = void 0;
54
+ export {
55
+ bundle,
56
+ bundles,
57
+ channelResendDetailWidgetSpotId,
58
+ integration,
59
+ integrations
60
+ };
61
+ //# sourceMappingURL=integration.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/modules/channel_resend/integration.ts"],
4
+ "sourcesContent": ["import { buildIntegrationDetailWidgetSpotId, type IntegrationBundle, type IntegrationDefinition } from '@open-mercato/shared/modules/integrations/types'\n\nexport const channelResendDetailWidgetSpotId = buildIntegrationDetailWidgetSpotId('channel_resend')\n\nexport const integration: IntegrationDefinition = {\n id: 'channel_resend',\n title: 'Resend Email',\n description: 'Send transactional email through Resend using the Communications Hub.',\n category: 'communication',\n hub: 'communication_channels',\n providerKey: 'resend',\n icon: 'mail',\n docsUrl: 'https://resend.com/docs',\n package: '@open-mercato/channel-resend',\n version: '0.1.0',\n author: 'Open Mercato Team',\n company: 'Open Mercato',\n license: 'MIT',\n tags: ['email', 'resend', 'transactional', 'communication'],\n detailPage: {\n widgetSpotId: channelResendDetailWidgetSpotId,\n },\n apiVersions: [\n {\n id: 'resend-v2',\n label: 'Resend Email API',\n status: 'stable',\n default: true,\n changelog: 'Initial outbound transactional email adapter.',\n },\n ],\n healthCheck: { service: 'channelResendHealthCheck' },\n credentials: {\n fields: [\n {\n key: 'apiKey',\n label: 'API key',\n type: 'secret',\n required: true,\n helpText: 'Resend API key used for outbound transactional email.',\n },\n {\n key: 'fromAddress',\n label: 'From address',\n type: 'text',\n required: true,\n placeholder: 'no-reply@example.com',\n helpText: 'Verified sender address or domain identity in Resend.',\n },\n ],\n },\n}\n\nexport const integrations: IntegrationDefinition[] = [integration]\nexport const bundles: IntegrationBundle[] = []\nexport const bundle: IntegrationBundle | undefined = undefined\n"],
5
+ "mappings": "AAAA,SAAS,0CAA8F;AAEhG,MAAM,kCAAkC,mCAAmC,gBAAgB;AAE3F,MAAM,cAAqC;AAAA,EAChD,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,aAAa;AAAA,EACb,UAAU;AAAA,EACV,KAAK;AAAA,EACL,aAAa;AAAA,EACb,MAAM;AAAA,EACN,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,SAAS;AAAA,EACT,MAAM,CAAC,SAAS,UAAU,iBAAiB,eAAe;AAAA,EAC1D,YAAY;AAAA,IACV,cAAc;AAAA,EAChB;AAAA,EACA,aAAa;AAAA,IACX;AAAA,MACE,IAAI;AAAA,MACJ,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,WAAW;AAAA,IACb;AAAA,EACF;AAAA,EACA,aAAa,EAAE,SAAS,2BAA2B;AAAA,EACnD,aAAa;AAAA,IACX,QAAQ;AAAA,MACN;AAAA,QACE,KAAK;AAAA,QACL,OAAO;AAAA,QACP,MAAM;AAAA,QACN,UAAU;AAAA,QACV,UAAU;AAAA,MACZ;AAAA,MACA;AAAA,QACE,KAAK;AAAA,QACL,OAAO;AAAA,QACP,MAAM;AAAA,QACN,UAAU;AAAA,QACV,aAAa;AAAA,QACb,UAAU;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AACF;AAEO,MAAM,eAAwC,CAAC,WAAW;AAC1D,MAAM,UAA+B,CAAC;AACtC,MAAM,SAAwC;",
6
+ "names": []
7
+ }
@@ -0,0 +1,109 @@
1
+ import { Resend } from "resend";
2
+ import {
3
+ htmlToText,
4
+ sanitizeHeaderValue,
5
+ stringOrUndefined,
6
+ toAddressList
7
+ } from "@open-mercato/core/modules/communication_channels/lib/email-mime";
8
+ import { resendCapabilities } from "../capabilities.js";
9
+ import { resendCredentialsSchema } from "./credentials.js";
10
+ function resolveResendErrorMessage(result) {
11
+ const value = result;
12
+ if (typeof value.error === "string") return value.error;
13
+ if (value.error && typeof value.error === "object" && "message" in value.error) {
14
+ const message = value.error.message;
15
+ return typeof message === "string" ? message : null;
16
+ }
17
+ return null;
18
+ }
19
+ function attachmentsFromMeta(value) {
20
+ if (!Array.isArray(value)) return void 0;
21
+ const attachments = value.flatMap((item) => {
22
+ if (!item || typeof item !== "object") return [];
23
+ const record = item;
24
+ const filename = stringOrUndefined(record.filename);
25
+ const content = stringOrUndefined(record.content);
26
+ if (!filename || !content) return [];
27
+ const contentType = stringOrUndefined(record.contentType);
28
+ return [{ filename, content, ...contentType ? { contentType } : {} }];
29
+ });
30
+ return attachments.length ? attachments : void 0;
31
+ }
32
+ class ResendChannelAdapter {
33
+ constructor() {
34
+ this.providerKey = "resend";
35
+ this.channelType = "email";
36
+ this.capabilities = resendCapabilities;
37
+ }
38
+ async sendMessage(input) {
39
+ const credentials = resendCredentialsSchema.parse(input.credentials);
40
+ const meta = input.metadata ?? {};
41
+ const to = Array.isArray(meta.to) ? meta.to : [];
42
+ if (to.length === 0) {
43
+ return { externalMessageId: "", status: "failed", error: "[internal] Email send requires at least one recipient" };
44
+ }
45
+ const subject = stringOrUndefined(meta.subject);
46
+ if (!subject) {
47
+ return { externalMessageId: "", status: "failed", error: "[internal] Email send requires a subject" };
48
+ }
49
+ const client = new Resend(credentials.apiKey);
50
+ const resendAttachments = attachmentsFromMeta(meta.attachments);
51
+ const basePayload = {
52
+ from: stringOrUndefined(meta.from) ?? credentials.fromAddress,
53
+ to,
54
+ subject,
55
+ ...stringOrUndefined(meta.replyTo) ? { replyTo: stringOrUndefined(meta.replyTo) } : {},
56
+ ...resendAttachments?.length ? { attachments: resendAttachments } : {}
57
+ };
58
+ const payload = input.content.html ? { ...basePayload, html: input.content.html, ...input.content.text ? { text: input.content.text } : {} } : { ...basePayload, text: input.content.text ?? "" };
59
+ const result = await client.emails.send(payload);
60
+ const errorMessage = resolveResendErrorMessage(result);
61
+ if (errorMessage) {
62
+ return { externalMessageId: "", status: "failed", error: `RESEND_SEND_FAILED: ${errorMessage}` };
63
+ }
64
+ const sentId = result.data?.id;
65
+ return {
66
+ externalMessageId: sentId || `resend:${Date.now()}`,
67
+ conversationId: input.conversationId,
68
+ status: "sent"
69
+ };
70
+ }
71
+ async verifyWebhook(_input) {
72
+ return { raw: {}, eventType: "other", metadata: { reason: "resend-system-email-outbound-only" } };
73
+ }
74
+ async getStatus(_input) {
75
+ return { status: "sent" };
76
+ }
77
+ async normalizeInbound(_raw) {
78
+ throw new Error("[internal] Resend system email adapter is outbound-only");
79
+ }
80
+ async convertOutbound(input) {
81
+ const meta = input.channelMetadata ?? {};
82
+ const to = toAddressList(meta.to).map(sanitizeHeaderValue);
83
+ const subject = stringOrUndefined(meta.subject);
84
+ const html = input.bodyFormat === "html" ? input.body : void 0;
85
+ const text = input.bodyFormat === "html" ? htmlToText(input.body) : input.body;
86
+ return {
87
+ content: {
88
+ text,
89
+ html,
90
+ bodyFormat: input.bodyFormat
91
+ },
92
+ metadata: {
93
+ to,
94
+ subject,
95
+ from: stringOrUndefined(meta.from),
96
+ replyTo: stringOrUndefined(meta.replyTo),
97
+ attachments: attachmentsFromMeta(meta.attachments)
98
+ }
99
+ };
100
+ }
101
+ }
102
+ const adapter = new ResendChannelAdapter();
103
+ function getResendChannelAdapter() {
104
+ return adapter;
105
+ }
106
+ export {
107
+ getResendChannelAdapter
108
+ };
109
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/channel_resend/lib/adapter.ts"],
4
+ "sourcesContent": ["import { Resend } from 'resend'\nimport type {\n ChannelAdapter,\n ChannelNativeContent,\n ConvertOutboundInput,\n GetMessageStatusInput,\n InboundMessage,\n MessageStatus,\n NormalizedInboundMessage,\n SendMessageInput,\n SendMessageResult,\n VerifyWebhookInput,\n} from '@open-mercato/core/modules/communication_channels/lib/adapter'\nimport {\n htmlToText,\n sanitizeHeaderValue,\n stringOrUndefined,\n toAddressList,\n} from '@open-mercato/core/modules/communication_channels/lib/email-mime'\nimport { resendCapabilities } from '../capabilities'\nimport { resendCredentialsSchema } from './credentials'\n\ntype ResendAttachment = {\n filename: string\n content: string\n contentType?: string\n}\n\ntype ResendSendResult = {\n data?: { id?: string | null } | null\n error?: unknown\n}\n\nfunction resolveResendErrorMessage(result: unknown): string | null {\n const value = result as ResendSendResult\n if (typeof value.error === 'string') return value.error\n if (value.error && typeof value.error === 'object' && 'message' in value.error) {\n const message = (value.error as { message?: unknown }).message\n return typeof message === 'string' ? message : null\n }\n return null\n}\n\nfunction attachmentsFromMeta(value: unknown): ResendAttachment[] | undefined {\n if (!Array.isArray(value)) return undefined\n const attachments = value.flatMap((item): ResendAttachment[] => {\n if (!item || typeof item !== 'object') return []\n const record = item as Record<string, unknown>\n const filename = stringOrUndefined(record.filename)\n const content = stringOrUndefined(record.content)\n if (!filename || !content) return []\n const contentType = stringOrUndefined(record.contentType)\n return [{ filename, content, ...(contentType ? { contentType } : {}) }]\n })\n return attachments.length ? attachments : undefined\n}\n\nclass ResendChannelAdapter implements ChannelAdapter {\n readonly providerKey = 'resend'\n readonly channelType = 'email'\n readonly capabilities = resendCapabilities\n\n async sendMessage(input: SendMessageInput): Promise<SendMessageResult> {\n const credentials = resendCredentialsSchema.parse(input.credentials)\n const meta = (input.metadata ?? {}) as Record<string, unknown>\n const to = Array.isArray(meta.to) ? (meta.to as string[]) : []\n if (to.length === 0) {\n return { externalMessageId: '', status: 'failed', error: '[internal] Email send requires at least one recipient' }\n }\n const subject = stringOrUndefined(meta.subject)\n if (!subject) {\n return { externalMessageId: '', status: 'failed', error: '[internal] Email send requires a subject' }\n }\n\n const client = new Resend(credentials.apiKey)\n const resendAttachments = attachmentsFromMeta(meta.attachments)\n const basePayload = {\n from: stringOrUndefined(meta.from) ?? credentials.fromAddress,\n to,\n subject,\n ...(stringOrUndefined(meta.replyTo) ? { replyTo: stringOrUndefined(meta.replyTo) } : {}),\n ...(resendAttachments?.length ? { attachments: resendAttachments } : {}),\n }\n const payload: Parameters<typeof client.emails.send>[0] = input.content.html\n ? { ...basePayload, html: input.content.html, ...(input.content.text ? { text: input.content.text } : {}) }\n : { ...basePayload, text: input.content.text ?? '' }\n const result = await client.emails.send(payload)\n const errorMessage = resolveResendErrorMessage(result)\n if (errorMessage) {\n return { externalMessageId: '', status: 'failed', error: `RESEND_SEND_FAILED: ${errorMessage}` }\n }\n const sentId = (result as ResendSendResult).data?.id\n return {\n externalMessageId: sentId || `resend:${Date.now()}`,\n conversationId: input.conversationId,\n status: 'sent',\n }\n }\n\n async verifyWebhook(_input: VerifyWebhookInput): Promise<InboundMessage> {\n return { raw: {}, eventType: 'other', metadata: { reason: 'resend-system-email-outbound-only' } }\n }\n\n async getStatus(_input: GetMessageStatusInput): Promise<MessageStatus> {\n return { status: 'sent' }\n }\n\n async normalizeInbound(_raw: InboundMessage): Promise<NormalizedInboundMessage> {\n throw new Error('[internal] Resend system email adapter is outbound-only')\n }\n\n async convertOutbound(input: ConvertOutboundInput): Promise<ChannelNativeContent> {\n const meta = (input.channelMetadata ?? {}) as Record<string, unknown>\n const to = toAddressList(meta.to).map(sanitizeHeaderValue)\n const subject = stringOrUndefined(meta.subject)\n const html = input.bodyFormat === 'html' ? input.body : undefined\n const text = input.bodyFormat === 'html' ? htmlToText(input.body) : input.body\n return {\n content: {\n text,\n html,\n bodyFormat: input.bodyFormat,\n },\n metadata: {\n to,\n subject,\n from: stringOrUndefined(meta.from),\n replyTo: stringOrUndefined(meta.replyTo),\n attachments: attachmentsFromMeta(meta.attachments),\n },\n }\n }\n}\n\nconst adapter = new ResendChannelAdapter()\n\nexport function getResendChannelAdapter(): ChannelAdapter {\n return adapter\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,9 @@
1
+ import { z } from "zod";
2
+ const resendCredentialsSchema = z.object({
3
+ apiKey: z.string().min(1),
4
+ fromAddress: z.string().email()
5
+ });
6
+ export {
7
+ resendCredentialsSchema
8
+ };
9
+ //# sourceMappingURL=credentials.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/channel_resend/lib/credentials.ts"],
4
+ "sourcesContent": ["import { z } from 'zod'\n\nexport const resendCredentialsSchema = z.object({\n apiKey: z.string().min(1),\n fromAddress: z.string().email(),\n})\n\nexport type ResendCredentials = z.infer<typeof resendCredentialsSchema>\n"],
5
+ "mappings": "AAAA,SAAS,SAAS;AAEX,MAAM,0BAA0B,EAAE,OAAO;AAAA,EAC9C,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACxB,aAAa,EAAE,OAAO,EAAE,MAAM;AAChC,CAAC;",
6
+ "names": []
7
+ }
@@ -0,0 +1,43 @@
1
+ import { fetchWithTimeout } from "@open-mercato/shared/lib/http/fetchWithTimeout";
2
+ import { resendCredentialsSchema } from "./credentials.js";
3
+ const channelResendHealthCheck = {
4
+ async check(credentials, _scope) {
5
+ const parsed = resendCredentialsSchema.safeParse(credentials ?? {});
6
+ if (!parsed.success) {
7
+ return {
8
+ status: "unhealthy",
9
+ message: `Resend credentials invalid: ${parsed.error.issues[0]?.message ?? "unknown validation error"}`,
10
+ details: { reason: "invalid_credentials" }
11
+ };
12
+ }
13
+ try {
14
+ const response = await fetchWithTimeout("https://api.resend.com/domains?limit=1", {
15
+ headers: { Authorization: `Bearer ${parsed.data.apiKey}` },
16
+ timeoutMs: 8e3
17
+ });
18
+ if (!response.ok) {
19
+ return {
20
+ status: "unhealthy",
21
+ message: `Resend API rejected the credentials with status ${response.status}`,
22
+ details: { reason: "api_rejected", status: response.status }
23
+ };
24
+ }
25
+ return {
26
+ status: "healthy",
27
+ message: "Resend API credentials are valid",
28
+ details: { endpoint: "domains" }
29
+ };
30
+ } catch (error) {
31
+ const message = error instanceof Error ? error.message : "Resend API request failed";
32
+ return {
33
+ status: "unhealthy",
34
+ message: `Resend health check failed: ${message}`,
35
+ details: { reason: "request_failed" }
36
+ };
37
+ }
38
+ }
39
+ };
40
+ export {
41
+ channelResendHealthCheck
42
+ };
43
+ //# sourceMappingURL=health.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/channel_resend/lib/health.ts"],
4
+ "sourcesContent": ["import type { IntegrationScope } from '@open-mercato/shared/modules/integrations/types'\nimport { fetchWithTimeout } from '@open-mercato/shared/lib/http/fetchWithTimeout'\nimport { resendCredentialsSchema } from './credentials'\n\ntype HealthCheckResult = {\n status: 'healthy' | 'unhealthy'\n message: string\n details: Record<string, unknown>\n}\n\nexport const channelResendHealthCheck = {\n async check(\n credentials: Record<string, unknown> | null,\n _scope: IntegrationScope,\n ): Promise<HealthCheckResult> {\n const parsed = resendCredentialsSchema.safeParse(credentials ?? {})\n if (!parsed.success) {\n return {\n status: 'unhealthy',\n message: `Resend credentials invalid: ${parsed.error.issues[0]?.message ?? 'unknown validation error'}`,\n details: { reason: 'invalid_credentials' },\n }\n }\n\n try {\n const response = await fetchWithTimeout('https://api.resend.com/domains?limit=1', {\n headers: { Authorization: `Bearer ${parsed.data.apiKey}` },\n timeoutMs: 8_000,\n })\n if (!response.ok) {\n return {\n status: 'unhealthy',\n message: `Resend API rejected the credentials with status ${response.status}`,\n details: { reason: 'api_rejected', status: response.status },\n }\n }\n return {\n status: 'healthy',\n message: 'Resend API credentials are valid',\n details: { endpoint: 'domains' },\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Resend API request failed'\n return {\n status: 'unhealthy',\n message: `Resend health check failed: ${message}`,\n details: { reason: 'request_failed' },\n }\n }\n },\n}\n"],
5
+ "mappings": "AACA,SAAS,wBAAwB;AACjC,SAAS,+BAA+B;AAQjC,MAAM,2BAA2B;AAAA,EACtC,MAAM,MACJ,aACA,QAC4B;AAC5B,UAAM,SAAS,wBAAwB,UAAU,eAAe,CAAC,CAAC;AAClE,QAAI,CAAC,OAAO,SAAS;AACnB,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,SAAS,+BAA+B,OAAO,MAAM,OAAO,CAAC,GAAG,WAAW,0BAA0B;AAAA,QACrG,SAAS,EAAE,QAAQ,sBAAsB;AAAA,MAC3C;AAAA,IACF;AAEA,QAAI;AACF,YAAM,WAAW,MAAM,iBAAiB,0CAA0C;AAAA,QAChF,SAAS,EAAE,eAAe,UAAU,OAAO,KAAK,MAAM,GAAG;AAAA,QACzD,WAAW;AAAA,MACb,CAAC;AACD,UAAI,CAAC,SAAS,IAAI;AAChB,eAAO;AAAA,UACL,QAAQ;AAAA,UACR,SAAS,mDAAmD,SAAS,MAAM;AAAA,UAC3E,SAAS,EAAE,QAAQ,gBAAgB,QAAQ,SAAS,OAAO;AAAA,QAC7D;AAAA,MACF;AACA,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,SAAS,EAAE,UAAU,UAAU;AAAA,MACjC;AAAA,IACF,SAAS,OAAO;AACd,YAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,SAAS,+BAA+B,OAAO;AAAA,QAC/C,SAAS,EAAE,QAAQ,iBAAiB;AAAA,MACtC;AAAA,IACF;AAAA,EACF;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,61 @@
1
+ import { normalizeEnvString, resolveDefaultEmailFromAddress } from "@open-mercato/shared/lib/email/config";
2
+ import { createLogger } from "@open-mercato/shared/lib/logger";
3
+ import { ensureSystemEmailChannel } from "@open-mercato/core/modules/communication_channels/lib/ensure-system-email-channel";
4
+ import { isSelectedSystemEmailProvider } from "@open-mercato/core/modules/communication_channels/lib/system-email-provider-config";
5
+ import { resendCapabilities } from "../capabilities.js";
6
+ const logger = createLogger("channel_resend");
7
+ function readResendEnvPreset() {
8
+ const apiKey = normalizeEnvString(process.env.RESEND_API_KEY);
9
+ const fromAddress = resolveDefaultEmailFromAddress();
10
+ if (!apiKey || !fromAddress) {
11
+ if (apiKey && !fromAddress) {
12
+ logger.warn("RESEND_API_KEY is set but no from-address is configured; skipping Resend preset", {
13
+ remedy: "set NOTIFICATIONS_EMAIL_FROM, EMAIL_FROM, or ADMIN_EMAIL"
14
+ });
15
+ }
16
+ return null;
17
+ }
18
+ return { apiKey, fromAddress };
19
+ }
20
+ async function applyResendEnvPreset(ctx) {
21
+ if (!isSelectedSystemEmailProvider("resend")) return;
22
+ const preset = readResendEnvPreset();
23
+ if (!preset) return;
24
+ let credentialsService;
25
+ try {
26
+ credentialsService = ctx.container.resolve("integrationCredentialsService");
27
+ } catch {
28
+ return;
29
+ }
30
+ await credentialsService.save("channel_resend", preset, {
31
+ tenantId: ctx.tenantId,
32
+ organizationId: ctx.organizationId,
33
+ userId: null
34
+ });
35
+ try {
36
+ const integrationStateService = ctx.container.resolve("integrationStateService");
37
+ await integrationStateService.upsert("channel_resend", { isEnabled: true }, {
38
+ tenantId: ctx.tenantId,
39
+ organizationId: ctx.organizationId
40
+ });
41
+ } catch (err) {
42
+ logger.warn("Failed to enable the Resend integration state; Integrations will read Disabled while email is live", {
43
+ err,
44
+ tenantId: ctx.tenantId,
45
+ organizationId: ctx.organizationId
46
+ });
47
+ }
48
+ await ensureSystemEmailChannel(ctx.em, {
49
+ tenantId: ctx.tenantId,
50
+ organizationId: ctx.organizationId,
51
+ providerKey: "resend",
52
+ externalIdentifier: preset.fromAddress,
53
+ displayName: "Resend system email",
54
+ capabilities: { ...resendCapabilities }
55
+ });
56
+ }
57
+ export {
58
+ applyResendEnvPreset,
59
+ readResendEnvPreset
60
+ };
61
+ //# sourceMappingURL=preset.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/channel_resend/lib/preset.ts"],
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { AppContainer } from '@open-mercato/shared/lib/di/container'\nimport { normalizeEnvString, resolveDefaultEmailFromAddress } from '@open-mercato/shared/lib/email/config'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { ensureSystemEmailChannel } from '@open-mercato/core/modules/communication_channels/lib/ensure-system-email-channel'\nimport { isSelectedSystemEmailProvider } from '@open-mercato/core/modules/communication_channels/lib/system-email-provider-config'\nimport { resendCapabilities } from '../capabilities'\n\nconst logger = createLogger('channel_resend')\n\ntype PresetScope = {\n em: EntityManager\n container: AppContainer\n tenantId: string\n organizationId: string\n}\n\ntype CredentialsServiceLike = {\n save: (\n integrationId: string,\n credentials: Record<string, unknown>,\n scope: { organizationId: string; tenantId: string; userId?: string | null },\n ) => Promise<void>\n}\n\ntype IntegrationStateServiceLike = {\n upsert: (\n integrationId: string,\n input: { isEnabled: boolean },\n scope: { organizationId: string; tenantId: string },\n ) => Promise<unknown>\n}\n\nexport function readResendEnvPreset(): { apiKey: string; fromAddress: string } | null {\n const apiKey = normalizeEnvString(process.env.RESEND_API_KEY)\n const fromAddress = resolveDefaultEmailFromAddress()\n if (!apiKey || !fromAddress) {\n // A key with no from-address is the trap worth naming: `.env.example` documents RESEND_API_KEY\n // prominently but the from-address separately, so this combination looks configured and seeds\n // nothing. Say so rather than returning null in silence.\n if (apiKey && !fromAddress) {\n logger.warn('RESEND_API_KEY is set but no from-address is configured; skipping Resend preset', {\n remedy: 'set NOTIFICATIONS_EMAIL_FROM, EMAIL_FROM, or ADMIN_EMAIL',\n })\n }\n return null\n }\n return { apiKey, fromAddress }\n}\n\nexport async function applyResendEnvPreset(ctx: PresetScope): Promise<void> {\n // Only the provider this instance actually selected seeds anything, so a leftover RESEND_API_KEY\n // on an instance that moved to `SYSTEM_EMAIL_PROVIDER=ses` no longer advertises an Enabled Resend\n // integration and a connected channel that nothing sends through. `resend` is the default, so the\n // documented `RESEND_API_KEY`-only setup is unaffected.\n if (!isSelectedSystemEmailProvider('resend')) return\n const preset = readResendEnvPreset()\n if (!preset) return\n\n let credentialsService: CredentialsServiceLike\n try {\n credentialsService = ctx.container.resolve('integrationCredentialsService') as CredentialsServiceLike\n } catch {\n return\n }\n\n await credentialsService.save('channel_resend', preset, {\n tenantId: ctx.tenantId,\n organizationId: ctx.organizationId,\n userId: null,\n })\n\n try {\n const integrationStateService = ctx.container.resolve('integrationStateService') as IntegrationStateServiceLike\n await integrationStateService.upsert('channel_resend', { isEnabled: true }, {\n tenantId: ctx.tenantId,\n organizationId: ctx.organizationId,\n })\n } catch (err) {\n logger.warn('Failed to enable the Resend integration state; Integrations will read Disabled while email is live', {\n err,\n tenantId: ctx.tenantId,\n organizationId: ctx.organizationId,\n })\n }\n\n await ensureSystemEmailChannel(ctx.em, {\n tenantId: ctx.tenantId,\n organizationId: ctx.organizationId,\n providerKey: 'resend',\n externalIdentifier: preset.fromAddress,\n displayName: 'Resend system email',\n capabilities: { ...resendCapabilities },\n })\n}\n"],
5
+ "mappings": "AAEA,SAAS,oBAAoB,sCAAsC;AACnE,SAAS,oBAAoB;AAC7B,SAAS,gCAAgC;AACzC,SAAS,qCAAqC;AAC9C,SAAS,0BAA0B;AAEnC,MAAM,SAAS,aAAa,gBAAgB;AAyBrC,SAAS,sBAAsE;AACpF,QAAM,SAAS,mBAAmB,QAAQ,IAAI,cAAc;AAC5D,QAAM,cAAc,+BAA+B;AACnD,MAAI,CAAC,UAAU,CAAC,aAAa;AAI3B,QAAI,UAAU,CAAC,aAAa;AAC1B,aAAO,KAAK,mFAAmF;AAAA,QAC7F,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT;AACA,SAAO,EAAE,QAAQ,YAAY;AAC/B;AAEA,eAAsB,qBAAqB,KAAiC;AAK1E,MAAI,CAAC,8BAA8B,QAAQ,EAAG;AAC9C,QAAM,SAAS,oBAAoB;AACnC,MAAI,CAAC,OAAQ;AAEb,MAAI;AACJ,MAAI;AACF,yBAAqB,IAAI,UAAU,QAAQ,+BAA+B;AAAA,EAC5E,QAAQ;AACN;AAAA,EACF;AAEA,QAAM,mBAAmB,KAAK,kBAAkB,QAAQ;AAAA,IACtD,UAAU,IAAI;AAAA,IACd,gBAAgB,IAAI;AAAA,IACpB,QAAQ;AAAA,EACV,CAAC;AAED,MAAI;AACF,UAAM,0BAA0B,IAAI,UAAU,QAAQ,yBAAyB;AAC/E,UAAM,wBAAwB,OAAO,kBAAkB,EAAE,WAAW,KAAK,GAAG;AAAA,MAC1E,UAAU,IAAI;AAAA,MACd,gBAAgB,IAAI;AAAA,IACtB,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,WAAO,KAAK,sGAAsG;AAAA,MAChH;AAAA,MACA,UAAU,IAAI;AAAA,MACd,gBAAgB,IAAI;AAAA,IACtB,CAAC;AAAA,EACH;AAEA,QAAM,yBAAyB,IAAI,IAAI;AAAA,IACrC,UAAU,IAAI;AAAA,IACd,gBAAgB,IAAI;AAAA,IACpB,aAAa;AAAA,IACb,oBAAoB,OAAO;AAAA,IAC3B,aAAa;AAAA,IACb,cAAc,EAAE,GAAG,mBAAmB;AAAA,EACxC,CAAC;AACH;",
6
+ "names": []
7
+ }
@@ -0,0 +1,13 @@
1
+ import { registerSystemEmailProviderConfigResolver } from "@open-mercato/core/modules/communication_channels/lib/system-email-provider-config";
2
+ import { readResendEnvPreset } from "./preset.js";
3
+ function registerResendSystemEmailConfigResolver() {
4
+ registerSystemEmailProviderConfigResolver({
5
+ providerKey: "resend",
6
+ isConfigured: () => Boolean(readResendEnvPreset()),
7
+ resolveCredentials: ({ fromAddress }) => readResendEnvPreset() ?? { fromAddress }
8
+ });
9
+ }
10
+ export {
11
+ registerResendSystemEmailConfigResolver
12
+ };
13
+ //# sourceMappingURL=system-email-config.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/channel_resend/lib/system-email-config.ts"],
4
+ "sourcesContent": ["import { registerSystemEmailProviderConfigResolver } from '@open-mercato/core/modules/communication_channels/lib/system-email-provider-config'\nimport { readResendEnvPreset } from './preset'\n\nexport function registerResendSystemEmailConfigResolver(): void {\n registerSystemEmailProviderConfigResolver({\n providerKey: 'resend',\n isConfigured: () => Boolean(readResendEnvPreset()),\n resolveCredentials: ({ fromAddress }) => readResendEnvPreset() ?? { fromAddress },\n })\n}\n"],
5
+ "mappings": "AAAA,SAAS,iDAAiD;AAC1D,SAAS,2BAA2B;AAE7B,SAAS,0CAAgD;AAC9D,4CAA0C;AAAA,IACxC,aAAa;AAAA,IACb,cAAc,MAAM,QAAQ,oBAAoB,CAAC;AAAA,IACjD,oBAAoB,CAAC,EAAE,YAAY,MAAM,oBAAoB,KAAK,EAAE,YAAY;AAAA,EAClF,CAAC;AACH;",
6
+ "names": []
7
+ }
@@ -0,0 +1,30 @@
1
+ import {
2
+ hasChannelAdapter,
3
+ registerChannelAdapter
4
+ } from "@open-mercato/core/modules/communication_channels/lib/adapter-registry-singleton";
5
+ import { getResendChannelAdapter } from "./lib/adapter.js";
6
+ import { applyResendEnvPreset } from "./lib/preset.js";
7
+ import { registerResendSystemEmailConfigResolver } from "./lib/system-email-config.js";
8
+ function ensureResendAdapterRegistered() {
9
+ if (hasChannelAdapter("resend")) return;
10
+ registerChannelAdapter(getResendChannelAdapter());
11
+ }
12
+ ensureResendAdapterRegistered();
13
+ registerResendSystemEmailConfigResolver();
14
+ const setup = {
15
+ defaultRoleFeatures: {
16
+ superadmin: ["channel_resend.view", "channel_resend.configure"],
17
+ admin: ["channel_resend.view", "channel_resend.configure"]
18
+ },
19
+ async seedDefaults(ctx) {
20
+ ensureResendAdapterRegistered();
21
+ registerResendSystemEmailConfigResolver();
22
+ await applyResendEnvPreset(ctx);
23
+ }
24
+ };
25
+ var setup_default = setup;
26
+ export {
27
+ setup_default as default,
28
+ setup
29
+ };
30
+ //# sourceMappingURL=setup.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/modules/channel_resend/setup.ts"],
4
+ "sourcesContent": ["import type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'\nimport {\n hasChannelAdapter,\n registerChannelAdapter,\n} from '@open-mercato/core/modules/communication_channels/lib/adapter-registry-singleton'\nimport { getResendChannelAdapter } from './lib/adapter'\nimport { applyResendEnvPreset } from './lib/preset'\nimport { registerResendSystemEmailConfigResolver } from './lib/system-email-config'\n\nfunction ensureResendAdapterRegistered(): void {\n if (hasChannelAdapter('resend')) return\n registerChannelAdapter(getResendChannelAdapter())\n}\n\nensureResendAdapterRegistered()\nregisterResendSystemEmailConfigResolver()\n\nexport const setup: ModuleSetupConfig = {\n defaultRoleFeatures: {\n superadmin: ['channel_resend.view', 'channel_resend.configure'],\n admin: ['channel_resend.view', 'channel_resend.configure'],\n },\n async seedDefaults(ctx) {\n ensureResendAdapterRegistered()\n registerResendSystemEmailConfigResolver()\n await applyResendEnvPreset(ctx)\n },\n}\n\nexport default setup\n"],
5
+ "mappings": "AACA;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,+BAA+B;AACxC,SAAS,4BAA4B;AACrC,SAAS,+CAA+C;AAExD,SAAS,gCAAsC;AAC7C,MAAI,kBAAkB,QAAQ,EAAG;AACjC,yBAAuB,wBAAwB,CAAC;AAClD;AAEA,8BAA8B;AAC9B,wCAAwC;AAEjC,MAAM,QAA2B;AAAA,EACtC,qBAAqB;AAAA,IACnB,YAAY,CAAC,uBAAuB,0BAA0B;AAAA,IAC9D,OAAO,CAAC,uBAAuB,0BAA0B;AAAA,EAC3D;AAAA,EACA,MAAM,aAAa,KAAK;AACtB,kCAA8B;AAC9B,4CAAwC;AACxC,UAAM,qBAAqB,GAAG;AAAA,EAChC;AACF;AAEA,IAAO,gBAAQ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,33 @@
1
+ /** @type {import('jest').Config} */
2
+ const base = require('../../jest.config.base.cjs')
3
+
4
+ module.exports = {
5
+ ...base,
6
+ testEnvironment: 'node',
7
+ watchman: false,
8
+ rootDir: '.',
9
+ moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'],
10
+ moduleNameMapper: {
11
+ '^@open-mercato/channel-resend/(.*)$': '<rootDir>/src/$1',
12
+ '^@open-mercato/core/(.*)$': '<rootDir>/../core/src/$1',
13
+ '^@open-mercato/shared/(.*)$': '<rootDir>/../shared/src/$1',
14
+ '^@open-mercato/ui/(.*)$': '<rootDir>/../ui/src/$1',
15
+ },
16
+ transform: {
17
+ '^.+\\.(t|j)sx?$': [
18
+ '<rootDir>/../../scripts/jest-mikroorm-transformer.cjs',
19
+ {
20
+ tsconfig: {
21
+ jsx: 'react-jsx',
22
+ rootDir: '.',
23
+ ignoreDeprecations: '6.0',
24
+ },
25
+ },
26
+ ],
27
+ },
28
+ transformIgnorePatterns: [
29
+ 'node_modules/(?!(@mikro-orm|kysely)/)',
30
+ ],
31
+ testMatch: ['<rootDir>/src/**/__tests__/**/*.test.(ts|tsx)'],
32
+ passWithNoTests: true,
33
+ }