first commit
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
# `template-engine` — Template registry and renderer
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
`template-engine.ts` ships the `TemplateEngine` registry that turns named templates into channel-specific rendered output. Templates carry a `defaultTemplate` (used when no channel variant matches) plus optional `channelTemplates` keyed by `NotificationChannel`, with `{{var}}` placeholders substituted from a flat `Record<string,string>`. Missing keys are left as-is so QA can spot them.
|
||||
|
||||
## Features
|
||||
|
||||
- Register / unregister / list templates by string id.
|
||||
- Per-channel variants (`channelTemplates.email`, `.sms`, etc.) with fallback to `defaultTemplate`.
|
||||
- `{{var}}` placeholder substitution against a flat string-valued record.
|
||||
- Throws `TemplateNotFoundError` for unknown ids — no silent empty renders.
|
||||
- Universal — no DOM/Node-specific APIs.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { TemplateEngine } from "@suaveplan/notification-channel";
|
||||
|
||||
const engine = new TemplateEngine();
|
||||
engine.register({
|
||||
id: "welcome",
|
||||
name: "Welcome",
|
||||
defaultTemplate: {
|
||||
subject: "Welcome {{name}}",
|
||||
bodyTemplate: "Hello {{name}}, welcome to {{app}}!",
|
||||
},
|
||||
});
|
||||
|
||||
const rendered = engine.render("welcome", { name: "Alice", app: "Suaveplan" });
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
engine.register({
|
||||
id: "order-status",
|
||||
name: "Order status",
|
||||
defaultTemplate: {
|
||||
subject: "Order {{orderId}} — {{status}}",
|
||||
bodyTemplate: "Order #{{orderId}}: {{status}}",
|
||||
},
|
||||
channelTemplates: {
|
||||
sms: { bodyTemplate: "Order {{orderId}}: {{status}}" },
|
||||
email: {
|
||||
subject: "Order {{orderId}} update",
|
||||
bodyTemplate: "<h1>Order {{orderId}}</h1><p>{{status}}</p>",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const sms = engine.render(
|
||||
"order-status",
|
||||
{ orderId: "12345", status: "shipped" },
|
||||
"sms"
|
||||
);
|
||||
const email = engine.render(
|
||||
"order-status",
|
||||
{ orderId: "12345", status: "shipped" },
|
||||
"email"
|
||||
);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `TemplateEngine` class.
|
||||
- `register(t: TemplateRegistration): void`
|
||||
- `unregister(id: string): boolean`
|
||||
- `has(id: string): boolean`
|
||||
- `listIds(): string[]`
|
||||
- `render(id, data?, channel?): RenderedTemplate`
|
||||
- `TemplateRegistration`, `ChannelTemplateBody`, `ChannelTemplate`, `RenderedTemplate`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Substitution uses `String.prototype.split(...).join(...)` rather than `replace(/.../g, ...)` so dollar-escape sequences in the value (`$&`, `$1`) flow through verbatim. Subjects flow through the same substitution pass when set. Unknown placeholders are intentionally preserved — silently dropping them masks integration mismatches that QA would otherwise catch. The engine is intentionally simple: no conditionals, no loops, no template inheritance. Consumers needing those features should plug a real template language (Handlebars, Liquid, MJML) into a custom `TemplateEngine` decorator or render upstream.
|
||||
Reference in New Issue
Block a user