@ackplus/nest-dynamic-templates 1.1.16 → 2.0.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/CHANGELOG.md +32 -0
- package/MIGRATION.md +97 -0
- package/README.md +201 -116
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/config/resolve-config.d.ts +7 -0
- package/dist/lib/config/resolve-config.js +50 -0
- package/dist/lib/config/resolve-config.js.map +1 -0
- package/dist/lib/constant.d.ts +1 -0
- package/dist/lib/constant.js +2 -1
- package/dist/lib/constant.js.map +1 -1
- package/dist/lib/dto/create-template-layout.dto.js +49 -12
- package/dist/lib/dto/create-template-layout.dto.js.map +1 -1
- package/dist/lib/dto/create-template.dto.js +59 -14
- package/dist/lib/dto/create-template.dto.js.map +1 -1
- package/dist/lib/dto/render-template.dto.d.ts +3 -6
- package/dist/lib/dto/render-template.dto.js +17 -38
- package/dist/lib/dto/render-template.dto.js.map +1 -1
- package/dist/lib/engines/language/html.engine.d.ts +2 -3
- package/dist/lib/engines/language/html.engine.js +4 -34
- package/dist/lib/engines/language/html.engine.js.map +1 -1
- package/dist/lib/engines/language/markdown.engine.d.ts +4 -2
- package/dist/lib/engines/language/markdown.engine.js +20 -20
- package/dist/lib/engines/language/markdown.engine.js.map +1 -1
- package/dist/lib/engines/language/mjml.engine.d.ts +4 -2
- package/dist/lib/engines/language/mjml.engine.js +18 -20
- package/dist/lib/engines/language/mjml.engine.js.map +1 -1
- package/dist/lib/engines/language/text.engine.d.ts +2 -1
- package/dist/lib/engines/language/text.engine.js +2 -1
- package/dist/lib/engines/language/text.engine.js.map +1 -1
- package/dist/lib/engines/language-engine.d.ts +4 -1
- package/dist/lib/engines/language-engine.js +2 -0
- package/dist/lib/engines/language-engine.js.map +1 -1
- package/dist/lib/engines/load-peer.d.ts +1 -0
- package/dist/lib/engines/load-peer.js +21 -0
- package/dist/lib/engines/load-peer.js.map +1 -0
- package/dist/lib/engines/template/ejs.engine.d.ts +6 -3
- package/dist/lib/engines/template/ejs.engine.js +13 -12
- package/dist/lib/engines/template/ejs.engine.js.map +1 -1
- package/dist/lib/engines/template/handlebars.engine.d.ts +8 -3
- package/dist/lib/engines/template/handlebars.engine.js +26 -13
- package/dist/lib/engines/template/handlebars.engine.js.map +1 -1
- package/dist/lib/engines/template/nunjucks.engine.d.ts +8 -8
- package/dist/lib/engines/template/nunjucks.engine.js +25 -37
- package/dist/lib/engines/template/nunjucks.engine.js.map +1 -1
- package/dist/lib/engines/template/pug.engine.d.ts +7 -5
- package/dist/lib/engines/template/pug.engine.js +11 -20
- package/dist/lib/engines/template/pug.engine.js.map +1 -1
- package/dist/lib/engines/template-engine.d.ts +4 -1
- package/dist/lib/engines/template-engine.js +2 -0
- package/dist/lib/engines/template-engine.js.map +1 -1
- package/dist/lib/entities/template-layout.entity.js +51 -14
- package/dist/lib/entities/template-layout.entity.js.map +1 -1
- package/dist/lib/entities/template.entity.js +58 -15
- package/dist/lib/entities/template.entity.js.map +1 -1
- package/dist/lib/errors/diagnose.d.ts +11 -0
- package/dist/lib/errors/diagnose.js +162 -0
- package/dist/lib/errors/diagnose.js.map +1 -0
- package/dist/lib/errors/template.errors.d.ts +77 -13
- package/dist/lib/errors/template.errors.js +104 -59
- package/dist/lib/errors/template.errors.js.map +1 -1
- package/dist/lib/interfaces/module-config.interface.d.ts +29 -18
- package/dist/lib/nest-dynamic-templates.module.d.ts +1 -4
- package/dist/lib/nest-dynamic-templates.module.js +40 -96
- package/dist/lib/nest-dynamic-templates.module.js.map +1 -1
- package/dist/lib/services/template-config.service.d.ts +12 -15
- package/dist/lib/services/template-config.service.js +30 -45
- package/dist/lib/services/template-config.service.js.map +1 -1
- package/dist/lib/services/template-engine.registry.d.ts +6 -10
- package/dist/lib/services/template-engine.registry.js +42 -50
- package/dist/lib/services/template-engine.registry.js.map +1 -1
- package/dist/lib/services/template-layout.service.d.ts +1 -3
- package/dist/lib/services/template-layout.service.js +80 -152
- package/dist/lib/services/template-layout.service.js.map +1 -1
- package/dist/lib/services/template.service.d.ts +1 -3
- package/dist/lib/services/template.service.js +107 -209
- package/dist/lib/services/template.service.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +17 -20
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 2.0.0
|
|
4
|
+
|
|
5
|
+
A focused redesign around clearer configuration, diagnostic errors and a lighter install. See [MIGRATION.md](./MIGRATION.md) for upgrade steps.
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **Diagnostic render errors.** Failures now throw `TemplateRenderError` carrying a structured `details` payload: the missing variable, the source `location` (line/column), a `snippet` of the offending line, the `contextKeys` you actually passed, and an actionable `hint`. The original error is preserved on `cause`.
|
|
9
|
+
- **Stable error codes** via `TemplateErrorCode` and an `isTemplateError()` guard. Every error extends its matching NestJS HTTP exception (404/422/400/403/409/500).
|
|
10
|
+
- **Flat configuration**: top-level `filters`, `globals` and `engineOptions`.
|
|
11
|
+
- **Custom filters/globals now work across all engines** (Nunjucks, Handlebars, EJS, Pug), not just Nunjucks.
|
|
12
|
+
- **Real Markdown rendering** via the optional `marked` peer.
|
|
13
|
+
- VitePress documentation site (GitHub Pages).
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- The `engines` list now **gates which engines are loaded** — only enabled engines are instantiated, making peer dependencies genuinely optional. The list is replaced, not merged with defaults.
|
|
17
|
+
- `TemplateConfigService` is now an injectable instance service (was a global static); this fixes `forRootAsync` timing.
|
|
18
|
+
- Render failures are **HTTP 422** (were 500).
|
|
19
|
+
- `render()` returns `subject: string | null` when there is no subject (was `''`).
|
|
20
|
+
- The HTML processor is a pass-through (the old strict validation that rejected valid output is removed).
|
|
21
|
+
- Engines load lazily; nothing is required until first use.
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
- MJML validation errors are now readable (were serialized as `[object Object]`).
|
|
25
|
+
- A missing layout now throws `TemplateNotFoundError` (404) instead of being miscategorized as a generic layout error.
|
|
26
|
+
- Removed double-wrapped error messages.
|
|
27
|
+
- The default HTML render path no longer fails with `"Invalid HTML content"`.
|
|
28
|
+
|
|
29
|
+
### Removed
|
|
30
|
+
- Required peer dependencies `@faker-js/faker` and `htmlparser2`; internal `yargs` and `deepmerge`.
|
|
31
|
+
- Error classes `TemplateEngineError`, `TemplateLanguageError`, `TemplateLayoutError`, `TemplateContentError`, `TemplateValidationError` (superseded by `TemplateRenderError` + `TemplateInputError`).
|
|
32
|
+
- Inline `content`/`language` fields on `RenderTemplateDto` (use `renderContent()`), and the `renderEngine()`/`renderLanguage()` service helpers.
|
package/MIGRATION.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Migrating to v2
|
|
2
|
+
|
|
3
|
+
v2 is a focused redesign around three goals: **clearer configuration**, **diagnostic errors**, and a **lighter install**. Most apps only need the config rename in step 1.
|
|
4
|
+
|
|
5
|
+
## 1. Configuration: flatten `enginesOptions`
|
|
6
|
+
|
|
7
|
+
The nested `enginesOptions` block is replaced by top-level fields. The old shape still works (with a one-time deprecation warning), but you should migrate:
|
|
8
|
+
|
|
9
|
+
```diff
|
|
10
|
+
NestDynamicTemplatesModule.forRoot({
|
|
11
|
+
engines: { template: ['njk'], language: ['html', 'mjml'] },
|
|
12
|
+
- enginesOptions: {
|
|
13
|
+
- filters: { formatDate },
|
|
14
|
+
- globalValues: { brandName: 'Acme' },
|
|
15
|
+
- template: { njk: { autoescape: true } },
|
|
16
|
+
- language: { mjml: { validationLevel: 'soft' } },
|
|
17
|
+
- },
|
|
18
|
+
+ filters: { formatDate },
|
|
19
|
+
+ globals: { brandName: 'Acme' }, // renamed from globalValues
|
|
20
|
+
+ engineOptions: {
|
|
21
|
+
+ template: { njk: { autoescape: true } },
|
|
22
|
+
+ language: { mjml: { validationLevel: 'soft' } },
|
|
23
|
+
+ },
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## 2. The `engines` list now gates loading
|
|
28
|
+
|
|
29
|
+
In v1 the `engines` list was ignored — every engine was loaded regardless. In v2 **only enabled engines are instantiated**, which is what makes the peer dependencies truly optional.
|
|
30
|
+
|
|
31
|
+
- List every engine you use under `engines.template` / `engines.language`.
|
|
32
|
+
- Install only those engines' peer packages.
|
|
33
|
+
- Rendering with a disabled engine throws `TemplateEngineUnavailableError` with a hint.
|
|
34
|
+
|
|
35
|
+
Defaults are unchanged: `{ template: ['njk'], language: ['html', 'mjml', 'txt'] }`.
|
|
36
|
+
|
|
37
|
+
> Note: the engine list is now **replaced**, not merged. In v1 (deepmerge) passing `template: ['hbs']` produced `['njk', 'hbs']`; in v2 you get exactly `['hbs']`.
|
|
38
|
+
|
|
39
|
+
## 3. Errors are restructured
|
|
40
|
+
|
|
41
|
+
The granular v1 error classes are gone, replaced by a smaller, richer set:
|
|
42
|
+
|
|
43
|
+
| Removed (v1) | Use instead (v2) |
|
|
44
|
+
| --- | --- |
|
|
45
|
+
| `TemplateEngineError`, `TemplateLanguageError`, `TemplateLayoutError`, `TemplateContentError` | `TemplateRenderError` (rich `details`, HTTP **422**) |
|
|
46
|
+
| `TemplateValidationError` | `TemplateInputError` (HTTP 400) |
|
|
47
|
+
| raw `NotFoundException` | `TemplateNotFoundError` (still `instanceof NotFoundException`) |
|
|
48
|
+
|
|
49
|
+
Key changes:
|
|
50
|
+
|
|
51
|
+
- Render failures are now **HTTP 422** (were 500) — they're input/data errors, not server faults.
|
|
52
|
+
- Each error carries `error.code` (a `TemplateErrorCode`) and `error.details` (variable, location, snippet, context keys, hint). The original error is on `error.cause`.
|
|
53
|
+
- Use the `isTemplateError(err)` guard, or switch on `err.code`. Each error still extends its matching NestJS HTTP exception.
|
|
54
|
+
|
|
55
|
+
```diff
|
|
56
|
+
- } catch (err) {
|
|
57
|
+
- if (err instanceof TemplateEngineError) { /* ... */ }
|
|
58
|
+
+ } catch (err) {
|
|
59
|
+
+ if (isTemplateError(err)) {
|
|
60
|
+
+ console.error(err.code, err.details.missingVariable, err.details.contextKeys);
|
|
61
|
+
+ }
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## 4. `TemplateConfigService` is now injectable
|
|
66
|
+
|
|
67
|
+
It was a global static in v1; it's a normal injectable service in v2 (fixes `forRootAsync` timing). The static methods (`setOptions`, `reset`, `hasConfig`, `get`) are removed. Inject it and call instance methods:
|
|
68
|
+
|
|
69
|
+
```diff
|
|
70
|
+
- TemplateConfigService.getOptions();
|
|
71
|
+
+ constructor(private readonly config: TemplateConfigService) {}
|
|
72
|
+
+ this.config.getOptions();
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## 5. `render()` no longer accepts inline content
|
|
76
|
+
|
|
77
|
+
`RenderTemplateDto`'s `content` / `language` fields (which never actually worked in `render()`) are removed. To render a raw string, use `renderContent()`:
|
|
78
|
+
|
|
79
|
+
```diff
|
|
80
|
+
- await templates.render({ content: 'Hi {{ name }}', language: 'html', context });
|
|
81
|
+
+ await templates.renderContent({ content: 'Hi {{ name }}', engine: 'njk', language: 'html', context });
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The low-level `renderEngine()` / `renderLanguage()` helpers were also removed from the services — use `renderContent()`.
|
|
85
|
+
|
|
86
|
+
## 6. Output and behavior tweaks
|
|
87
|
+
|
|
88
|
+
- `render()` returns `subject: string | null` (was `''`) when a template has no subject.
|
|
89
|
+
- The **HTML** processor is now a pass-through. v1 ran a strict validation that could reject valid output with `"Invalid HTML content"`; that's gone.
|
|
90
|
+
- **Markdown** now actually renders (via the optional `marked` peer). It was a no-op in v1.
|
|
91
|
+
- Only **active** templates (`isActive: true`) are resolved by `render()`.
|
|
92
|
+
|
|
93
|
+
## 7. Dependencies
|
|
94
|
+
|
|
95
|
+
- **Removed required peers:** `@faker-js/faker`, `htmlparser2`. Remove them from your install if you only added them for this library.
|
|
96
|
+
- **New optional peer:** `marked` — install it only if you enable the `md` language.
|
|
97
|
+
- No database schema changes — your existing tables are compatible.
|
package/README.md
CHANGED
|
@@ -1,95 +1,97 @@
|
|
|
1
1
|
# @ackplus/nest-dynamic-templates
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Database-backed, multi-engine template rendering for NestJS — with **error messages that actually tell you what went wrong**.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Store templates in your database, render them at runtime with the engine of your choice (Nunjucks, Handlebars, EJS, Pug), and post-process the output as HTML, MJML, Markdown or plain text. Built for transactional email, notifications, PDFs and any per-tenant/per-locale content.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- 📝 **Multi-Format** - Render HTML, MJML, Markdown, or Plain Text
|
|
9
|
-
- 🗄️ **Database Storage** - Store templates in your database (TypeORM support)
|
|
10
|
-
- 🎨 **Layout Support** - Create reusable layouts for your templates
|
|
11
|
-
- 🌍 **Scope & Locale** - Manage templates by scope (system/user/tenant) and locale (en/es/etc.)
|
|
12
|
-
- 🚀 **Dynamic Rendering** - Render templates with dynamic context at runtime
|
|
7
|
+
📖 **Full documentation:** https://ack-solutions.github.io/nest-dynamic-templates/
|
|
13
8
|
|
|
14
|
-
|
|
9
|
+
---
|
|
15
10
|
|
|
16
|
-
|
|
17
|
-
npm install @ackplus/nest-dynamic-templates
|
|
18
|
-
# or
|
|
19
|
-
pnpm add @ackplus/nest-dynamic-templates
|
|
20
|
-
# or
|
|
21
|
-
yarn add @ackplus/nest-dynamic-templates
|
|
22
|
-
```
|
|
11
|
+
## Why this library
|
|
23
12
|
|
|
24
|
-
|
|
13
|
+
- 🧩 **Two-stage pipeline** — a *template engine* interpolates your variables, then a *language processor* turns the result into final markup. Mix and match (e.g. Nunjucks → MJML).
|
|
14
|
+
- 🗄️ **Database storage** — templates and layouts live in your DB (TypeORM), versioned and editable at runtime.
|
|
15
|
+
- 🌍 **Scope & locale resolution** — ship `system` defaults and let a `user`/`tenant`/`organization` override them, per locale, with automatic fallback.
|
|
16
|
+
- 🪶 **Lightweight & lazy** — only the engines you enable are loaded, so you only install the peers you actually use.
|
|
17
|
+
- 🛑 **Diagnostic errors** — when a render fails you get the **missing variable**, the **source line**, the **context keys you passed**, and an **actionable hint** — not a generic 500.
|
|
25
18
|
|
|
26
|
-
|
|
19
|
+
## Install
|
|
27
20
|
|
|
28
21
|
```bash
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
# Template Engines (install at least one)
|
|
33
|
-
npm install nunjucks @types/nunjucks
|
|
34
|
-
# OR
|
|
35
|
-
npm install handlebars
|
|
36
|
-
# OR
|
|
37
|
-
npm install ejs @types/ejs
|
|
38
|
-
# OR
|
|
39
|
-
npm install pug @types/pug
|
|
40
|
-
|
|
41
|
-
# Language Support (optional)
|
|
42
|
-
npm install mjml @types/mjml # For MJML support
|
|
43
|
-
npm install htmlparser2 # For HTML processing
|
|
22
|
+
npm install @ackplus/nest-dynamic-templates
|
|
23
|
+
# peer deps you always need:
|
|
24
|
+
npm install @nestjs/typeorm typeorm reflect-metadata
|
|
44
25
|
```
|
|
45
26
|
|
|
46
|
-
|
|
27
|
+
Then install **only the engines you enable**:
|
|
28
|
+
|
|
29
|
+
| You enable | Install |
|
|
30
|
+
| --- | --- |
|
|
31
|
+
| `njk` (Nunjucks, default) | `npm install nunjucks` |
|
|
32
|
+
| `hbs` (Handlebars) | `npm install handlebars` |
|
|
33
|
+
| `ejs` | `npm install ejs` |
|
|
34
|
+
| `pug` | `npm install pug` |
|
|
35
|
+
| `mjml` (email) | `npm install mjml` |
|
|
36
|
+
| `md` (Markdown) | `npm install marked` |
|
|
37
|
+
| `html`, `txt` | nothing — built in |
|
|
47
38
|
|
|
48
|
-
|
|
39
|
+
## Quick start
|
|
49
40
|
|
|
50
|
-
|
|
41
|
+
### 1. Register the module
|
|
51
42
|
|
|
52
43
|
```typescript
|
|
53
44
|
import { Module } from '@nestjs/common';
|
|
54
45
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
|
55
|
-
import {
|
|
46
|
+
import {
|
|
47
|
+
NestDynamicTemplatesModule,
|
|
48
|
+
TemplateEngineEnum,
|
|
49
|
+
TemplateLanguageEnum,
|
|
50
|
+
} from '@ackplus/nest-dynamic-templates';
|
|
56
51
|
|
|
57
52
|
@Module({
|
|
58
53
|
imports: [
|
|
59
54
|
TypeOrmModule.forRoot({
|
|
60
|
-
|
|
55
|
+
type: 'postgres',
|
|
56
|
+
// ...your db config
|
|
57
|
+
autoLoadEntities: true, // picks up the library's entities automatically
|
|
61
58
|
}),
|
|
62
59
|
NestDynamicTemplatesModule.forRoot({
|
|
60
|
+
isGlobal: true,
|
|
63
61
|
engines: {
|
|
64
|
-
template: [TemplateEngineEnum.NUNJUCKS],
|
|
65
|
-
language: [TemplateLanguageEnum.HTML, TemplateLanguageEnum.MJML]
|
|
62
|
+
template: [TemplateEngineEnum.NUNJUCKS],
|
|
63
|
+
language: [TemplateLanguageEnum.HTML, TemplateLanguageEnum.MJML],
|
|
66
64
|
},
|
|
67
|
-
isGlobal: true, // Optional: make module global
|
|
68
65
|
}),
|
|
69
66
|
],
|
|
70
67
|
})
|
|
71
68
|
export class AppModule {}
|
|
72
69
|
```
|
|
73
70
|
|
|
74
|
-
|
|
71
|
+
> Prefer explicit entities (e.g. for migrations)? Import `NestDynamicTemplatesEntities` and spread it into your TypeORM `entities` array.
|
|
75
72
|
|
|
76
|
-
|
|
73
|
+
### 2. Create a template
|
|
77
74
|
|
|
78
75
|
```typescript
|
|
79
76
|
import { Injectable } from '@nestjs/common';
|
|
80
|
-
import {
|
|
77
|
+
import {
|
|
78
|
+
TemplateService,
|
|
79
|
+
TemplateEngineEnum,
|
|
80
|
+
TemplateLanguageEnum,
|
|
81
|
+
} from '@ackplus/nest-dynamic-templates';
|
|
81
82
|
|
|
82
83
|
@Injectable()
|
|
83
|
-
export class
|
|
84
|
-
constructor(private readonly
|
|
84
|
+
export class TemplatesSeeder {
|
|
85
|
+
constructor(private readonly templates: TemplateService) {}
|
|
85
86
|
|
|
86
|
-
|
|
87
|
-
|
|
87
|
+
seed() {
|
|
88
|
+
return this.templates.createTemplate({
|
|
88
89
|
name: 'welcome-email',
|
|
89
|
-
|
|
90
|
+
displayName: 'Welcome email',
|
|
91
|
+
scope: 'system',
|
|
90
92
|
locale: 'en',
|
|
91
|
-
subject: 'Welcome, {{
|
|
92
|
-
content: '<h1>Hello {{
|
|
93
|
+
subject: 'Welcome, {{ firstName }}!',
|
|
94
|
+
content: '<h1>Hello {{ firstName }}</h1><p>Thanks for joining.</p>',
|
|
93
95
|
engine: TemplateEngineEnum.NUNJUCKS,
|
|
94
96
|
language: TemplateLanguageEnum.HTML,
|
|
95
97
|
type: 'email',
|
|
@@ -98,101 +100,184 @@ export class MyService {
|
|
|
98
100
|
}
|
|
99
101
|
```
|
|
100
102
|
|
|
101
|
-
### 3. Render
|
|
103
|
+
### 3. Render it
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
const { subject, content } = await this.templates.render({
|
|
107
|
+
name: 'welcome-email',
|
|
108
|
+
scope: 'system',
|
|
109
|
+
locale: 'en',
|
|
110
|
+
context: { firstName: 'Ada' },
|
|
111
|
+
});
|
|
112
|
+
// subject -> "Welcome, Ada!"
|
|
113
|
+
// content -> "<h1>Hello Ada</h1><p>Thanks for joining.</p>"
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Need to render a raw string without touching the DB? Use `renderContent()`:
|
|
102
117
|
|
|
103
|
-
|
|
118
|
+
```typescript
|
|
119
|
+
const html = await this.templates.renderContent({
|
|
120
|
+
content: 'Hi {{ name }}',
|
|
121
|
+
engine: TemplateEngineEnum.NUNJUCKS,
|
|
122
|
+
language: TemplateLanguageEnum.HTML,
|
|
123
|
+
context: { name: 'World' },
|
|
124
|
+
});
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Error handling
|
|
128
|
+
|
|
129
|
+
This is the headline feature. Every render failure throws a typed error with a structured `details` payload and the original error attached as `cause`. Missing-variable failures name the variable and list the context you actually passed:
|
|
104
130
|
|
|
105
131
|
```typescript
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
132
|
+
import { TemplateRenderError, TemplateErrorCode, isTemplateError } from '@ackplus/nest-dynamic-templates';
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
await templates.render({ name: 'welcome-email', context: { email: 'a@b.com' } });
|
|
136
|
+
} catch (err) {
|
|
137
|
+
if (isTemplateError(err)) {
|
|
138
|
+
console.error(err.code); // 'TEMPLATE_RENDER_FAILED'
|
|
139
|
+
console.error(err.details.missingVariable); // 'firstName'
|
|
140
|
+
console.error(err.details.contextKeys); // ['email']
|
|
141
|
+
console.error(err.details.location); // { line: 1, column: 16 }
|
|
142
|
+
console.error(err.details.snippet); // '<h1>Hello {{ firstName }}</h1>...'
|
|
143
|
+
console.error(err.details.hint); // 'Pass "firstName" in the render context. ...'
|
|
144
|
+
}
|
|
118
145
|
}
|
|
119
146
|
```
|
|
120
147
|
|
|
121
|
-
|
|
148
|
+
The thrown message reads:
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
Failed to render template "welcome-email" [njk → html, scope=system, locale=en]: variable "firstName" is undefined.
|
|
152
|
+
```
|
|
122
153
|
|
|
123
|
-
|
|
154
|
+
Each error also **extends the matching NestJS HTTP exception**, so Nest's exception filter maps the right status automatically and your existing `catch (NotFoundException)` keeps working:
|
|
124
155
|
|
|
125
|
-
|
|
156
|
+
| Error | HTTP | `code` |
|
|
157
|
+
| --- | --- | --- |
|
|
158
|
+
| `TemplateRenderError` | 422 | `TEMPLATE_RENDER_FAILED` |
|
|
159
|
+
| `TemplateNotFoundError` | 404 | `TEMPLATE_NOT_FOUND` |
|
|
160
|
+
| `TemplateInputError` | 400 | `TEMPLATE_INVALID_INPUT` |
|
|
161
|
+
| `TemplateForbiddenError` | 403 | `TEMPLATE_FORBIDDEN` |
|
|
162
|
+
| `TemplateConflictError` | 409 | `TEMPLATE_CONFLICT` |
|
|
163
|
+
| `TemplateEngineUnavailableError` | 500 | `TEMPLATE_ENGINE_UNAVAILABLE` |
|
|
126
164
|
|
|
127
|
-
|
|
128
|
-
Renders a template stored in the database.
|
|
165
|
+
## Configuration
|
|
129
166
|
|
|
130
167
|
```typescript
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
168
|
+
NestDynamicTemplatesModule.forRoot({
|
|
169
|
+
isGlobal: true,
|
|
170
|
+
|
|
171
|
+
// Only these engines are loaded. Default: { template: ['njk'], language: ['html','mjml','txt'] }
|
|
172
|
+
engines: {
|
|
173
|
+
template: [TemplateEngineEnum.NUNJUCKS, TemplateEngineEnum.HANDLEBARS],
|
|
174
|
+
language: [TemplateLanguageEnum.HTML, TemplateLanguageEnum.MJML, TemplateLanguageEnum.MARKDOWN],
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
// Custom filters/helpers — available in EVERY template engine.
|
|
178
|
+
filters: {
|
|
179
|
+
formatDate: (d: Date, fmt: string) => /* ... */ '',
|
|
180
|
+
formatCurrency: (n: number, ccy: string) =>
|
|
181
|
+
new Intl.NumberFormat('en-US', { style: 'currency', currency: ccy }).format(n),
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
// Global values injected into every render (strings, numbers, objects or functions).
|
|
185
|
+
globals: {
|
|
186
|
+
brandName: 'Acme',
|
|
187
|
+
year: () => new Date().getFullYear(),
|
|
188
|
+
},
|
|
189
|
+
|
|
190
|
+
// Raw options forwarded to the underlying engine libraries.
|
|
191
|
+
engineOptions: {
|
|
192
|
+
template: { njk: { autoescape: true, trimBlocks: true } },
|
|
193
|
+
language: { mjml: { validationLevel: 'soft' } },
|
|
194
|
+
},
|
|
136
195
|
});
|
|
137
196
|
```
|
|
138
197
|
|
|
139
|
-
|
|
140
|
-
Renders raw content string directly without fetching from the database.
|
|
198
|
+
### Async configuration
|
|
141
199
|
|
|
142
200
|
```typescript
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
201
|
+
NestDynamicTemplatesModule.forRootAsync({
|
|
202
|
+
isGlobal: true,
|
|
203
|
+
inject: [ConfigService],
|
|
204
|
+
useFactory: (config: ConfigService) => ({
|
|
205
|
+
engines: { template: ['njk'], language: ['html', 'mjml'] },
|
|
206
|
+
globals: { appUrl: config.get('APP_URL') },
|
|
207
|
+
}),
|
|
147
208
|
});
|
|
148
209
|
```
|
|
149
210
|
|
|
150
|
-
|
|
151
|
-
|
|
211
|
+
## Scope & locale resolution
|
|
212
|
+
|
|
213
|
+
`render()` resolves a template by trying, in order:
|
|
214
|
+
|
|
215
|
+
1. requested **scope** + requested **locale**
|
|
216
|
+
2. requested **scope** + `en`
|
|
217
|
+
3. **system** scope + requested locale
|
|
218
|
+
4. **system** scope + `en`
|
|
219
|
+
|
|
220
|
+
This lets you ship `system` defaults and override them per tenant/user and per language. Only **active** templates (`isActive: true`) are resolved. Example:
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
// Falls back to the system template if this user has no override.
|
|
224
|
+
await templates.render({ name: 'welcome-email', scope: 'user', scopeId: userId, locale: 'fr' });
|
|
225
|
+
```
|
|
152
226
|
|
|
153
|
-
|
|
154
|
-
Updates an existing template. If you try to update a `system` template without permission, it may create a scoped override instead.
|
|
227
|
+
## Template fields
|
|
155
228
|
|
|
156
|
-
|
|
229
|
+
A template is a database row. The key fields (full reference with use cases in the [docs](https://ack-solutions.github.io/nest-dynamic-templates/reference/template-fields)):
|
|
157
230
|
|
|
158
|
-
|
|
231
|
+
| Field | Required | Default | Purpose |
|
|
232
|
+
| --- | --- | --- | --- |
|
|
233
|
+
| `name` | ✅ | — | Stable id you render by, e.g. `welcome-email`. |
|
|
234
|
+
| `scope` | — | `system` | **Who owns this version** — `system` (shared default) or a custom owner kind like `tenant`/`user`. |
|
|
235
|
+
| `scopeId` | — | `null` | **Which owner** inside the scope (e.g. the tenant id). Empty for `system`. |
|
|
236
|
+
| `locale` | — | `en` | Language variant; missing locales fall back to `en`. |
|
|
237
|
+
| `engine` | — | `njk` | Template engine: `njk`, `hbs`, `ejs`, `pug`. |
|
|
238
|
+
| `language` | — | `null` | Output processor: `html`, `mjml`, `md`, `txt`. |
|
|
239
|
+
| `subject` | — | `null` | Subject line (emails); rendered with the engine. |
|
|
240
|
+
| `content` | ✅ | — | The template body. |
|
|
241
|
+
| `templateLayoutName` | — | `null` | Layout to wrap this content in. |
|
|
242
|
+
| `displayName` | — | — | Human-friendly label for admin UIs. |
|
|
243
|
+
| `type` | — | `null` | Free category for grouping, e.g. `email`/`sms`. |
|
|
244
|
+
| `previewContext` | — | `null` | Sample data for previews (not used at render time). |
|
|
245
|
+
| `isActive` | — | `true` | Set `false` to disable without deleting. |
|
|
159
246
|
|
|
160
|
-
|
|
161
|
-
|
|
247
|
+
**`scope` vs `scopeId`** is the part to understand: `scope` is the *kind* of owner (`system`, or your own `tenant`/`user`/…), and `scopeId` is the *exact* owner (the tenant id). Together with `name` + `locale` they uniquely identify a template, and a render falls back from a specific override to the `system` default — so you only store overrides where they differ.
|
|
248
|
+
|
|
249
|
+
## Layouts
|
|
250
|
+
|
|
251
|
+
Layouts are reusable wrappers (e.g. an email shell). The child content is injected where the layout references `{{ content }}`:
|
|
162
252
|
|
|
163
253
|
```typescript
|
|
164
|
-
await
|
|
165
|
-
name: '
|
|
166
|
-
|
|
254
|
+
await layouts.createTemplateLayout({
|
|
255
|
+
name: 'email-shell',
|
|
256
|
+
displayName: 'Email shell',
|
|
167
257
|
engine: TemplateEngineEnum.NUNJUCKS,
|
|
258
|
+
language: TemplateLanguageEnum.HTML,
|
|
259
|
+
content: '<html><body><header>{{ brandName }}</header>{{ content }}</body></html>',
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// Attach it to a template:
|
|
263
|
+
await templates.createTemplate({
|
|
264
|
+
name: 'welcome-email',
|
|
265
|
+
templateLayoutName: 'email-shell',
|
|
266
|
+
/* ...rest... */
|
|
168
267
|
});
|
|
169
268
|
```
|
|
170
269
|
|
|
171
|
-
##
|
|
270
|
+
## Services
|
|
172
271
|
|
|
173
|
-
|
|
272
|
+
- **`TemplateService`** — `render`, `renderContent`, `createTemplate`, `updateTemplate`, `overwriteSystemTemplate`, `deleteTemplate`, `getTemplates`, `findTemplate`, `getTemplateById`.
|
|
273
|
+
- **`TemplateLayoutService`** — the same surface for layouts.
|
|
274
|
+
- **`TemplateConfigService`** — read-only accessor over the resolved config.
|
|
275
|
+
- **`TemplateEngineRegistryService`** — access the engine instances directly.
|
|
174
276
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
// Template Logic Engines
|
|
179
|
-
template: [
|
|
180
|
-
TemplateEngineEnum.NUNJUCKS,
|
|
181
|
-
TemplateEngineEnum.HANDLEBARS,
|
|
182
|
-
TemplateEngineEnum.EJS,
|
|
183
|
-
TemplateEngineEnum.PUG
|
|
184
|
-
],
|
|
185
|
-
// Output Language Processors
|
|
186
|
-
language: [
|
|
187
|
-
TemplateLanguageEnum.HTML,
|
|
188
|
-
TemplateLanguageEnum.MJML,
|
|
189
|
-
TemplateLanguageEnum.TEXT,
|
|
190
|
-
TemplateLanguageEnum.MARKDOWN
|
|
191
|
-
]
|
|
192
|
-
}
|
|
193
|
-
})
|
|
194
|
-
```
|
|
277
|
+
## Migrating from v1
|
|
278
|
+
|
|
279
|
+
v2 is a focused redesign. See **[MIGRATION.md](./MIGRATION.md)** for the (short) list of breaking changes and how to update — most apps only need to rename `enginesOptions` to the new flat `filters` / `globals` / `engineOptions`.
|
|
195
280
|
|
|
196
|
-
##
|
|
281
|
+
## License
|
|
197
282
|
|
|
198
|
-
|
|
283
|
+
MIT © AckPlus
|
package/dist/index.d.ts
CHANGED
|
@@ -6,9 +6,11 @@ export * from './lib/services/template-config.service';
|
|
|
6
6
|
export * from './lib/services/template-engine.registry';
|
|
7
7
|
export * from './lib/interfaces/module-config.interface';
|
|
8
8
|
export * from './lib/interfaces/template.types';
|
|
9
|
+
export * from './lib/config/resolve-config';
|
|
9
10
|
export * from './lib/entities/template.entity';
|
|
10
11
|
export * from './lib/entities/template-layout.entity';
|
|
11
12
|
export * from './lib/errors/template.errors';
|
|
13
|
+
export * from './lib/errors/diagnose';
|
|
12
14
|
export * from './lib/constant';
|
|
13
15
|
export * from './lib/engines/template-engine';
|
|
14
16
|
export * from './lib/engines/language-engine';
|
package/dist/index.js
CHANGED
|
@@ -24,9 +24,11 @@ __exportStar(require("./lib/services/template-config.service"), exports);
|
|
|
24
24
|
__exportStar(require("./lib/services/template-engine.registry"), exports);
|
|
25
25
|
__exportStar(require("./lib/interfaces/module-config.interface"), exports);
|
|
26
26
|
__exportStar(require("./lib/interfaces/template.types"), exports);
|
|
27
|
+
__exportStar(require("./lib/config/resolve-config"), exports);
|
|
27
28
|
__exportStar(require("./lib/entities/template.entity"), exports);
|
|
28
29
|
__exportStar(require("./lib/entities/template-layout.entity"), exports);
|
|
29
30
|
__exportStar(require("./lib/errors/template.errors"), exports);
|
|
31
|
+
__exportStar(require("./lib/errors/diagnose"), exports);
|
|
30
32
|
__exportStar(require("./lib/constant"), exports);
|
|
31
33
|
__exportStar(require("./lib/engines/template-engine"), exports);
|
|
32
34
|
__exportStar(require("./lib/engines/language-engine"), exports);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,kFAAkF;AAClF,oEAAqE;AAErE,sEAAoD;AAGpD,kEAAgD;AAChD,yEAAuD;AACvD,yEAAuD;AACvD,0EAAwD;AAGxD,2EAAyD;AACzD,kEAAgD;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,kFAAkF;AAClF,oEAAqE;AAErE,sEAAoD;AAGpD,kEAAgD;AAChD,yEAAuD;AACvD,yEAAuD;AACvD,0EAAwD;AAGxD,2EAAyD;AACzD,kEAAgD;AAChD,8DAA4C;AAG5C,iEAA+C;AAC/C,wEAAsD;AAGtD,+DAA6C;AAC7C,wDAAsC;AAGtC,iDAA+B;AAG/B,gEAA8C;AAC9C,gEAA8C;AAG9C,gEAA8C;AAC9C,gEAA8C;AAC9C,wEAAsD;AACtD,gEAA8C;AAC9C,uEAAqD;AACrD,uEAAqD;AACrD,+EAA6D;AAC7D,uEAAqD;AAExC,QAAA,4BAA4B,GAAG;IACxC,qCAAmB;IACnB,kDAAyB;CAC5B,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { TemplateEngineEnum, TemplateLanguageEnum } from '../interfaces/template.types';
|
|
2
|
+
import { NestDynamicTemplatesModuleConfig, ResolvedTemplatesConfig } from '../interfaces/module-config.interface';
|
|
3
|
+
export declare const DEFAULT_ENGINES: {
|
|
4
|
+
template: TemplateEngineEnum[];
|
|
5
|
+
language: TemplateLanguageEnum[];
|
|
6
|
+
};
|
|
7
|
+
export declare function resolveConfig(config?: NestDynamicTemplatesModuleConfig): ResolvedTemplatesConfig;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_ENGINES = void 0;
|
|
4
|
+
exports.resolveConfig = resolveConfig;
|
|
5
|
+
const template_types_1 = require("../interfaces/template.types");
|
|
6
|
+
exports.DEFAULT_ENGINES = {
|
|
7
|
+
template: [template_types_1.TemplateEngineEnum.NUNJUCKS],
|
|
8
|
+
language: [
|
|
9
|
+
template_types_1.TemplateLanguageEnum.HTML,
|
|
10
|
+
template_types_1.TemplateLanguageEnum.MJML,
|
|
11
|
+
template_types_1.TemplateLanguageEnum.TEXT,
|
|
12
|
+
],
|
|
13
|
+
};
|
|
14
|
+
let warnedAboutLegacy = false;
|
|
15
|
+
function resolveConfig(config = {}) {
|
|
16
|
+
const legacy = mapLegacy(config);
|
|
17
|
+
return {
|
|
18
|
+
isGlobal: config.isGlobal ?? false,
|
|
19
|
+
engines: {
|
|
20
|
+
template: dedupe(config.engines?.template ?? exports.DEFAULT_ENGINES.template),
|
|
21
|
+
language: dedupe(config.engines?.language ?? exports.DEFAULT_ENGINES.language),
|
|
22
|
+
},
|
|
23
|
+
filters: { ...legacy.filters, ...config.filters },
|
|
24
|
+
globals: { ...legacy.globals, ...config.globals },
|
|
25
|
+
engineOptions: {
|
|
26
|
+
template: { ...legacy.engineOptions.template, ...config.engineOptions?.template },
|
|
27
|
+
language: { ...legacy.engineOptions.language, ...config.engineOptions?.language },
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function mapLegacy(config) {
|
|
32
|
+
const legacy = config.enginesOptions;
|
|
33
|
+
if (legacy && !warnedAboutLegacy) {
|
|
34
|
+
warnedAboutLegacy = true;
|
|
35
|
+
console.warn('[nest-dynamic-templates] `enginesOptions` is deprecated. Use top-level ' +
|
|
36
|
+
'`filters`, `globals` and `engineOptions` instead. See the v2 migration guide.');
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
filters: legacy?.filters ?? {},
|
|
40
|
+
globals: legacy?.globalValues ?? {},
|
|
41
|
+
engineOptions: {
|
|
42
|
+
template: legacy?.template ?? {},
|
|
43
|
+
language: legacy?.language ?? {},
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function dedupe(items) {
|
|
48
|
+
return Array.from(new Set(items));
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=resolve-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-config.js","sourceRoot":"","sources":["../../../src/lib/config/resolve-config.ts"],"names":[],"mappings":";;;AAyBA,sCAkBC;AA3CD,iEAAwF;AAO3E,QAAA,eAAe,GAAG;IAC3B,QAAQ,EAAE,CAAC,mCAAkB,CAAC,QAAQ,CAAyB;IAC/D,QAAQ,EAAE;QACN,qCAAoB,CAAC,IAAI;QACzB,qCAAoB,CAAC,IAAI;QACzB,qCAAoB,CAAC,IAAI;KACF;CAC9B,CAAC;AAEF,IAAI,iBAAiB,GAAG,KAAK,CAAC;AAS9B,SAAgB,aAAa,CACzB,SAA2C,EAAE;IAE7C,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IAEjC,OAAO;QACH,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,KAAK;QAClC,OAAO,EAAE;YACL,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,QAAQ,IAAI,uBAAe,CAAC,QAAQ,CAAC;YACtE,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,QAAQ,IAAI,uBAAe,CAAC,QAAQ,CAAC;SACzE;QACD,OAAO,EAAE,EAAE,GAAG,MAAM,CAAC,OAAO,EAAE,GAAG,MAAM,CAAC,OAAO,EAAE;QACjD,OAAO,EAAE,EAAE,GAAG,MAAM,CAAC,OAAO,EAAE,GAAG,MAAM,CAAC,OAAO,EAAE;QACjD,aAAa,EAAE;YACX,QAAQ,EAAE,EAAE,GAAG,MAAM,CAAC,aAAa,CAAC,QAAQ,EAAE,GAAG,MAAM,CAAC,aAAa,EAAE,QAAQ,EAAE;YACjF,QAAQ,EAAE,EAAE,GAAG,MAAM,CAAC,aAAa,CAAC,QAAQ,EAAE,GAAG,MAAM,CAAC,aAAa,EAAE,QAAQ,EAAE;SACpF;KACJ,CAAC;AACN,CAAC;AAGD,SAAS,SAAS,CAAC,MAAwC;IACvD,MAAM,MAAM,GAAG,MAAM,CAAC,cAAc,CAAC;IACrC,IAAI,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC/B,iBAAiB,GAAG,IAAI,CAAC;QAEzB,OAAO,CAAC,IAAI,CACR,yEAAyE;YACrE,+EAA+E,CACtF,CAAC;IACN,CAAC;IACD,OAAO;QACH,OAAO,EAAE,MAAM,EAAE,OAAO,IAAI,EAAE;QAC9B,OAAO,EAAE,MAAM,EAAE,YAAY,IAAI,EAAE;QACnC,aAAa,EAAE;YACX,QAAQ,EAAE,MAAM,EAAE,QAAQ,IAAI,EAAE;YAChC,QAAQ,EAAE,MAAM,EAAE,QAAQ,IAAI,EAAE;SACnC;KACJ,CAAC;AACN,CAAC;AAED,SAAS,MAAM,CAAI,KAAU;IACzB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;AACtC,CAAC"}
|
package/dist/lib/constant.d.ts
CHANGED
package/dist/lib/constant.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.NEST_DYNAMIC_TEMPLATES_ASYNC_OPTIONS_PROVIDER = exports.NEST_DYNAMIC_TEMPLATES_MODULE_CONFIG = void 0;
|
|
3
|
+
exports.NEST_DYNAMIC_TEMPLATES_ASYNC_OPTIONS_PROVIDER = exports.NEST_DYNAMIC_TEMPLATES_MODULE_CONFIG = exports.NEST_DYNAMIC_TEMPLATES_OPTIONS = void 0;
|
|
4
|
+
exports.NEST_DYNAMIC_TEMPLATES_OPTIONS = Symbol('NEST_DYNAMIC_TEMPLATES_OPTIONS');
|
|
4
5
|
exports.NEST_DYNAMIC_TEMPLATES_MODULE_CONFIG = Symbol('NEST_DYNAMIC_TEMPLATES_MODULE_CONFIG');
|
|
5
6
|
exports.NEST_DYNAMIC_TEMPLATES_ASYNC_OPTIONS_PROVIDER = 'NEST_DYNAMIC_TEMPLATES_ASYNC_OPTIONS_PROVIDER';
|
|
6
7
|
//# sourceMappingURL=constant.js.map
|
package/dist/lib/constant.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constant.js","sourceRoot":"","sources":["../../src/lib/constant.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"constant.js","sourceRoot":"","sources":["../../src/lib/constant.ts"],"names":[],"mappings":";;;AACa,QAAA,8BAA8B,GAAG,MAAM,CAAC,gCAAgC,CAAC,CAAC;AAG1E,QAAA,oCAAoC,GAAG,MAAM,CAAC,sCAAsC,CAAC,CAAC;AAGtF,QAAA,6CAA6C,GAAG,+CAA+C,CAAC"}
|