Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,250 @@
# @suaveplan/templates - Plugin-based template engine with type-safe contexts, variable interpolation, transform and control-flow plugins, and preset bundles.
## Summary
A plugin-based string template engine for TypeScript. Resolves `{variable}` and `{variable:transform}` tokens in strings using a priority-sorted, regex-scan pipeline — no eval, no AST parser overhead. Designed for filling templates in notification messages, email subjects, file name patterns, code generation, and similar string-expansion tasks. Ships nine built-in plugins: dot-notation and array-index variable resolution; case, regex, and split transforms; counter, datetime, and UUID dynamic generators; and conditional/loop control flow. Three preset bundles (`minimal`, `standard`, `full`) let you trade features for bundle size. The injectable `Clock` and `Rng` make every rendering call deterministic in tests. Do not confuse with `@suaveplan/i18n`, which handles locale-keyed message catalogs and plural forms — use this package when your source strings are already in the target language and you only need token substitution.
<!-- begin:auto-docs -->
## Documentation tree
- [ParseState](./src/engine/ParseState.md) — Provides factory functions and mutation helpers for `ParseState` — the stateful object that the `TemplateEngine` passes…
- [TemplateEngine](./src/engine/TemplateEngine.md) — The `TemplateEngine` class is the central orchestrator of `@suaveplan/templates`. It manages a priority-sorted registry…
- [apply-plugin](./src/engine/apply-plugin.md) — Provides the core two-phase plugin application logic used by `TemplateEngine` for both synchronous and asynchronous ren…
- [error-codes](./src/errors/error-codes.md) — Defines and freezes the complete error-code registry for `@suaveplan/templates` using the `defineCodes` utility from `@…
- [template-error](./src/errors/template-error.md) — Defines the structured error hierarchy for `@suaveplan/templates`. All errors extend `SuaveplanError` from `@suaveplan/…
- [otel](./src/otel/templates-otel.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [templates-otel](./src/otel/templates-otel.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [ConditionalPlugin](./src/plugins/control/ConditionalPlugin.md) — Conditionally includes or excludes blocks of template content based on context values. The plugin evaluates a condition…
- [LoopPlugin](./src/plugins/control/LoopPlugin.md) — Iterates over an array in the context object and renders a sub-template once for each element, concatenating the result…
- [CounterPlugin](./src/plugins/dynamic/CounterPlugin.md) — Provides stateful auto-incrementing counters within a single template rendering pass. Counters are scoped to the `Parse…
- [DateTimePlugin](./src/plugins/dynamic/DateTimePlugin.md) — Injects current date and time values into templates using a flexible, token-based formatting syntax. Because date-time…
- [UuidPlugin](./src/plugins/dynamic/UuidPlugin.md) — Generates UUID v4 and URL-safe nano-ID strings inline within templates. Because ID generation depends on `crypto.random…
- [CaseTransformPlugin](./src/plugins/transform/CaseTransformPlugin.md) — Applies string case transformations to resolved context values. Supports eight case styles covering all common naming c…
- [RegexPlugin](./src/plugins/transform/RegexPlugin.md) — Applies inline regular-expression operations to resolved context values. The plugin enables match extraction, capture g…
- [StringSplitPlugin](./src/plugins/transform/StringSplitPlugin.md) — Splits a resolved string value on a configurable delimiter and returns a single element by index. This plugin solves th…
- [ArrayIndexPlugin](./src/plugins/variables/ArrayIndexPlugin.md) — Resolves bracket-notation array-index references in templates. Handles `{items[0]}`, `{items[-1]}`, and nested `{users[…
- [DotNotationPlugin](./src/plugins/variables/DotNotationPlugin.md) — Resolves simple variable references using dot-notation paths. This is the primary workhorse plugin for template renderi…
- [full](./src/presets/full.md) — Provides the complete plugin suite in two variants: `fullPreset()` matches `standardPreset()` exactly — it is the recom…
- [minimal](./src/presets/minimal.md) — Provides the lightest possible `PluginPreset` for the `TemplateEngine`. It bundles only two plugins — `array-index` and…
- [standard](./src/presets/standard.md) — Provides a comprehensive `PluginPreset` suitable for the majority of production use cases. It bundles all non-loop plug…
- [schemas](./src/schemas/templates.schemas.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [templates.schemas](./src/schemas/templates.schemas.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [types](./src/types/index.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [index](./src/types/index.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [escape](./src/utils/escape.md) — Provides three utility functions for handling escape sequences in template strings. These are low-level primitives used…
- [format](./src/utils/format.md) — Provides core formatting utilities used by the dynamic plugins: zero-padded number/string padding, date formatting with…
- [nested-value](./src/utils/nested-value.md) — Provides three functions for traversing, checking, and mutating plain object trees using dot-notation or bracket-notati…
<!-- end:auto-docs --> - [templates-otel](./src/otel/templates-otel.md) — <!-- begin:auto-docs --> _(no submodules)_- [ConditionalPlugin](./src/plugins/control/ConditionalPlugin.md) — Conditionally includes or excludes blocks of template content based on context values. The plugin evaluates a condition…
- [LoopPlugin](./src/plugins/control/LoopPlugin.md) — Iterates over an array in the context object and renders a sub-template once for each element, concatenating the result…
- [CounterPlugin](./src/plugins/dynamic/CounterPlugin.md) — Provides stateful auto-incrementing counters within a single template rendering pass. Counters are scoped to the `Parse…
- [DateTimePlugin](./src/plugins/dynamic/DateTimePlugin.md) — Injects current date and time values into templates using a flexible, token-based formatting syntax. Because date-time…
- [UuidPlugin](./src/plugins/dynamic/UuidPlugin.md) — Generates UUID v4 and URL-safe nano-ID strings inline within templates. Because ID generation depends on `crypto.random…
- [CaseTransformPlugin](./src/plugins/transform/CaseTransformPlugin.md) — Applies string case transformations to resolved context values. Supports eight case styles covering all common naming c…
- [RegexPlugin](./src/plugins/transform/RegexPlugin.md) — Applies inline regular-expression operations to resolved context values. The plugin enables match extraction, capture g…
- [StringSplitPlugin](./src/plugins/transform/StringSplitPlugin.md) — Splits a resolved string value on a configurable delimiter and returns a single element by index. This plugin solves th…
- [ArrayIndexPlugin](./src/plugins/variables/ArrayIndexPlugin.md) — Resolves bracket-notation array-index references in templates. Handles `{items[0]}`, `{items[-1]}`, and nested `{users[…
- [DotNotationPlugin](./src/plugins/variables/DotNotationPlugin.md) — Resolves simple variable references using dot-notation paths. This is the primary workhorse plugin for template renderi…
- [full](./src/presets/full.md) — Provides the complete plugin suite in two variants: `fullPreset()` matches `standardPreset()` exactly — it is the recom…
- [minimal](./src/presets/minimal.md) — Provides the lightest possible `PluginPreset` for the `TemplateEngine`. It bundles only two plugins — `array-index` and…
- [standard](./src/presets/standard.md) — Provides a comprehensive `PluginPreset` suitable for the majority of production use cases. It bundles all non-loop plug…
- [schemas](./src/schemas/templates.schemas.md) — <!-- begin:auto-docs --> _(no submodules)_- [templates.schemas](./src/schemas/templates.schemas.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/index.md) — <!-- begin:auto-docs --> _(no submodules)_- [index](./src/types/index.md) — <!-- begin:auto-docs --> _(no submodules)_- [escape](./src/utils/escape.md) — Provides three utility functions for handling escape sequences in template strings. These are low-level primitives used…
- [format](./src/utils/format.md) — Provides core formatting utilities used by the dynamic plugins: zero-padded number/string padding, date formatting with…
- [nested-value](./src/utils/nested-value.md) — Provides three functions for traversing, checking, and mutating plain object trees using dot-notation or bracket-notati…
- [templates-otel](./src/otel/templates-otel.md) — <!-- begin:auto-docs --> _(no submodules)_- [ConditionalPlugin](./src/plugins/control/ConditionalPlugin.md) — Conditionally includes or excludes blocks of template content based on context values. The plugin evaluates a condition…
- [LoopPlugin](./src/plugins/control/LoopPlugin.md) — Iterates over an array in the context object and renders a sub-template once for each element, concatenating the result…
- [CounterPlugin](./src/plugins/dynamic/CounterPlugin.md) — Provides stateful auto-incrementing counters within a single template rendering pass. Counters are scoped to the `Parse…
- [DateTimePlugin](./src/plugins/dynamic/DateTimePlugin.md) — Injects current date and time values into templates using a flexible, token-based formatting syntax. Because date-time…
- [UuidPlugin](./src/plugins/dynamic/UuidPlugin.md) — Generates UUID v4 and URL-safe nano-ID strings inline within templates. Because ID generation depends on `crypto.random…
- [CaseTransformPlugin](./src/plugins/transform/CaseTransformPlugin.md) — Applies string case transformations to resolved context values. Supports eight case styles covering all common naming c…
- [RegexPlugin](./src/plugins/transform/RegexPlugin.md) — Applies inline regular-expression operations to resolved context values. The plugin enables match extraction, capture g…
- [StringSplitPlugin](./src/plugins/transform/StringSplitPlugin.md) — Splits a resolved string value on a configurable delimiter and returns a single element by index. This plugin solves th…
- [ArrayIndexPlugin](./src/plugins/variables/ArrayIndexPlugin.md) — Resolves bracket-notation array-index references in templates. Handles `{items[0]}`, `{items[-1]}`, and nested `{users[…
- [DotNotationPlugin](./src/plugins/variables/DotNotationPlugin.md) — Resolves simple variable references using dot-notation paths. This is the primary workhorse plugin for template renderi…
- [full](./src/presets/full.md) — Provides the complete plugin suite in two variants: `fullPreset()` matches `standardPreset()` exactly — it is the recom…
- [minimal](./src/presets/minimal.md) — Provides the lightest possible `PluginPreset` for the `TemplateEngine`. It bundles only two plugins — `array-index` and…
- [standard](./src/presets/standard.md) — Provides a comprehensive `PluginPreset` suitable for the majority of production use cases. It bundles all non-loop plug…
- [schemas](./src/schemas/templates.schemas.md) — <!-- begin:auto-docs --> _(no submodules)_- [templates.schemas](./src/schemas/templates.schemas.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/index.md) — <!-- begin:auto-docs --> _(no submodules)_- [index](./src/types/index.md) — <!-- begin:auto-docs --> _(no submodules)_- [escape](./src/utils/escape.md) — Provides three utility functions for handling escape sequences in template strings. These are low-level primitives used…
- [format](./src/utils/format.md) — Provides core formatting utilities used by the dynamic plugins: zero-padded number/string padding, date formatting with…
- [nested-value](./src/utils/nested-value.md) — Provides three functions for traversing, checking, and mutating plain object trees using dot-notation or bracket-notati…
- [templates-otel](./src/otel/templates-otel.md) — <!-- begin:auto-docs --> _(no submodules)_- [ConditionalPlugin](./src/plugins/control/ConditionalPlugin.md) — Conditionally includes or excludes blocks of template content based on context values. The plugin evaluates a condition…
- [LoopPlugin](./src/plugins/control/LoopPlugin.md) — Iterates over an array in the context object and renders a sub-template once for each element, concatenating the result…
- [CounterPlugin](./src/plugins/dynamic/CounterPlugin.md) — Provides stateful auto-incrementing counters within a single template rendering pass. Counters are scoped to the `Parse…
- [DateTimePlugin](./src/plugins/dynamic/DateTimePlugin.md) — Injects current date and time values into templates using a flexible, token-based formatting syntax. Because date-time…
- [UuidPlugin](./src/plugins/dynamic/UuidPlugin.md) — Generates UUID v4 and URL-safe nano-ID strings inline within templates. Because ID generation depends on `crypto.random…
- [CaseTransformPlugin](./src/plugins/transform/CaseTransformPlugin.md) — Applies string case transformations to resolved context values. Supports eight case styles covering all common naming c…
- [RegexPlugin](./src/plugins/transform/RegexPlugin.md) — Applies inline regular-expression operations to resolved context values. The plugin enables match extraction, capture g…
- [StringSplitPlugin](./src/plugins/transform/StringSplitPlugin.md) — Splits a resolved string value on a configurable delimiter and returns a single element by index. This plugin solves th…
- [ArrayIndexPlugin](./src/plugins/variables/ArrayIndexPlugin.md) — Resolves bracket-notation array-index references in templates. Handles `{items[0]}`, `{items[-1]}`, and nested `{users[…
- [DotNotationPlugin](./src/plugins/variables/DotNotationPlugin.md) — Resolves simple variable references using dot-notation paths. This is the primary workhorse plugin for template renderi…
- [full](./src/presets/full.md) — Provides the complete plugin suite in two variants: `fullPreset()` matches `standardPreset()` exactly — it is the recom…
- [minimal](./src/presets/minimal.md) — Provides the lightest possible `PluginPreset` for the `TemplateEngine`. It bundles only two plugins — `array-index` and…
- [standard](./src/presets/standard.md) — Provides a comprehensive `PluginPreset` suitable for the majority of production use cases. It bundles all non-loop plug…
- [schemas](./src/schemas/templates.schemas.md) — <!-- begin:auto-docs --> _(no submodules)_- [templates.schemas](./src/schemas/templates.schemas.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/index.md) — <!-- begin:auto-docs --> _(no submodules)_- [index](./src/types/index.md) — <!-- begin:auto-docs --> _(no submodules)_- [escape](./src/utils/escape.md) — Provides three utility functions for handling escape sequences in template strings. These are low-level primitives used…
- [format](./src/utils/format.md) — Provides core formatting utilities used by the dynamic plugins: zero-padded number/string padding, date formatting with…
- [nested-value](./src/utils/nested-value.md) — Provides three functions for traversing, checking, and mutating plain object trees using dot-notation or bracket-notati…
- [templates-otel](./src/otel/templates-otel.md) — <!-- begin:auto-docs --> _(no submodules)_- [ConditionalPlugin](./src/plugins/control/ConditionalPlugin.md) — Conditionally includes or excludes blocks of template content based on context values. The plugin evaluates a condition…
- [LoopPlugin](./src/plugins/control/LoopPlugin.md) — Iterates over an array in the context object and renders a sub-template once for each element, concatenating the result…
- [CounterPlugin](./src/plugins/dynamic/CounterPlugin.md) — Provides stateful auto-incrementing counters within a single template rendering pass. Counters are scoped to the `Parse…
- [DateTimePlugin](./src/plugins/dynamic/DateTimePlugin.md) — Injects current date and time values into templates using a flexible, token-based formatting syntax. Because date-time…
- [UuidPlugin](./src/plugins/dynamic/UuidPlugin.md) — Generates UUID v4 and URL-safe nano-ID strings inline within templates. Because ID generation depends on `crypto.random…
- [CaseTransformPlugin](./src/plugins/transform/CaseTransformPlugin.md) — Applies string case transformations to resolved context values. Supports eight case styles covering all common naming c…
- [RegexPlugin](./src/plugins/transform/RegexPlugin.md) — Applies inline regular-expression operations to resolved context values. The plugin enables match extraction, capture g…
- [StringSplitPlugin](./src/plugins/transform/StringSplitPlugin.md) — Splits a resolved string value on a configurable delimiter and returns a single element by index. This plugin solves th…
- [ArrayIndexPlugin](./src/plugins/variables/ArrayIndexPlugin.md) — Resolves bracket-notation array-index references in templates. Handles `{items[0]}`, `{items[-1]}`, and nested `{users[…
- [DotNotationPlugin](./src/plugins/variables/DotNotationPlugin.md) — Resolves simple variable references using dot-notation paths. This is the primary workhorse plugin for template renderi…
- [full](./src/presets/full.md) — Provides the complete plugin suite in two variants: `fullPreset()` matches `standardPreset()` exactly — it is the recom…
- [minimal](./src/presets/minimal.md) — Provides the lightest possible `PluginPreset` for the `TemplateEngine`. It bundles only two plugins — `array-index` and…
- [standard](./src/presets/standard.md) — Provides a comprehensive `PluginPreset` suitable for the majority of production use cases. It bundles all non-loop plug…
- [schemas](./src/schemas/templates.schemas.md) — <!-- begin:auto-docs --> _(no submodules)_ - [templates.schemas](./src/schemas/templates.schemas.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/index.md) — <!-- begin:auto-docs --> _(no submodules)_ - [index](./src/types/index.md) — <!-- begin:auto-docs --> _(no submodules)_- [escape](./src/utils/escape.md) — Provides three utility functions for handling escape sequences in template strings. These are low-level primitives used…
- [format](./src/utils/format.md) — Provides core formatting utilities used by the dynamic plugins: zero-padded number/string padding, date formatting with…
- [nested-value](./src/utils/nested-value.md) — Provides three functions for traversing, checking, and mutating plain object trees using dot-notation or bracket-notati…
## Features
- Priority-sorted plugin pipeline (CRITICAL → HIGH → MEDIUM → LOW → MINIMAL)
- 9 built-in plugins covering variables, transforms, dynamic generation, and control flow
- 3 preset bundles (minimal, standard, full)
- Synchronous and asynchronous rendering paths
- `compile()` for reusable template objects
- Injectable `Clock` and `Rng` for deterministic testing
- `ParseState` per render pass — counters, cache, custom plugin state
- Zod schemas for runtime configuration validation
- 100% test coverage on all modules
## Installation
```bash
bun add @suaveplan/templates
```
## Quick Start
```ts
import { TemplateEngine, standardPreset } from "@suaveplan/templates";
const engine = new TemplateEngine().usePreset(standardPreset());
engine.parse("Hello, {name:pascal}! You have {count} messages.", {
name: "alice smith",
count: 5,
});
// → "Hello, AliceSmith! You have 5 messages."
```
## Plugins
### Variable Resolution
| Plugin | Module | Priority |
|--------|--------|----------|
| [DotNotationPlugin](src/plugins/variables/DotNotationPlugin.md) | `{name}`, `{user.email}` | LOW (20) |
| [ArrayIndexPlugin](src/plugins/variables/ArrayIndexPlugin.md) | `{items[0]}`, `{arr[-1]}` | LOW (20) |
### Transforms
| Plugin | Module | Priority |
|--------|--------|----------|
| [CaseTransformPlugin](src/plugins/transform/CaseTransformPlugin.md) | `{key:pascal}`, `{val:snake}` | MEDIUM (50) |
| [RegexPlugin](src/plugins/transform/RegexPlugin.md) | `{str:/pattern/flags}` | MEDIUM (50) |
| [StringSplitPlugin](src/plugins/transform/StringSplitPlugin.md) | `{email:@[0]}` | MEDIUM (50) |
### Dynamic
| Plugin | Module | Priority |
|--------|--------|----------|
| [CounterPlugin](src/plugins/dynamic/CounterPlugin.md) | `{_counter}`, `{_counter:name:1:3}` | HIGH (80) |
| [DateTimePlugin](src/plugins/dynamic/DateTimePlugin.md) | `{_date}`, `{_timestamp:s}` | HIGH (80) |
| [UuidPlugin](src/plugins/dynamic/UuidPlugin.md) | `{_uuid}`, `{_nanoid:10}` | HIGH (80) |
### Control Flow
| Plugin | Module | Priority |
|--------|--------|----------|
| [ConditionalPlugin](src/plugins/control/ConditionalPlugin.md) | `{if:cond}...{/if}` | CRITICAL (95) |
| [LoopPlugin](src/plugins/control/LoopPlugin.md) | `{each:items:el}...{/each}` | CRITICAL (95) |
## Presets
| Preset | Module | Included plugins |
|--------|--------|-----------------|
| [minimalPreset](src/presets/minimal.md) | dot-notation + array-index | 2 |
| [standardPreset](src/presets/standard.md) | all except loop | 9 |
| [fullPreset](src/presets/full.md) | same as standard | 9 |
| [fullPresetWithLoops](src/presets/full.md) | all 10 plugins | 10 |
## Engine API
| Symbol | Module |
|--------|--------|
| [TemplateEngine](src/engine/TemplateEngine.md) | Core engine class |
| [ParseState](src/engine/ParseState.md) | Render state interface and helpers |
## Types & Schemas
| Symbol | Module |
|--------|--------|
| [types](src/types/index.md) | All interfaces, enums, and `SKIP_PLUGIN` |
| [templates.schemas](src/schemas/templates.schemas.md) | Zod validation schemas |
## Utilities
| Symbol | Module |
|--------|--------|
| [nested-value](src/utils/nested-value.md) | `getNestedValue`, `hasNestedPath`, `setNestedValue` |
| [format](src/utils/format.md) | `pad`, `formatDate`, `generateUuid`, `generateNanoid` |
| [escape](src/utils/escape.md) | `escapeTemplate`, `unescapeTemplate` |
## Errors
| Symbol | Module |
|--------|--------|
| [error-codes](src/errors/error-codes.md) | `TEMPLATE_CODES` registry |
| [template-error](src/errors/template-error.md) | `TemplateError`, `PluginError`, `MaxDepthError` |
## Writing a Custom Plugin
```ts
import type { Plugin, PluginMatch, TemplateContext } from "@suaveplan/templates";
import { PluginPriority, SKIP_PLUGIN } from "@suaveplan/templates";
function createUpperBoldPlugin<TCtx extends TemplateContext>(): Plugin<TCtx> {
return {
name: "upper-bold",
priority: PluginPriority.MEDIUM,
pattern: /\{bold:([^}]+)\}/g,
transform(match: PluginMatch): string | typeof SKIP_PLUGIN {
const text = match.groups?.[1];
if (!text) return SKIP_PLUGIN;
return `**${text.toUpperCase()}**`;
},
};
}
const engine = new TemplateEngine().use(createUpperBoldPlugin());
engine.parse("Welcome {bold:alice}!", {});
// → "Welcome **ALICE**!"
```
## Loop Support
Loops require injecting the engine's internal render function:
```ts
import { TemplateEngine, fullPresetWithLoops } from "@suaveplan/templates";
import type { ParseState } from "@suaveplan/templates";
const engine = new TemplateEngine();
engine.usePreset(
fullPresetWithLoops(
(t: string, c: Record<string, unknown>, s: ParseState) =>
(engine as unknown as { parseInternal: typeof fn }).parseInternal(t, c, s)
)
);
engine.parse(
"{each:items:i}{_number}. {i.name}\n{/each}",
{ items: [{ name: "Alice" }, { name: "Bob" }] }
);
// → "1. Alice\n2. Bob\n"
```