chore: server-and-serverless template

This commit is contained in:
e2e
2026-06-28 18:56:43 +02:00
commit b58ae55df5
202 changed files with 9063 additions and 0 deletions

View File

@@ -0,0 +1,211 @@
/**
* This file was generated by @pikku/cli@0.12.48
*/
type EmailPrimitive = string | number | boolean | null | undefined
type EmailTemplateValue = EmailPrimitive | Record<string, unknown> | Array<unknown>
const EMAIL_THEME = {
"appName": "Pikku Starter",
"fonts": {
"body": "Inter, Arial, sans-serif"
},
"colors": {
"canvas": "#0b1020",
"surface": "#11182d",
"border": "#263252",
"text": "#f7f8fb",
"muted": "#a8b0c5",
"accent": "#7dd3fc",
"button": "#f59e0b",
"buttonText": "#111827"
}
} as const
const EMAIL_LOCALES = {
"en": {
"common": {
"footer": "If you did not create this account, you can safely ignore this email."
},
"confirmEmail": {
"subject": "Confirm your email for {{appName}}",
"preview": "Confirm your email address to finish setting up your account.",
"heading": "Confirm your email",
"intro": "Thanks for signing up for {{appName}}! Confirm {{email}} to finish setting up your account.",
"cta": "Confirm email",
"fallback": "If the button does not work, copy and paste this URL into your browser:",
"expiry": "This link expires in 24 hours."
}
}
} as const
const EMAIL_PARTIALS = {
"footer": "<p style=\"margin:32px 0 0;color:{{theme.colors.muted}};font-size:13px;line-height:1.6;\">\n {{t.common.footer}}\n</p>\n",
"layout": "<!doctype html>\n<html lang=\"{{locale}}\">\n <head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>{{subject}}</title>\n </head>\n <body style=\"margin:0;padding:0;background:{{theme.colors.canvas}};font-family:{{theme.fonts.body}};\">\n <div style=\"max-width:560px;margin:0 auto;padding:32px 16px;\">\n {{content}}\n </div>\n </body>\n</html>\n"
} as const
const EMAIL_TEMPLATES = {
"confirm-email": {
"html": "<p style=\"margin:0 0 12px;color:{{theme.colors.accent}};font-size:12px;font-weight:700;letter-spacing:0.12em;text-transform:uppercase;\">\n {{appName}}\n</p>\n<h1 style=\"margin:0 0 16px;color:{{theme.colors.text}};font-size:28px;line-height:1.1;\">\n {{t.confirmEmail.heading}}\n</h1>\n<p style=\"margin:0 0 24px;color:{{theme.colors.muted}};font-size:16px;line-height:1.6;\">\n {{t.confirmEmail.intro}}\n</p>\n<p style=\"margin:0 0 24px;\">\n <a\n href=\"{{confirmUrl}}\"\n style=\"display:inline-block;background:{{theme.colors.button}};color:{{theme.colors.buttonText}};text-decoration:none;padding:14px 22px;border-radius:999px;font-weight:700;\"\n >\n {{t.confirmEmail.cta}}\n </a>\n</p>\n<p style=\"margin:0 0 8px;color:{{theme.colors.text}};font-size:14px;line-height:1.6;\">\n {{t.confirmEmail.fallback}}\n</p>\n<p style=\"margin:0;color:{{theme.colors.muted}};font-size:14px;line-height:1.6;word-break:break-all;\">\n {{confirmUrl}}\n</p>\n<p style=\"margin:24px 0 0;color:{{theme.colors.muted}};font-size:13px;line-height:1.6;\">\n {{t.confirmEmail.expiry}}\n</p>\n{{> footer}}\n",
"subject": "{{t.confirmEmail.subject}}\n",
"text": "{{t.confirmEmail.heading}}\n\n{{t.confirmEmail.intro}}\n\n{{confirmUrl}}\n\n{{t.confirmEmail.expiry}}\n",
"variables": [
"appName",
"confirmUrl",
"email"
],
"hashes": {
"en": {
"contentHash": "fa3ef0d0ff652703fc0586c5c78b4c4820206a09bd47651c56a9f1c26a5d2cfd",
"htmlHash": "c4b9403e096b1f6c439e120f701bf8cbc6bdba9fdb8e4f21fbad524961343a39",
"subjectHash": "209d4ac15c8f7e70e14de04c81724beb00dac7f4ea450a6e8ce527faf4a0ad75",
"textHash": "4b494839407f2817290b9b08103cd84103e53a6339ff4e4f24e71dd89ebb7d13"
}
}
}
} as const
export type EmailTemplateName = keyof typeof EMAIL_TEMPLATES
export type EmailLocale = keyof typeof EMAIL_LOCALES
type TemplateVariableMap = {
"confirm-email": {
"appName"?: EmailTemplateValue
"confirmUrl"?: EmailTemplateValue
"email"?: EmailTemplateValue
}
}
export type EmailTemplateVariables<TName extends EmailTemplateName> =
TemplateVariableMap[TName]
export type RenderEmailInput<TName extends EmailTemplateName> = {
name: TName
locale?: EmailLocale
data: EmailTemplateVariables<TName>
}
export type RenderedEmail<TName extends EmailTemplateName> = {
name: TName
locale: EmailLocale
subject: string
html: string
text?: string
variables: ReadonlyArray<string>
hash: (typeof EMAIL_TEMPLATES)[TName]['hashes'][EmailLocale]['contentHash']
}
function getNestedValue(source: Record<string, unknown>, path: string): string {
const segments = path.split('.')
let current: unknown = source
for (const segment of segments) {
if (!current || typeof current !== 'object' || !(segment in current)) {
return ''
}
current = (current as Record<string, unknown>)[segment]
}
return typeof current === 'string' || typeof current === 'number'
? String(current)
: ''
}
function applyTemplate(source: string, context: Record<string, unknown>): string {
return source.replace(/\{\{\s*([^}]+?)\s*\}\}/g, (_match, rawKey) => {
const key = String(rawKey).trim()
if (key === 'content') {
return typeof context.content === 'string' ? context.content : ''
}
if (key.startsWith('>')) {
return ''
}
return getNestedValue(context, key)
})
}
function renderTemplate(source: string, context: Record<string, unknown>): string {
let rendered = source
for (let i = 0; i < 5; i += 1) {
const next = applyTemplate(rendered, context)
if (next === rendered) break
rendered = next
}
return rendered
}
function renderPartial(name: string, context: Record<string, unknown>): string {
const partial = EMAIL_PARTIALS[name as keyof typeof EMAIL_PARTIALS]
return partial ? renderTemplate(partial, context) : ''
}
export const EMAILS = EMAIL_TEMPLATES
export function renderEmailTemplate<TName extends EmailTemplateName>(
input: RenderEmailInput<TName>
): RenderedEmail<TName> {
const locale = (input.locale ?? 'en') as EmailLocale
const template = EMAIL_TEMPLATES[input.name]
if (!template) {
throw new Error(`Unknown email template: ${String(input.name)}`)
}
const strings = EMAIL_LOCALES[locale]
if (!strings) {
throw new Error(`Unknown email locale: ${String(locale)}`)
}
const data = (input.data ?? {}) as Record<string, unknown>
const appName =
(typeof data.appName === 'string' && data.appName) ||
getNestedValue(EMAIL_THEME as Record<string, unknown>, 'appName')
const baseContext = {
...data,
locale,
theme: EMAIL_THEME,
t: strings,
appName,
}
const subject = renderTemplate(template.subject, baseContext).trim()
const htmlWithPartials = template.html.replace(
/\{\{\s*>\s*([a-zA-Z0-9-_/.]+)\s*\}\}/g,
(_match, partialName) => `@@PARTIAL:${String(partialName).trim()}@@`
)
let htmlBody = renderTemplate(htmlWithPartials, {
...baseContext,
subject,
})
const partialMatches = [...htmlBody.matchAll(/@@PARTIAL:([^@]+)@@/g)]
for (const match of partialMatches) {
const rendered = renderPartial(match[1], {
...baseContext,
subject,
})
htmlBody = htmlBody.replace(match[0], rendered)
}
const html = EMAIL_PARTIALS.layout
? renderTemplate(EMAIL_PARTIALS.layout, {
...baseContext,
subject,
content: htmlBody,
})
: htmlBody
const text = template.text
? renderTemplate(template.text, {
...baseContext,
subject,
}).trim()
: undefined
const hash = (template.hashes[locale]?.contentHash ??
'') as RenderedEmail<TName>['hash']
return {
name: input.name,
locale,
subject,
html,
...(text ? { text } : {}),
variables: template.variables,
hash,
}
}