@headless-lms/adapter-email-templates 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 mdwt
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,8 @@
1
+ import { EmailContent, EmailTemplateId, EmailTemplateParams, TemplateContext, TemplateRenderer } from "@headless-lms/core/types";
2
+ //#region src/index.d.ts
3
+ declare class ReactEmailTemplateRenderer implements TemplateRenderer {
4
+ render<K extends EmailTemplateId>(id: K, ctx: TemplateContext, params: EmailTemplateParams[K]): Promise<EmailContent>;
5
+ }
6
+ //#endregion
7
+ export { ReactEmailTemplateRenderer };
8
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js ADDED
@@ -0,0 +1,286 @@
1
+ import { render } from "@react-email/render";
2
+ import { Body, Button, Container, Head, Heading, Hr, Html, Img, Link, Section, Tailwind, Text } from "@react-email/components";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ //#region src/emails/layout.tsx
5
+ function Layout({ ctx, heading, children }) {
6
+ return /* @__PURE__ */ jsx(Html, { children: /* @__PURE__ */ jsxs(Tailwind, { children: [/* @__PURE__ */ jsx(Head, {}), /* @__PURE__ */ jsx(Body, {
7
+ className: "bg-white font-sans",
8
+ children: /* @__PURE__ */ jsxs(Container, {
9
+ className: "mx-auto max-w-[560px] px-6 pt-5 pb-12",
10
+ children: [
11
+ ctx.logoUrl ? /* @__PURE__ */ jsx(Img, {
12
+ src: ctx.logoUrl,
13
+ alt: ctx.brandName,
14
+ height: 42,
15
+ className: "h-[42px]"
16
+ }) : /* @__PURE__ */ jsx(Text, {
17
+ className: "m-0 text-[15px] font-semibold text-[#3c4149]",
18
+ children: ctx.brandName
19
+ }),
20
+ /* @__PURE__ */ jsx(Heading, {
21
+ className: "mt-[28px] mb-0 text-[24px] font-normal leading-[1.3] tracking-[-0.5px] text-[#484848]",
22
+ children: heading
23
+ }),
24
+ /* @__PURE__ */ jsx(Section, { children }),
25
+ /* @__PURE__ */ jsx(Hr, { className: "mt-[42px] mb-[26px] border-[#dfe1e4]" }),
26
+ /* @__PURE__ */ jsx(Link, {
27
+ href: ctx.baseUrl,
28
+ className: "text-[14px] text-[#b4becc]",
29
+ children: ctx.brandName
30
+ })
31
+ ]
32
+ })
33
+ })] }) });
34
+ }
35
+ function EmailButton({ href, children }) {
36
+ return /* @__PURE__ */ jsx(Section, {
37
+ className: "py-[20px]",
38
+ children: /* @__PURE__ */ jsx(Button, {
39
+ href,
40
+ className: "rounded-[3px] bg-[#18181b] px-[23px] py-[11px] text-[15px] font-semibold text-white no-underline",
41
+ children
42
+ })
43
+ });
44
+ }
45
+ function Paragraph({ children }) {
46
+ return /* @__PURE__ */ jsx(Text, {
47
+ className: "my-[15px] text-[15px] leading-[1.4] text-[#3c4149]",
48
+ children
49
+ });
50
+ }
51
+ const PREVIEW_CTX = {
52
+ brandName: "Acme LMS",
53
+ baseUrl: "http://localhost:8001",
54
+ studentPortalUrl: "http://localhost:8002"
55
+ };
56
+ //#endregion
57
+ //#region src/emails/magic-link.tsx
58
+ const subject$7 = (ctx, _params) => `Sign in to ${ctx.brandName}`;
59
+ function MagicLink({ ctx, params }) {
60
+ return /* @__PURE__ */ jsxs(Layout, {
61
+ ctx,
62
+ heading: "Sign in",
63
+ children: [
64
+ /* @__PURE__ */ jsx(Paragraph, { children: "Click the button below to sign in. This link is valid once and expires shortly." }),
65
+ /* @__PURE__ */ jsxs(EmailButton, {
66
+ href: params.url,
67
+ children: ["Sign in to ", ctx.brandName]
68
+ }),
69
+ /* @__PURE__ */ jsx(Paragraph, { children: "If you did not request this, you can ignore this email." })
70
+ ]
71
+ });
72
+ }
73
+ MagicLink.PreviewProps = {
74
+ ctx: PREVIEW_CTX,
75
+ params: { url: "http://localhost:8002/magic?token=demo" }
76
+ };
77
+ //#endregion
78
+ //#region src/emails/student-invite.tsx
79
+ const subject$6 = (ctx, _params) => `You're invited to ${ctx.brandName}`;
80
+ function StudentInvite({ ctx, params }) {
81
+ return /* @__PURE__ */ jsxs(Layout, {
82
+ ctx,
83
+ heading: "Welcome",
84
+ children: [
85
+ /* @__PURE__ */ jsxs(Paragraph, { children: [
86
+ params.studentName,
87
+ ", you've been invited to ",
88
+ ctx.brandName,
89
+ ". Create your account to get started."
90
+ ] }),
91
+ /* @__PURE__ */ jsx(EmailButton, {
92
+ href: params.inviteUrl,
93
+ children: "Create your account"
94
+ }),
95
+ /* @__PURE__ */ jsx(Paragraph, { children: "This invitation link is personal — please don't forward it." })
96
+ ]
97
+ });
98
+ }
99
+ StudentInvite.PreviewProps = {
100
+ ctx: PREVIEW_CTX,
101
+ params: {
102
+ inviteUrl: "http://localhost:8002/welcome?token=demo",
103
+ studentName: "Sam"
104
+ }
105
+ };
106
+ //#endregion
107
+ //#region src/emails/member-invite.tsx
108
+ const subject$5 = (ctx, _params) => `You've been invited to join ${ctx.brandName}`;
109
+ function MemberInvite({ ctx, params }) {
110
+ return /* @__PURE__ */ jsxs(Layout, {
111
+ ctx,
112
+ heading: `Join ${ctx.brandName}`,
113
+ children: [
114
+ /* @__PURE__ */ jsxs(Paragraph, { children: [
115
+ params.inviterName,
116
+ " invited you to join ",
117
+ ctx.brandName,
118
+ " as ",
119
+ params.role,
120
+ "."
121
+ ] }),
122
+ /* @__PURE__ */ jsx(EmailButton, {
123
+ href: params.inviteUrl,
124
+ children: "Accept invitation"
125
+ }),
126
+ /* @__PURE__ */ jsx(Paragraph, { children: "If you weren't expecting this invitation, you can ignore this email." })
127
+ ]
128
+ });
129
+ }
130
+ MemberInvite.PreviewProps = {
131
+ ctx: PREVIEW_CTX,
132
+ params: {
133
+ inviteUrl: "http://localhost:8001/invite?token=demo",
134
+ inviterName: "Ann",
135
+ role: "admin"
136
+ }
137
+ };
138
+ //#endregion
139
+ //#region src/emails/password-reset.tsx
140
+ const subject$4 = (ctx, _params) => `Reset your ${ctx.brandName} password`;
141
+ function PasswordReset({ ctx, params }) {
142
+ return /* @__PURE__ */ jsxs(Layout, {
143
+ ctx,
144
+ heading: "Reset your password",
145
+ children: [
146
+ /* @__PURE__ */ jsx(Paragraph, { children: "Click the button below to choose a new password. The link expires shortly." }),
147
+ /* @__PURE__ */ jsx(EmailButton, {
148
+ href: params.resetUrl,
149
+ children: "Reset password"
150
+ }),
151
+ /* @__PURE__ */ jsx(Paragraph, { children: "If you did not request a reset, your password is unchanged and you can ignore this email." })
152
+ ]
153
+ });
154
+ }
155
+ PasswordReset.PreviewProps = {
156
+ ctx: PREVIEW_CTX,
157
+ params: { resetUrl: "http://localhost:8002/reset?token=demo" }
158
+ };
159
+ //#endregion
160
+ //#region src/emails/email-verification.tsx
161
+ const subject$3 = (ctx, _params) => `Verify your email for ${ctx.brandName}`;
162
+ function EmailVerification({ ctx, params }) {
163
+ return /* @__PURE__ */ jsxs(Layout, {
164
+ ctx,
165
+ heading: "Verify your email",
166
+ children: [/* @__PURE__ */ jsx(Paragraph, { children: "Confirm this is your email address to finish setting up your account." }), /* @__PURE__ */ jsx(EmailButton, {
167
+ href: params.verifyUrl,
168
+ children: "Verify email"
169
+ })]
170
+ });
171
+ }
172
+ EmailVerification.PreviewProps = {
173
+ ctx: PREVIEW_CTX,
174
+ params: { verifyUrl: "http://localhost:8002/verify?token=demo" }
175
+ };
176
+ //#endregion
177
+ //#region src/emails/access-granted.tsx
178
+ const subject$2 = (_ctx, params) => `You now have access to ${params.contentTitle}`;
179
+ function AccessGranted({ ctx, params }) {
180
+ return /* @__PURE__ */ jsxs(Layout, {
181
+ ctx,
182
+ heading: `${params.contentTitle} is ready for you`,
183
+ children: [/* @__PURE__ */ jsx(Paragraph, { children: "You've been granted access. Jump in whenever you're ready." }), /* @__PURE__ */ jsx(EmailButton, {
184
+ href: `${ctx.studentPortalUrl}/courses/${params.contentId}`,
185
+ children: "Start learning"
186
+ })]
187
+ });
188
+ }
189
+ AccessGranted.PreviewProps = {
190
+ ctx: PREVIEW_CTX,
191
+ params: {
192
+ contentTitle: "Fly Tying 101",
193
+ contentId: "demo"
194
+ }
195
+ };
196
+ //#endregion
197
+ //#region src/emails/access-revoked.tsx
198
+ const subject$1 = (_ctx, params) => `Your access to ${params.contentTitle} has ended`;
199
+ function AccessRevoked({ ctx, params }) {
200
+ return /* @__PURE__ */ jsx(Layout, {
201
+ ctx,
202
+ heading: "Access ended",
203
+ children: /* @__PURE__ */ jsxs(Paragraph, { children: [
204
+ "Your access to ",
205
+ params.contentTitle,
206
+ " has ended. If you think this is a mistake, reply to this email."
207
+ ] })
208
+ });
209
+ }
210
+ AccessRevoked.PreviewProps = {
211
+ ctx: PREVIEW_CTX,
212
+ params: { contentTitle: "Fly Tying 101" }
213
+ };
214
+ //#endregion
215
+ //#region src/emails/course-completed.tsx
216
+ const subject = (_ctx, params) => `You completed ${params.courseTitle} 🎉`;
217
+ function CourseCompleted({ ctx, params }) {
218
+ return /* @__PURE__ */ jsx(Layout, {
219
+ ctx,
220
+ heading: "Congratulations!",
221
+ children: /* @__PURE__ */ jsxs(Paragraph, { children: [
222
+ "You've completed ",
223
+ params.courseTitle,
224
+ ". Nice work."
225
+ ] })
226
+ });
227
+ }
228
+ CourseCompleted.PreviewProps = {
229
+ ctx: PREVIEW_CTX,
230
+ params: { courseTitle: "Fly Tying 101" }
231
+ };
232
+ //#endregion
233
+ //#region src/index.tsx
234
+ const registry = {
235
+ magicLink: {
236
+ subject: subject$7,
237
+ Component: MagicLink
238
+ },
239
+ studentInvite: {
240
+ subject: subject$6,
241
+ Component: StudentInvite
242
+ },
243
+ memberInvite: {
244
+ subject: subject$5,
245
+ Component: MemberInvite
246
+ },
247
+ passwordReset: {
248
+ subject: subject$4,
249
+ Component: PasswordReset
250
+ },
251
+ emailVerification: {
252
+ subject: subject$3,
253
+ Component: EmailVerification
254
+ },
255
+ accessGranted: {
256
+ subject: subject$2,
257
+ Component: AccessGranted
258
+ },
259
+ accessRevoked: {
260
+ subject: subject$1,
261
+ Component: AccessRevoked
262
+ },
263
+ courseCompleted: {
264
+ subject,
265
+ Component: CourseCompleted
266
+ }
267
+ };
268
+ var ReactEmailTemplateRenderer = class {
269
+ async render(id, ctx, params) {
270
+ const entry = registry[id];
271
+ const element = /* @__PURE__ */ jsx(entry.Component, {
272
+ ctx,
273
+ params
274
+ });
275
+ const [html, text] = await Promise.all([render(element), render(element, { plainText: true })]);
276
+ return {
277
+ subject: entry.subject(ctx, params),
278
+ html,
279
+ text
280
+ };
281
+ }
282
+ };
283
+ //#endregion
284
+ export { ReactEmailTemplateRenderer };
285
+
286
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["subject","subject","subject","subject","subject","subject","subject","magicLink","studentInvite","memberInvite","passwordReset","emailVerification","accessGranted","accessRevoked","courseCompleted"],"sources":["../src/emails/layout.tsx","../src/emails/magic-link.tsx","../src/emails/student-invite.tsx","../src/emails/member-invite.tsx","../src/emails/password-reset.tsx","../src/emails/email-verification.tsx","../src/emails/access-granted.tsx","../src/emails/access-revoked.tsx","../src/emails/course-completed.tsx","../src/index.tsx"],"sourcesContent":["import { Body, Button, Container, Head, Heading, Hr, Html, Img, Link, Section, Tailwind, Text } from '@react-email/components';\nimport type { ReactNode } from 'react';\nimport type { TemplateContext } from '@headless-lms/core/types';\n\nexport function Layout({ ctx, heading, children }: {\n ctx: TemplateContext;\n heading: string;\n children: ReactNode;\n}) {\n return (\n <Html>\n <Tailwind>\n <Head />\n <Body className=\"bg-white font-sans\">\n <Container className=\"mx-auto max-w-[560px] px-6 pt-5 pb-12\">\n {ctx.logoUrl ? (\n <Img src={ctx.logoUrl} alt={ctx.brandName} height={42} className=\"h-[42px]\" />\n ) : (\n <Text className=\"m-0 text-[15px] font-semibold text-[#3c4149]\">{ctx.brandName}</Text>\n )}\n <Heading className=\"mt-[28px] mb-0 text-[24px] font-normal leading-[1.3] tracking-[-0.5px] text-[#484848]\">\n {heading}\n </Heading>\n <Section>{children}</Section>\n <Hr className=\"mt-[42px] mb-[26px] border-[#dfe1e4]\" />\n <Link href={ctx.baseUrl} className=\"text-[14px] text-[#b4becc]\">\n {ctx.brandName}\n </Link>\n </Container>\n </Body>\n </Tailwind>\n </Html>\n );\n}\n\nexport function EmailButton({ href, children }: { href: string; children: ReactNode }) {\n return (\n <Section className=\"py-[20px]\">\n <Button\n href={href}\n className=\"rounded-[3px] bg-[#18181b] px-[23px] py-[11px] text-[15px] font-semibold text-white no-underline\"\n >\n {children}\n </Button>\n </Section>\n );\n}\n\nexport function Paragraph({ children }: { children: ReactNode }) {\n return <Text className=\"my-[15px] text-[15px] leading-[1.4] text-[#3c4149]\">{children}</Text>;\n}\n\nexport const PREVIEW_CTX: TemplateContext = {\n brandName: 'Acme LMS',\n baseUrl: 'http://localhost:8001',\n studentPortalUrl: 'http://localhost:8002',\n};\n","import type { EmailTemplateParams, TemplateContext } from '@headless-lms/core/types';\nimport { EmailButton, Layout, Paragraph, PREVIEW_CTX } from './layout.js';\n\ntype Params = EmailTemplateParams['magicLink'];\n\nexport const subject = (ctx: TemplateContext, _params: Params) => `Sign in to ${ctx.brandName}`;\n\nexport default function MagicLink({ ctx, params }: { ctx: TemplateContext; params: Params }) {\n return (\n <Layout ctx={ctx} heading=\"Sign in\">\n <Paragraph>Click the button below to sign in. This link is valid once and expires shortly.</Paragraph>\n <EmailButton href={params.url}>Sign in to {ctx.brandName}</EmailButton>\n <Paragraph>If you did not request this, you can ignore this email.</Paragraph>\n </Layout>\n );\n}\n\nMagicLink.PreviewProps = { ctx: PREVIEW_CTX, params: { url: 'http://localhost:8002/magic?token=demo' } };\n","import type { EmailTemplateParams, TemplateContext } from '@headless-lms/core/types';\nimport { EmailButton, Layout, Paragraph, PREVIEW_CTX } from './layout.js';\n\ntype Params = EmailTemplateParams['studentInvite'];\n\nexport const subject = (ctx: TemplateContext, _params: Params) => `You're invited to ${ctx.brandName}`;\n\nexport default function StudentInvite({ ctx, params }: { ctx: TemplateContext; params: Params }) {\n return (\n <Layout ctx={ctx} heading=\"Welcome\">\n <Paragraph>\n {params.studentName}, you've been invited to {ctx.brandName}. Create your account to get started.\n </Paragraph>\n <EmailButton href={params.inviteUrl}>Create your account</EmailButton>\n <Paragraph>This invitation link is personal — please don't forward it.</Paragraph>\n </Layout>\n );\n}\n\nStudentInvite.PreviewProps = {\n ctx: PREVIEW_CTX,\n params: {\n inviteUrl: 'http://localhost:8002/welcome?token=demo',\n studentName: 'Sam',\n },\n};\n","import type { EmailTemplateParams, TemplateContext } from '@headless-lms/core/types';\nimport { EmailButton, Layout, Paragraph, PREVIEW_CTX } from './layout.js';\n\ntype Params = EmailTemplateParams['memberInvite'];\n\nexport const subject = (ctx: TemplateContext, _params: Params) =>\n `You've been invited to join ${ctx.brandName}`;\n\nexport default function MemberInvite({ ctx, params }: { ctx: TemplateContext; params: Params }) {\n return (\n <Layout ctx={ctx} heading={`Join ${ctx.brandName}`}>\n <Paragraph>\n {params.inviterName} invited you to join {ctx.brandName} as {params.role}.\n </Paragraph>\n <EmailButton href={params.inviteUrl}>Accept invitation</EmailButton>\n <Paragraph>If you weren't expecting this invitation, you can ignore this email.</Paragraph>\n </Layout>\n );\n}\n\nMemberInvite.PreviewProps = {\n ctx: PREVIEW_CTX,\n params: {\n inviteUrl: 'http://localhost:8001/invite?token=demo',\n inviterName: 'Ann',\n role: 'admin',\n },\n};\n","import type { EmailTemplateParams, TemplateContext } from '@headless-lms/core/types';\nimport { EmailButton, Layout, Paragraph, PREVIEW_CTX } from './layout.js';\n\ntype Params = EmailTemplateParams['passwordReset'];\n\nexport const subject = (ctx: TemplateContext, _params: Params) => `Reset your ${ctx.brandName} password`;\n\nexport default function PasswordReset({ ctx, params }: { ctx: TemplateContext; params: Params }) {\n return (\n <Layout ctx={ctx} heading=\"Reset your password\">\n <Paragraph>Click the button below to choose a new password. The link expires shortly.</Paragraph>\n <EmailButton href={params.resetUrl}>Reset password</EmailButton>\n <Paragraph>If you did not request a reset, your password is unchanged and you can ignore this email.</Paragraph>\n </Layout>\n );\n}\n\nPasswordReset.PreviewProps = { ctx: PREVIEW_CTX, params: { resetUrl: 'http://localhost:8002/reset?token=demo' } };\n","import type { EmailTemplateParams, TemplateContext } from '@headless-lms/core/types';\nimport { EmailButton, Layout, Paragraph, PREVIEW_CTX } from './layout.js';\n\ntype Params = EmailTemplateParams['emailVerification'];\n\nexport const subject = (ctx: TemplateContext, _params: Params) => `Verify your email for ${ctx.brandName}`;\n\nexport default function EmailVerification({ ctx, params }: { ctx: TemplateContext; params: Params }) {\n return (\n <Layout ctx={ctx} heading=\"Verify your email\">\n <Paragraph>Confirm this is your email address to finish setting up your account.</Paragraph>\n <EmailButton href={params.verifyUrl}>Verify email</EmailButton>\n </Layout>\n );\n}\n\nEmailVerification.PreviewProps = { ctx: PREVIEW_CTX, params: { verifyUrl: 'http://localhost:8002/verify?token=demo' } };\n","import type { EmailTemplateParams, TemplateContext } from '@headless-lms/core/types';\nimport { EmailButton, Layout, Paragraph, PREVIEW_CTX } from './layout.js';\n\ntype Params = EmailTemplateParams['accessGranted'];\n\nexport const subject = (_ctx: TemplateContext, params: Params) =>\n `You now have access to ${params.contentTitle}`;\n\nexport default function AccessGranted({ ctx, params }: { ctx: TemplateContext; params: Params }) {\n return (\n <Layout ctx={ctx} heading={`${params.contentTitle} is ready for you`}>\n <Paragraph>You've been granted access. Jump in whenever you're ready.</Paragraph>\n <EmailButton href={`${ctx.studentPortalUrl}/courses/${params.contentId}`}>Start learning</EmailButton>\n </Layout>\n );\n}\n\nAccessGranted.PreviewProps = {\n ctx: PREVIEW_CTX,\n params: {\n contentTitle: 'Fly Tying 101',\n contentId: 'demo',\n },\n};\n","import type { EmailTemplateParams, TemplateContext } from '@headless-lms/core/types';\nimport { Layout, Paragraph, PREVIEW_CTX } from './layout.js';\n\ntype Params = EmailTemplateParams['accessRevoked'];\n\nexport const subject = (_ctx: TemplateContext, params: Params) =>\n `Your access to ${params.contentTitle} has ended`;\n\nexport default function AccessRevoked({ ctx, params }: { ctx: TemplateContext; params: Params }) {\n return (\n <Layout ctx={ctx} heading=\"Access ended\">\n <Paragraph>\n Your access to {params.contentTitle} has ended. If you think this is a mistake, reply to this email.\n </Paragraph>\n </Layout>\n );\n}\n\nAccessRevoked.PreviewProps = {\n ctx: PREVIEW_CTX,\n params: {\n contentTitle: 'Fly Tying 101',\n },\n};\n","import type { EmailTemplateParams, TemplateContext } from '@headless-lms/core/types';\nimport { Layout, Paragraph, PREVIEW_CTX } from './layout.js';\n\ntype Params = EmailTemplateParams['courseCompleted'];\n\nexport const subject = (_ctx: TemplateContext, params: Params) => `You completed ${params.courseTitle} 🎉`;\n\nexport default function CourseCompleted({ ctx, params }: { ctx: TemplateContext; params: Params }) {\n return (\n <Layout ctx={ctx} heading=\"Congratulations!\">\n <Paragraph>You've completed {params.courseTitle}. Nice work.</Paragraph>\n </Layout>\n );\n}\n\nCourseCompleted.PreviewProps = {\n ctx: PREVIEW_CTX,\n params: {\n courseTitle: 'Fly Tying 101',\n },\n};\n","// Default TemplateRenderer: react-email components rendered at call time.\nimport { render } from '@react-email/render';\nimport type { JSX } from 'react';\nimport type {\n EmailContent,\n EmailTemplateId,\n EmailTemplateParams,\n TemplateContext,\n TemplateRenderer,\n} from '@headless-lms/core/types';\nimport MagicLink, { subject as magicLink } from './emails/magic-link.js';\nimport StudentInvite, { subject as studentInvite } from './emails/student-invite.js';\nimport MemberInvite, { subject as memberInvite } from './emails/member-invite.js';\nimport PasswordReset, { subject as passwordReset } from './emails/password-reset.js';\nimport EmailVerification, { subject as emailVerification } from './emails/email-verification.js';\nimport AccessGranted, { subject as accessGranted } from './emails/access-granted.js';\nimport AccessRevoked, { subject as accessRevoked } from './emails/access-revoked.js';\nimport CourseCompleted, { subject as courseCompleted } from './emails/course-completed.js';\n\ninterface Entry<K extends EmailTemplateId> {\n subject: (ctx: TemplateContext, params: EmailTemplateParams[K]) => string;\n Component: (props: { ctx: TemplateContext; params: EmailTemplateParams[K] }) => JSX.Element;\n}\n\n// The catalog is closed: a missing key here is a compile error.\nconst registry: { [K in EmailTemplateId]: Entry<K> } = {\n magicLink: { subject: magicLink, Component: MagicLink },\n studentInvite: { subject: studentInvite, Component: StudentInvite },\n memberInvite: { subject: memberInvite, Component: MemberInvite },\n passwordReset: { subject: passwordReset, Component: PasswordReset },\n emailVerification: { subject: emailVerification, Component: EmailVerification },\n accessGranted: { subject: accessGranted, Component: AccessGranted },\n accessRevoked: { subject: accessRevoked, Component: AccessRevoked },\n courseCompleted: { subject: courseCompleted, Component: CourseCompleted },\n};\n\nexport class ReactEmailTemplateRenderer implements TemplateRenderer {\n async render<K extends EmailTemplateId>(\n id: K,\n ctx: TemplateContext,\n params: EmailTemplateParams[K],\n ): Promise<EmailContent> {\n const entry = registry[id] as Entry<K>;\n const element = <entry.Component ctx={ctx} params={params} />;\n const [html, text] = await Promise.all([render(element), render(element, { plainText: true })]);\n return { subject: entry.subject(ctx, params), html, text };\n }\n}\n"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@headless-lms/adapter-email-templates",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "default": "./dist/index.js"
11
+ }
12
+ },
13
+ "dependencies": {
14
+ "@react-email/components": "^1.0.12",
15
+ "@react-email/render": "^2.1.0",
16
+ "react": "19.2.4",
17
+ "react-dom": "19.2.4",
18
+ "@headless-lms/core": "0.1.0"
19
+ },
20
+ "devDependencies": {
21
+ "@react-email/ui": "6.9.0",
22
+ "@types/node": "^22.20.0",
23
+ "@types/react": "^19.2.17",
24
+ "react-email": "^6.9.0",
25
+ "tsdown": "^0.22.9",
26
+ "typescript": "^5.9.3",
27
+ "vitest": "^2.1.9"
28
+ },
29
+ "description": "React Email templates for the headless LMS",
30
+ "license": "MIT",
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "git+https://github.com/mdwt/headless-lms.git",
34
+ "directory": "adapters/email-templates"
35
+ },
36
+ "homepage": "https://github.com/mdwt/headless-lms#readme",
37
+ "bugs": {
38
+ "url": "https://github.com/mdwt/headless-lms/issues"
39
+ },
40
+ "files": [
41
+ "dist"
42
+ ],
43
+ "publishConfig": {
44
+ "access": "public"
45
+ },
46
+ "scripts": {
47
+ "build": "tsdown",
48
+ "typecheck": "tsc --noEmit",
49
+ "test": "vitest run",
50
+ "dev": "email dev --dir src/emails"
51
+ }
52
+ }