@orkestrel/template 0.0.5 → 0.0.7
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/README.md +26 -4
- package/dist/src/core/index.cjs +186 -94
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +779 -603
- package/dist/src/core/index.d.ts +779 -603
- package/dist/src/core/index.js +186 -95
- package/dist/src/core/index.js.map +1 -1
- package/package.json +19 -16
package/dist/src/core/index.d.ts
CHANGED
|
@@ -1,665 +1,841 @@
|
|
|
1
|
-
import { ContractShape } from '@orkestrel/contract';
|
|
2
|
-
import { EmitterErrorHandler } from '@orkestrel/emitter';
|
|
3
|
-
import { EmitterHooks } from '@orkestrel/emitter';
|
|
4
|
-
import { EmitterInterface } from '@orkestrel/emitter';
|
|
5
|
-
import { FieldPath } from '@orkestrel/contract';
|
|
1
|
+
import type { ContractShape } from '@orkestrel/contract';
|
|
2
|
+
import type { EmitterErrorHandler } from '@orkestrel/emitter';
|
|
3
|
+
import type { EmitterHooks } from '@orkestrel/emitter';
|
|
4
|
+
import type { EmitterInterface } from '@orkestrel/emitter';
|
|
5
|
+
import type { FieldPath } from '@orkestrel/contract';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* Creates a working {@link TemplateInterface} from a {@link TemplateOptions}
|
|
9
|
+
* bag, backed by the `Template` class.
|
|
9
10
|
*
|
|
10
11
|
* @param options - The template's `name` / `content`, an optional `id`
|
|
11
12
|
* (defaults to a generated UUID), `placeholders`, catalog metadata, and
|
|
12
13
|
* `missing` / `locale` fill defaults
|
|
13
14
|
* @returns A working {@link TemplateInterface}
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* ```ts
|
|
17
|
-
* import { createTemplate } from '@src/core'
|
|
18
|
-
*
|
|
19
|
-
* const greeting = createTemplate({ name: 'greeting', content: 'Hi {{name}}' })
|
|
20
|
-
* greeting.fill({ name: 'Ada' }) // 'Hi Ada'
|
|
21
|
-
* ```
|
|
22
|
-
*/
|
|
23
|
-
export declare function createTemplate(options: TemplateOptions): TemplateInterface;
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Create a template registry.
|
|
27
|
-
*
|
|
28
|
-
* @param options - Optional initial `templates` seed collection and
|
|
29
|
-
* manager-wide `missing` / `locale` fill defaults, emitter `on` hooks, and
|
|
30
|
-
* an `error` handler
|
|
31
|
-
* @returns A working {@link TemplateManagerInterface}
|
|
32
|
-
*
|
|
33
|
-
* @example
|
|
34
|
-
* ```ts
|
|
35
|
-
* import { createTemplateManager } from '@src/core'
|
|
36
|
-
*
|
|
37
|
-
* const templates = createTemplateManager({
|
|
38
|
-
* templates: [{ id: 'greeting', name: 'greeting', content: 'Hi {{name}}' }],
|
|
39
|
-
* })
|
|
40
|
-
* templates.fill('greeting', { name: 'Ada' }) // 'Hi Ada'
|
|
41
|
-
* ```
|
|
42
|
-
*/
|
|
43
|
-
export declare function createTemplateManager(options?: TemplateManagerOptions): TemplateManagerInterface;
|
|
44
|
-
|
|
45
|
-
/** Default `locale` for `Template#fill` / `TemplateManager#fill` when unspecified. */
|
|
46
|
-
export declare const DEFAULT_LOCALE = "en-US";
|
|
47
|
-
|
|
48
|
-
/** Default `missing` policy for `Template#fill` / `TemplateManager#fill` when unspecified. */
|
|
49
|
-
export declare const DEFAULT_MISSING_POLICY: MissingPolicy;
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* The single-pass `{{name}}` substitution pattern shared by `Template#fill`
|
|
53
|
-
* and `Template#validate`.
|
|
54
|
-
*
|
|
55
|
-
* @remarks
|
|
56
|
-
* Global-flagged, two-alternative pattern: a match of the FIRST alternative
|
|
57
|
-
* (`\{{` — a literal backslash followed by `{{`) means "emit a literal
|
|
58
|
-
* `{{`" — the escape hatch for content that must show `{{` without
|
|
59
|
-
* triggering substitution. A match that instead populates capture group 1
|
|
60
|
-
* (`\{{([^{}]+?)\}\}`) means "substitute the named token" — group 1 is the
|
|
61
|
-
* RAW (untrimmed) token text between the braces; every call site trims it
|
|
62
|
-
* (`token.trim()`) before using it as a lookup name, so `'{{ name }}'` still
|
|
63
|
-
* resolves `'name'`. The pattern intentionally does NOT wrap the token in
|
|
64
|
-
* `\s*` — an unclosed `'{{' + ' '.repeat(n)` with no closing `}}` would
|
|
65
|
-
* otherwise force the regex engine into catastrophic backtracking over the
|
|
66
|
-
* whitespace run (O(n^2)); trimming after the match keeps the same
|
|
67
|
-
* whitespace tolerance without the backtracking hazard. Every call site
|
|
68
|
-
* builds a fresh `RegExp` from `.source` / `.flags` rather than sharing this
|
|
69
|
-
* instance's mutable `lastIndex` across scans.
|
|
70
|
-
*/
|
|
71
|
-
export declare const FILL_PATTERN: RegExp;
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Substitute every `{{name}}` token in `content` in a single pass.
|
|
75
|
-
*
|
|
76
|
-
* @remarks
|
|
77
|
-
* Uses a fresh `RegExp` clone of `FILL_PATTERN` per call (never sharing its
|
|
78
|
-
* `lastIndex`) and a single `String#replace` scan — substituted output is
|
|
79
|
-
* never re-scanned. For each token: the matching declared
|
|
80
|
-
* {@link TemplatePlaceholder} (exact `name`) supplies its `path` (falling
|
|
81
|
-
* back to the token split on `.`); ANY path segment in `UNSAFE_FIELD_SEGMENTS`
|
|
82
|
-
* makes the token unresolved without ever calling `resolveField` (a
|
|
83
|
-
* prototype-pollution guard). A resolved value formats via `formatValue`; an
|
|
84
|
-
* unresolved value falls back to the placeholder's `fallback` when declared;
|
|
85
|
-
* otherwise `options.missing` governs — `'literal'` re-emits the original
|
|
86
|
-
* `{{name}}` text, `'empty'` emits `''`, and `'error'` emits `''` for every
|
|
87
|
-
* token but collects EVERY unresolved required token (an undeclared token, or
|
|
88
|
-
* a declared token with `required !== false`) and throws one
|
|
89
|
-
* {@link TemplateError} coded `MISSING` listing them all, in first-appearance
|
|
90
|
-
* order, once the scan completes. An escaped `\{{` emits a literal `{{`.
|
|
91
|
-
*
|
|
92
|
-
* PARITY: called with no declared `placeholders` and `{ missing: 'empty' }`,
|
|
93
|
-
* this reproduces `interpolateMessage` (`@src/core` sibling
|
|
94
|
-
* `interpret`) vector-for-vector. KNOWN DIVERGENCE: `FILL_PATTERN`'s token
|
|
95
|
-
* class (`[^{}]`) excludes `{`, where `interpolateMessage`'s (`[^}]`) allows
|
|
96
|
-
* it — a token containing `{` therefore behaves differently here.
|
|
97
|
-
*
|
|
98
|
-
* @param content - The template content carrying `{{name}}` tokens
|
|
99
|
-
* @param values - The values tokens resolve against
|
|
100
|
-
* @param options - `missing` (default `'error'`), `locale` (default `'en-US'`), and the declared `placeholders` (default none) tokens resolve against
|
|
101
|
-
* @returns The substituted content
|
|
102
|
-
*
|
|
103
|
-
* @example
|
|
104
|
-
* ```ts
|
|
105
|
-
* import { fillTemplate } from '@src/core'
|
|
106
|
-
*
|
|
107
|
-
* fillTemplate('Hi {{name}}', { name: 'Ada' }) // 'Hi Ada'
|
|
108
|
-
* fillTemplate('Limit {{limit}}', { limit: 5010 }, { missing: 'empty' }) // 'Limit 5,010'
|
|
109
|
-
* ```
|
|
110
|
-
*/
|
|
111
|
-
export declare function fillTemplate(content: string, values?: TemplateFillValues, options?: TemplateFillOptions & {
|
|
112
|
-
readonly placeholders?: readonly TemplatePlaceholder[];
|
|
113
|
-
}): string;
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Format a resolved fill value for substitution into a template's `content`.
|
|
117
|
-
*
|
|
118
|
-
* @remarks
|
|
119
|
-
* A finite number renders with the given locale's thousand grouping (via
|
|
120
|
-
* `toLocaleString`); every other value — including `null` — String-coerces.
|
|
121
|
-
* `null` therefore renders as the literal string `'null'`, intentionally
|
|
122
|
-
* mirroring `interpolateMessage`'s coercion parity (see `fillTemplate`). An
|
|
123
|
-
* invalid BCP-47 `locale` tag throws a `RangeError` from the underlying
|
|
124
|
-
* `toLocaleString` call when `value` is a finite number — this is a caller
|
|
125
|
-
* error (an invalid locale argument), by design, and is not caught here.
|
|
126
|
-
*
|
|
127
|
-
* @param value - The resolved value to format
|
|
128
|
-
* @param locale - The locale used for finite-number formatting
|
|
129
|
-
* @returns The formatted string
|
|
130
|
-
*
|
|
131
|
-
* @example
|
|
132
|
-
* ```ts
|
|
133
|
-
* import { formatValue } from '@src/core'
|
|
134
|
-
*
|
|
135
|
-
* formatValue(5010, 'en-US') // '5,010'
|
|
136
|
-
* formatValue(null, 'en-US') // 'null'
|
|
137
|
-
* ```
|
|
138
|
-
*/
|
|
139
|
-
export declare function formatValue(value: unknown, locale: string): string;
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Narrow an unknown caught value to a {@link TemplateError}.
|
|
143
|
-
*
|
|
144
|
-
* @param value - The value to test (typically a `catch` binding)
|
|
145
|
-
* @returns `true` when `value` is a {@link TemplateError}
|
|
146
|
-
*
|
|
147
|
-
* @example
|
|
148
|
-
* ```ts
|
|
149
|
-
* import { isTemplateError } from '@src/core'
|
|
150
|
-
*
|
|
151
|
-
* try {
|
|
152
|
-
* manager.template('missing')
|
|
153
|
-
* } catch (error) {
|
|
154
|
-
* if (isTemplateError(error) && error.code === 'NOTFOUND') return
|
|
155
|
-
* }
|
|
156
|
-
* ```
|
|
157
|
-
*/
|
|
158
|
-
export declare function isTemplateError(value: unknown): value is TemplateError;
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* How {@link TemplateInterface#fill} handles an unresolved required
|
|
162
|
-
* placeholder.
|
|
163
|
-
*
|
|
164
|
-
* @remarks
|
|
165
|
-
* `error` — throws a {@link TemplateError} coded `MISSING`. `empty` —
|
|
166
|
-
* substitutes an empty string. `literal` — substitutes the placeholder's own
|
|
167
|
-
* `{{name}}` token back into the output, unchanged.
|
|
168
|
-
*/
|
|
169
|
-
export declare type MissingPolicy = 'error' | 'empty' | 'literal';
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Build the `@orkestrel/contract` object shape describing a template's
|
|
173
|
-
* declared placeholders.
|
|
174
|
-
*
|
|
175
|
-
* @remarks
|
|
176
|
-
* Each placeholder becomes a `stringShape` carrying its `description`;
|
|
177
|
-
* `required === false` wraps it in `optionalShape`. Used by `Template` to
|
|
178
|
-
* compile its `parameters()` contract once per instance.
|
|
179
|
-
*
|
|
180
|
-
* @param placeholders - The declared placeholders to shape
|
|
181
|
-
* @returns The contract shape for `createContract`
|
|
182
|
-
*
|
|
183
|
-
* @example
|
|
184
|
-
* ```ts
|
|
185
|
-
* import { placeholderShape } from '@src/core'
|
|
186
|
-
* import { createContract } from '@orkestrel/contract'
|
|
187
|
-
*
|
|
188
|
-
* const contract = createContract(placeholderShape([{ name: 'city' }]))
|
|
189
|
-
* ```
|
|
190
|
-
*/
|
|
191
|
-
export declare function placeholderShape(placeholders: readonly TemplatePlaceholder[]): ContractShape;
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Resolve a field path against a fill-values record, refusing any path that
|
|
195
|
-
* touches a prototype-pollution-unsafe segment.
|
|
196
|
-
*
|
|
197
|
-
* @remarks
|
|
198
|
-
* A prototype-pollution guard shared by `fillTemplate` and `Template#validate`
|
|
199
|
-
* so the two stay in lockstep: `path` normalizes to a segment array (a bare
|
|
200
|
-
* string `path` becomes a single-segment array); if ANY segment appears in
|
|
201
|
-
* `UNSAFE_FIELD_SEGMENTS` (`'__proto__'`, `'constructor'`, `'prototype'`), the
|
|
202
|
-
* lookup is refused and `undefined` is returned WITHOUT ever calling
|
|
203
|
-
* `resolveField` — a path like `['__proto__', 'polluted']` can never reach
|
|
204
|
-
* the record's actual prototype chain through this function. Every other
|
|
205
|
-
* path resolves through `@orkestrel/contract`'s `resolveField`.
|
|
206
|
-
*
|
|
207
|
-
* @param record - The fill-values record to resolve against
|
|
208
|
-
* @param path - The field path — a single segment or a segment array
|
|
209
|
-
* @returns The resolved value, or `undefined` when unresolved or the path is unsafe
|
|
210
|
-
*
|
|
211
|
-
* @example
|
|
212
|
-
* ```ts
|
|
213
|
-
* import { resolveSafeField } from '@src/core'
|
|
214
|
-
*
|
|
215
|
-
* resolveSafeField({ a: { b: 1 } }, ['a', 'b']) // 1
|
|
216
|
-
* resolveSafeField({}, ['__proto__', 'polluted']) // undefined
|
|
217
|
-
* ```
|
|
218
|
-
*/
|
|
219
|
-
export declare function resolveSafeField(record: TemplateFillValues, path: FieldPath): unknown;
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* A named, versionable template — `{{name}}` tokens in `content`, filled
|
|
223
|
-
* against a values record.
|
|
224
|
-
*
|
|
225
|
-
* @remarks
|
|
226
|
-
* `missing` / `locale` seed this instance's default {@link TemplateFillOptions},
|
|
227
|
-
* overridable per `fill` call. Its `parameters()` contract (built from
|
|
228
|
-
* `placeholders` via `placeholderShape`) compiles once, in the constructor.
|
|
229
|
-
*
|
|
230
|
-
* @example
|
|
231
|
-
* ```ts
|
|
232
|
-
* const greeting = new Template({ name: 'greeting', content: 'Hi {{name}}' })
|
|
233
|
-
* greeting.fill({ name: 'Ada' }) // 'Hi Ada'
|
|
234
|
-
* ```
|
|
235
|
-
*/
|
|
236
|
-
export declare class Template implements TemplateInterface {
|
|
237
|
-
#private;
|
|
238
|
-
readonly id: string;
|
|
239
|
-
readonly name: string;
|
|
240
|
-
readonly content: string;
|
|
241
|
-
readonly placeholders: readonly TemplatePlaceholder[];
|
|
242
|
-
readonly summary?: string;
|
|
243
|
-
readonly description?: string;
|
|
244
|
-
readonly category?: string;
|
|
245
|
-
readonly tags?: readonly string[];
|
|
246
|
-
constructor(options: TemplateOptions);
|
|
247
|
-
/**
|
|
248
|
-
* The plain, JSON-serializable data this template carries.
|
|
249
|
-
*
|
|
250
|
-
* @returns The {@link TemplateDefinition} record
|
|
251
|
-
*
|
|
252
|
-
* @example
|
|
253
|
-
* ```ts
|
|
254
|
-
* const instance = new Template({ name: 'greeting', content: 'Hi {{name}}' })
|
|
255
|
-
* instance.definition().name // 'greeting'
|
|
256
|
-
* ```
|
|
257
|
-
*/
|
|
258
|
-
definition(): TemplateDefinition;
|
|
259
|
-
/**
|
|
260
|
-
* Substitute every `{{name}}` token in `content` against `values`.
|
|
261
|
-
*
|
|
262
|
-
* @param values - The values tokens resolve against
|
|
263
|
-
* @param options - Per-call overrides for this instance's `missing` / `locale` defaults
|
|
264
|
-
* @returns The substituted content
|
|
265
|
-
*
|
|
266
|
-
* @example
|
|
267
|
-
* ```ts
|
|
268
|
-
* const instance = new Template({ name: 'greeting', content: 'Hi {{name}}' })
|
|
269
|
-
* instance.fill({ name: 'Ada' }) // 'Hi Ada'
|
|
270
|
-
* ```
|
|
271
|
-
*/
|
|
272
|
-
fill(values?: TemplateFillValues, options?: TemplateFillOptions): string;
|
|
273
|
-
/**
|
|
274
|
-
* Report which required placeholders would stay unresolved, and which
|
|
275
|
-
* `values` keys go unused, without producing output.
|
|
276
|
-
*
|
|
277
|
-
* @remarks
|
|
278
|
-
* Content-token driven: scans `this.content` for every `{{name}}` token
|
|
279
|
-
* (skipping escaped `\{{` matches) the same way `fill` does, so `validate`
|
|
280
|
-
* predicts `fill`'s `'error'`-{@link MissingPolicy} outcome exactly — a
|
|
281
|
-
* token reported here as missing is precisely a token that would throw
|
|
282
|
-
* under `fill(values, { missing: 'error' })`. For each distinct token
|
|
283
|
-
* (first-appearance order, trimmed): a declared {@link TemplatePlaceholder}
|
|
284
|
-
* sharing its `name` supplies `path` (falling back to the token split on
|
|
285
|
-
* `.`); the value resolves via `resolveSafeField`. The token is `missing`
|
|
286
|
-
* only when the value is unresolved AND no `fallback` is declared AND the
|
|
287
|
-
* placeholder is required (`required !== false`, including undeclared
|
|
288
|
-
* tokens). `extra` lists every `values` key with no declared placeholder.
|
|
15
|
+
* @throws {@link TemplateError} Thrown when `options.placeholders` declares a duplicate `name` or an empty `path` (coded `INVALID`)
|
|
289
16
|
*
|
|
290
|
-
* @
|
|
291
|
-
* @returns The {@link TemplateValidationResult}
|
|
292
|
-
*
|
|
293
|
-
* @example
|
|
17
|
+
* @example Create a template and a registry
|
|
294
18
|
* ```ts
|
|
295
|
-
*
|
|
296
|
-
* name: 'greeting',
|
|
297
|
-
* content: 'Hi {{name}}',
|
|
298
|
-
* placeholders: [{ name: 'name' }],
|
|
299
|
-
* })
|
|
300
|
-
* instance.validate({}).missing // ['name']
|
|
301
|
-
* ```
|
|
302
|
-
*/
|
|
303
|
-
validate(values?: TemplateFillValues): TemplateValidationResult;
|
|
304
|
-
/**
|
|
305
|
-
* Project this template's placeholders to the open tool-parameters record
|
|
306
|
-
* shape.
|
|
19
|
+
* import { createTemplate, createTemplateManager } from '@orkestrel/template'
|
|
307
20
|
*
|
|
308
|
-
*
|
|
21
|
+
* const greeting = createTemplate({ name: 'greeting', content: 'Hi {{name}}' })
|
|
22
|
+
* greeting.fill({ name: 'Ada' }) // 'Hi Ada'
|
|
309
23
|
*
|
|
310
|
-
*
|
|
311
|
-
*
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
*
|
|
315
|
-
*
|
|
24
|
+
* const templates = createTemplateManager({
|
|
25
|
+
* templates: [
|
|
26
|
+
* { id: 'greeting', name: 'greeting', content: 'Hi {{name}}', category: 'mail' },
|
|
27
|
+
* { id: 'farewell', name: 'farewell', content: 'Bye {{name}}', category: 'mail' },
|
|
28
|
+
* { id: 'alert', name: 'alert', content: 'Alert: {{reason}}', category: 'ops' },
|
|
29
|
+
* ],
|
|
316
30
|
* })
|
|
317
|
-
*
|
|
31
|
+
* templates.fill('greeting', { name: 'Ada' }) // 'Hi Ada'
|
|
32
|
+
* templates.find({ category: 'mail' }).map((one) => one.id) // ['greeting', 'farewell']
|
|
33
|
+
* templates.has('alert') // true
|
|
34
|
+
* templates.has('missing') // false
|
|
318
35
|
* ```
|
|
319
36
|
*/
|
|
320
|
-
|
|
321
|
-
}
|
|
37
|
+
export declare function createTemplate(options: TemplateOptions): TemplateInterface;
|
|
322
38
|
|
|
323
|
-
/**
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Creates a working {@link TemplateManagerInterface}, optionally seeded with
|
|
41
|
+
* the templates the options carry, backed by the `TemplateManager` class.
|
|
42
|
+
*
|
|
43
|
+
* @param options - Optional initial `templates` seed collection and
|
|
44
|
+
* manager-wide `missing` / `locale` fill defaults, emitter `on` hooks, and
|
|
45
|
+
* an `error` handler
|
|
46
|
+
* @returns A working {@link TemplateManagerInterface}
|
|
47
|
+
* @throws {@link TemplateError} Thrown when a seeded `options.templates` bag declares a duplicate placeholder `name` or an empty `path` (coded `INVALID`)
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* import { createTemplateManager } from '@src/core'
|
|
52
|
+
*
|
|
53
|
+
* const templates = createTemplateManager({
|
|
54
|
+
* templates: [{ id: 'greeting', name: 'greeting', content: 'Hi {{name}}' }],
|
|
55
|
+
* })
|
|
56
|
+
* templates.fill('greeting', { name: 'Ada' }) // 'Hi Ada'
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export declare function createTemplateManager(options?: TemplateManagerOptions): TemplateManagerInterface;
|
|
342
60
|
|
|
343
|
-
/**
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
* {@link MissingPolicy} (`MISSING`), an unknown template id
|
|
349
|
-
* (`NOTFOUND`), `createTemplate` handed invalid data (`INVALID`), and
|
|
350
|
-
* `TemplateManagerInterface#register` handed an id already present without
|
|
351
|
-
* `options.replace` (`CONFLICT`). `context`, when present, carries the
|
|
352
|
-
* offending id / name.
|
|
353
|
-
*/
|
|
354
|
-
export declare class TemplateError extends Error {
|
|
355
|
-
readonly code: TemplateErrorCode;
|
|
356
|
-
readonly context?: Readonly<Record<string, unknown>>;
|
|
357
|
-
constructor(code: TemplateErrorCode, message: string, context?: Readonly<Record<string, unknown>>);
|
|
358
|
-
}
|
|
61
|
+
/**
|
|
62
|
+
* Holds `'en-US'`, the default `locale` for `Template#fill` /
|
|
63
|
+
* `TemplateManager#fill` when unspecified.
|
|
64
|
+
*/
|
|
65
|
+
export declare const DEFAULT_LOCALE = "en-US";
|
|
359
66
|
|
|
360
|
-
/**
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
* {@link MissingPolicy}. `NOTFOUND` — `TemplateManagerInterface#template`
|
|
366
|
-
* (or `fill` / `validate` / `parameters` by id) was handed an unknown id.
|
|
367
|
-
* `INVALID` — `createTemplate` was handed data that fails validation.
|
|
368
|
-
* `CONFLICT` — `register` was handed an id already present without
|
|
369
|
-
* `options.replace`.
|
|
370
|
-
*/
|
|
371
|
-
export declare type TemplateErrorCode = 'MISSING' | 'NOTFOUND' | 'INVALID' | 'CONFLICT';
|
|
372
|
-
|
|
373
|
-
/** Per-call options for `TemplateInterface#fill` / `TemplateManagerInterface#fill`. */
|
|
374
|
-
export declare interface TemplateFillOptions {
|
|
375
|
-
readonly missing?: MissingPolicy;
|
|
376
|
-
readonly locale?: string;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
/** The values a {@link TemplateInterface#fill} / `#validate` call resolves placeholders against. */
|
|
380
|
-
export declare type TemplateFillValues = Readonly<Record<string, unknown>>;
|
|
67
|
+
/**
|
|
68
|
+
* Holds `'error'`, the default `missing` policy for `Template#fill` /
|
|
69
|
+
* `TemplateManager#fill` when unspecified.
|
|
70
|
+
*/
|
|
71
|
+
export declare const DEFAULT_MISSING_POLICY: MissingPolicy;
|
|
381
72
|
|
|
382
|
-
/**
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
readonly tags?: readonly string[];
|
|
404
|
-
definition(): TemplateDefinition;
|
|
405
|
-
fill(values?: TemplateFillValues, options?: TemplateFillOptions): string;
|
|
406
|
-
validate(values?: TemplateFillValues): TemplateValidationResult;
|
|
407
|
-
parameters(): Readonly<Record<string, unknown>> | undefined;
|
|
408
|
-
}
|
|
73
|
+
/**
|
|
74
|
+
* Holds the single-pass `{{name}}` substitution pattern shared by
|
|
75
|
+
* `Template#fill` and `Template#validate`.
|
|
76
|
+
*
|
|
77
|
+
* @remarks
|
|
78
|
+
* Global-flagged, two-alternative pattern: a match of the first alternative
|
|
79
|
+
* (`\{{` — a literal backslash followed by `{{`) means "emit a literal
|
|
80
|
+
* `{{`" — the escape hatch for content that must show `{{` without
|
|
81
|
+
* triggering substitution. A match that instead populates capture group 1
|
|
82
|
+
* (`\{{([^{}]+?)\}\}`) means "substitute the named token" — group 1 is the
|
|
83
|
+
* untrimmed token text between the braces; every call site trims it
|
|
84
|
+
* (`token.trim()`) before using it as a lookup name, so `'{{ name }}'` still
|
|
85
|
+
* resolves `'name'`. The pattern deliberately does not wrap the token in
|
|
86
|
+
* `\s*` — an unclosed `'{{' + ' '.repeat(n)` with no closing `}}` would
|
|
87
|
+
* otherwise force the regex engine into catastrophic backtracking over the
|
|
88
|
+
* whitespace run (O(n^2)); trimming after the match keeps the same
|
|
89
|
+
* whitespace tolerance without the backtracking hazard. Every call site
|
|
90
|
+
* builds a fresh `RegExp` from `.source` / `.flags` rather than sharing this
|
|
91
|
+
* instance's mutable `lastIndex` across scans.
|
|
92
|
+
*/
|
|
93
|
+
export declare const FILL_PATTERN: RegExp;
|
|
409
94
|
|
|
410
|
-
/**
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
95
|
+
/**
|
|
96
|
+
* Substitutes every `{{name}}` token in `content` in a single pass.
|
|
97
|
+
*
|
|
98
|
+
* @remarks
|
|
99
|
+
* Uses a fresh `RegExp` clone of `FILL_PATTERN` per call (never sharing its
|
|
100
|
+
* `lastIndex`) and a single `String#replace` scan — substituted output is
|
|
101
|
+
* never re-scanned. Each token resolves through `resolveToken`, the one rule
|
|
102
|
+
* `Template#validate` also applies: the matching declared
|
|
103
|
+
* {@link TemplatePlaceholder} (exact `name`) supplies its `path` (falling
|
|
104
|
+
* back to the token split on `.`); any path segment in `UNSAFE_FIELD_SEGMENTS`
|
|
105
|
+
* makes the token unresolved without ever calling `resolveField` (a
|
|
106
|
+
* prototype-pollution guard). A resolved value formats through `formatValue`; an
|
|
107
|
+
* unresolved value falls back to the placeholder's `fallback` when declared;
|
|
108
|
+
* otherwise `options.missing` governs — `'literal'` re-emits the original
|
|
109
|
+
* `{{name}}` text, `'empty'` emits `''`, and `'error'` emits `''` for every
|
|
110
|
+
* token but collects every unresolved required token (an undeclared token, or
|
|
111
|
+
* a declared token with `required !== false`) and throws one
|
|
112
|
+
* {@link TemplateError} coded `MISSING` listing them all, in first-appearance
|
|
113
|
+
* order, once the scan completes. An escaped `\{{` emits a literal `{{`.
|
|
114
|
+
*
|
|
115
|
+
* Called with no declared `placeholders` and `{ missing: 'empty' }`, this is a
|
|
116
|
+
* bare interpolation over `content` — every token resolves by dotted path
|
|
117
|
+
* against the values record and every unresolved token emits `''`.
|
|
118
|
+
* `FILL_PATTERN`'s token class (`[^{}]`) excludes `{`, so a token containing
|
|
119
|
+
* `{` never matches and the surrounding `{{` stays literal.
|
|
120
|
+
*
|
|
121
|
+
* @param content - The template content carrying `{{name}}` tokens
|
|
122
|
+
* @param values - The values tokens resolve against
|
|
123
|
+
* @param options - `missing` (default `'error'`), `locale` (default `'en-US'`), and the declared `placeholders` (default none) tokens resolve against
|
|
124
|
+
* @returns The substituted content
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* ```ts
|
|
128
|
+
* import { fillTemplate } from '@src/core'
|
|
129
|
+
*
|
|
130
|
+
* fillTemplate('Hi {{name}}', { name: 'Ada' }) // 'Hi Ada'
|
|
131
|
+
* fillTemplate('Limit {{limit}}', { limit: 5010 }, { missing: 'empty' }) // 'Limit 5,010'
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
export declare function fillTemplate(content: string, values?: TemplateFillValues, options?: TemplateFillContext): string;
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Formats a resolved fill value for substitution into a template's `content`.
|
|
138
|
+
*
|
|
139
|
+
* @remarks
|
|
140
|
+
* A finite number renders with the given locale's thousand grouping (through
|
|
141
|
+
* `toLocaleString`); every other value — including `null` — String-coerces.
|
|
142
|
+
* `null` therefore renders as the literal string `'null'`, matching
|
|
143
|
+
* `String(value)` exactly, so a resolved `null` is visible in the output
|
|
144
|
+
* rather than silently empty. An
|
|
145
|
+
* invalid BCP-47 `locale` tag throws a `RangeError` from the underlying
|
|
146
|
+
* `toLocaleString` call when `value` is a finite number — this is a caller
|
|
147
|
+
* error (an invalid locale argument), by design, and is not caught here.
|
|
148
|
+
*
|
|
149
|
+
* @param value - The resolved value to format
|
|
150
|
+
* @param locale - The locale used for finite-number formatting
|
|
151
|
+
* @returns The formatted string
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```ts
|
|
155
|
+
* import { formatValue } from '@src/core'
|
|
156
|
+
*
|
|
157
|
+
* formatValue(5010, 'en-US') // '5,010'
|
|
158
|
+
* formatValue(null, 'en-US') // 'null'
|
|
159
|
+
* ```
|
|
160
|
+
*/
|
|
161
|
+
export declare function formatValue(value: unknown, locale: string): string;
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Narrows an unknown caught value to a {@link TemplateError}.
|
|
165
|
+
*
|
|
166
|
+
* @param value - The value to test (typically a `catch` binding)
|
|
167
|
+
* @returns True if `value` is a {@link TemplateError}; false otherwise
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* ```ts
|
|
171
|
+
* import { isTemplateError } from '@src/core'
|
|
172
|
+
*
|
|
173
|
+
* try {
|
|
174
|
+
* manager.fill('missing')
|
|
175
|
+
* } catch (error) {
|
|
176
|
+
* if (isTemplateError(error) && error.code === 'NOTFOUND') return
|
|
177
|
+
* }
|
|
178
|
+
* ```
|
|
179
|
+
*/
|
|
180
|
+
export declare function isTemplateError(value: unknown): value is TemplateError;
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Names how {@link TemplateInterface#fill} handles an unresolved required
|
|
184
|
+
* placeholder.
|
|
185
|
+
*
|
|
186
|
+
* @remarks
|
|
187
|
+
* `error` — throws a {@link TemplateError} coded `MISSING`. `empty` —
|
|
188
|
+
* substitutes an empty string. `literal` — substitutes the placeholder's own
|
|
189
|
+
* `{{name}}` token back into the output, unchanged.
|
|
190
|
+
*/
|
|
191
|
+
export declare type MissingPolicy = 'error' | 'empty' | 'literal';
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Builds the `@orkestrel/contract` object shape describing a template's
|
|
195
|
+
* declared placeholders.
|
|
196
|
+
*
|
|
197
|
+
* @remarks
|
|
198
|
+
* Each placeholder becomes a `stringShape` carrying its `description`;
|
|
199
|
+
* `required === false` wraps it in `optionalShape`. Used by `Template` to
|
|
200
|
+
* compile its `parameters()` contract once per instance.
|
|
201
|
+
*
|
|
202
|
+
* @param placeholders - The declared placeholders to shape
|
|
203
|
+
* @returns The contract shape for `createContract`
|
|
204
|
+
*
|
|
205
|
+
* @example
|
|
206
|
+
* ```ts
|
|
207
|
+
* import { placeholderShape } from '@src/core'
|
|
208
|
+
* import { createContract } from '@orkestrel/contract'
|
|
209
|
+
*
|
|
210
|
+
* const contract = createContract(placeholderShape([{ name: 'city' }]))
|
|
211
|
+
* ```
|
|
212
|
+
*/
|
|
213
|
+
export declare function placeholderShape(placeholders: readonly TemplatePlaceholder[]): ContractShape;
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Resolves a field path against a fill-values record, refusing any path that
|
|
217
|
+
* touches a prototype-pollution-unsafe segment.
|
|
218
|
+
*
|
|
219
|
+
* @remarks
|
|
220
|
+
* A prototype-pollution guard shared by `fillTemplate` and `Template#validate`
|
|
221
|
+
* so the two stay in lockstep: `path` normalizes to a segment array (a bare
|
|
222
|
+
* string `path` becomes a single-segment array); if any segment appears in
|
|
223
|
+
* `UNSAFE_FIELD_SEGMENTS` (`'__proto__'`, `'constructor'`, `'prototype'`), the
|
|
224
|
+
* lookup is refused and `undefined` is returned without ever calling
|
|
225
|
+
* `resolveField` — a path like `['__proto__', 'polluted']` can never reach
|
|
226
|
+
* the record's actual prototype chain through this function. Every other
|
|
227
|
+
* path resolves through `@orkestrel/contract`'s `resolveField`.
|
|
228
|
+
*
|
|
229
|
+
* @param record - The fill-values record to resolve against
|
|
230
|
+
* @param path - The field path — a single segment or a segment array
|
|
231
|
+
* @returns The resolved value, or `undefined` when unresolved or the path is unsafe
|
|
232
|
+
*
|
|
233
|
+
* @example
|
|
234
|
+
* ```ts
|
|
235
|
+
* import { resolveSafeField } from '@src/core'
|
|
236
|
+
*
|
|
237
|
+
* resolveSafeField({ a: { b: 1 } }, ['a', 'b']) // 1
|
|
238
|
+
* resolveSafeField({}, ['__proto__', 'polluted']) // undefined
|
|
239
|
+
* ```
|
|
240
|
+
*/
|
|
241
|
+
export declare function resolveSafeField(record: TemplateFillValues, path: FieldPath): unknown;
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Resolves one `{{name}}` token against the declared placeholders and the
|
|
245
|
+
* fill-values record.
|
|
246
|
+
*
|
|
247
|
+
* @remarks
|
|
248
|
+
* The single implementation of the token rule `fillTemplate` and
|
|
249
|
+
* `Template#validate` both apply, so the two can never drift: the declared
|
|
250
|
+
* {@link TemplatePlaceholder} sharing the token's `name` (exact match)
|
|
251
|
+
* supplies its `path`, falling back to the token split on `.`; the value
|
|
252
|
+
* resolves through `resolveSafeField`, so any segment in
|
|
253
|
+
* `UNSAFE_FIELD_SEGMENTS` yields `undefined` without ever calling
|
|
254
|
+
* `resolveField`; `required` is `true` for an undeclared token and for a
|
|
255
|
+
* declared placeholder whose `required` is not `false`. The token is passed
|
|
256
|
+
* already trimmed. `fallback` is not applied here — it is read from
|
|
257
|
+
* `declared` by each caller, because `fill` substitutes it and `validate`
|
|
258
|
+
* only counts it.
|
|
259
|
+
*
|
|
260
|
+
* @param record - The fill-values record the token resolves against
|
|
261
|
+
* @param placeholders - The declared placeholders the token matches by name
|
|
262
|
+
* @param token - The trimmed token text, without its `{{` / `}}` delimiters
|
|
263
|
+
* @returns The {@link TemplateTokenResolution} for the token
|
|
264
|
+
*
|
|
265
|
+
* @example
|
|
266
|
+
* ```ts
|
|
267
|
+
* import { resolveToken } from '@src/core'
|
|
268
|
+
*
|
|
269
|
+
* resolveToken({ name: 'Ada' }, [], 'name').value // 'Ada'
|
|
270
|
+
* resolveToken({}, [{ name: 'nickname', required: false }], 'nickname').required // false
|
|
271
|
+
* ```
|
|
272
|
+
*/
|
|
273
|
+
export declare function resolveToken(record: TemplateFillValues, placeholders: readonly TemplatePlaceholder[], token: string): TemplateTokenResolution;
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Represents a named, versionable template — `{{name}}` tokens in `content`,
|
|
277
|
+
* filled against a values record — implementing `TemplateInterface` exactly.
|
|
278
|
+
*
|
|
279
|
+
* @remarks
|
|
280
|
+
* `missing` / `locale` seed this instance's default {@link TemplateFillOptions},
|
|
281
|
+
* overridable per `fill` call. Its `parameters()` contract (built from
|
|
282
|
+
* `placeholders` through `placeholderShape`) compiles once, in the constructor.
|
|
283
|
+
*
|
|
284
|
+
* @throws {@link TemplateError} Thrown when `options.placeholders` declares a duplicate `name` or an empty `path` (coded `INVALID`)
|
|
522
285
|
*
|
|
523
|
-
* @
|
|
524
|
-
*
|
|
525
|
-
*
|
|
526
|
-
*
|
|
527
|
-
|
|
528
|
-
|
|
286
|
+
* @example
|
|
287
|
+
* ```ts
|
|
288
|
+
* const greeting = new Template({ name: 'greeting', content: 'Hi {{name}}' })
|
|
289
|
+
* greeting.fill({ name: 'Ada' }) // 'Hi Ada'
|
|
290
|
+
* ```
|
|
291
|
+
*/
|
|
292
|
+
export declare class Template implements TemplateInterface {
|
|
293
|
+
#private;
|
|
294
|
+
readonly id: string;
|
|
295
|
+
readonly name: string;
|
|
296
|
+
readonly content: string;
|
|
297
|
+
readonly placeholders: readonly TemplatePlaceholder[];
|
|
298
|
+
readonly summary?: string;
|
|
299
|
+
readonly description?: string;
|
|
300
|
+
readonly category?: string;
|
|
301
|
+
readonly tags?: readonly string[];
|
|
302
|
+
constructor(options: TemplateOptions);
|
|
529
303
|
/**
|
|
530
|
-
*
|
|
304
|
+
* Returns the plain, JSON-serializable data this template carries.
|
|
305
|
+
*
|
|
306
|
+
* @returns The {@link TemplateDefinition} record
|
|
531
307
|
*
|
|
532
|
-
* @
|
|
533
|
-
*
|
|
534
|
-
*
|
|
308
|
+
* @example
|
|
309
|
+
* ```ts
|
|
310
|
+
* const instance = new Template({ name: 'greeting', content: 'Hi {{name}}' })
|
|
311
|
+
* instance.definition().name // 'greeting'
|
|
312
|
+
* ```
|
|
313
|
+
*/
|
|
314
|
+
definition(): TemplateDefinition;
|
|
315
|
+
/**
|
|
316
|
+
* Substitutes every `{{name}}` token in `content` against `values`.
|
|
317
|
+
*
|
|
318
|
+
* @param values - The values tokens resolve against
|
|
319
|
+
* @param options - Per-call overrides for this instance's `missing` / `locale` defaults
|
|
320
|
+
* @returns The substituted content
|
|
321
|
+
* @throws {@link TemplateError} Thrown when a required placeholder stays unresolved under the `'error'` policy (coded `MISSING`)
|
|
322
|
+
*
|
|
323
|
+
* @example
|
|
324
|
+
* ```ts
|
|
325
|
+
* const instance = new Template({ name: 'greeting', content: 'Hi {{name}}' })
|
|
326
|
+
* instance.fill({ name: 'Ada' }) // 'Hi Ada'
|
|
327
|
+
* ```
|
|
535
328
|
*/
|
|
536
|
-
|
|
329
|
+
fill(values?: TemplateFillValues, options?: TemplateFillOptions): string;
|
|
330
|
+
/**
|
|
331
|
+
* Reports which required placeholders would stay unresolved, and which
|
|
332
|
+
* `values` keys go unused, without producing output.
|
|
333
|
+
*
|
|
334
|
+
* @remarks
|
|
335
|
+
* Content-token driven: scans `this.content` for every `{{name}}` token
|
|
336
|
+
* (skipping escaped `\{{` matches) the same way `fill` does, so `validate`
|
|
337
|
+
* predicts `fill`'s `'error'`-{@link MissingPolicy} outcome exactly — a
|
|
338
|
+
* token reported here as missing is precisely a token that would throw
|
|
339
|
+
* under `fill(values, { missing: 'error' })`. For each distinct token
|
|
340
|
+
* (first-appearance order, trimmed): `resolveToken` applies the one shared
|
|
341
|
+
* token rule `fill` also applies — a declared {@link TemplatePlaceholder}
|
|
342
|
+
* sharing its `name` supplies `path` (falling back to the token split on
|
|
343
|
+
* `.`), and the value resolves through `resolveSafeField`. The token is `missing`
|
|
344
|
+
* only when the value is unresolved, no `fallback` is declared, and the
|
|
345
|
+
* placeholder is required (`required !== false`, including undeclared
|
|
346
|
+
* tokens). `extra` lists every `values` key with no declared placeholder.
|
|
347
|
+
*
|
|
348
|
+
* @param values - The values to check
|
|
349
|
+
* @returns The {@link TemplateValidationResult}
|
|
350
|
+
*
|
|
351
|
+
* @example
|
|
352
|
+
* ```ts
|
|
353
|
+
* const instance = new Template({
|
|
354
|
+
* name: 'greeting',
|
|
355
|
+
* content: 'Hi {{name}}',
|
|
356
|
+
* placeholders: [{ name: 'name' }],
|
|
357
|
+
* })
|
|
358
|
+
* instance.validate({}).missing // ['name']
|
|
359
|
+
* ```
|
|
360
|
+
*/
|
|
361
|
+
validate(values?: TemplateFillValues): TemplateValidationResult;
|
|
362
|
+
/**
|
|
363
|
+
* Projects this template's placeholders to the open tool-parameters record
|
|
364
|
+
* shape.
|
|
365
|
+
*
|
|
366
|
+
* @returns The compiled parameters record, or `undefined` when `schemaToParameters` yields none
|
|
367
|
+
*
|
|
368
|
+
* @example
|
|
369
|
+
* ```ts
|
|
370
|
+
* const instance = new Template({
|
|
371
|
+
* name: 'greeting',
|
|
372
|
+
* content: 'Hi {{name}}',
|
|
373
|
+
* placeholders: [{ name: 'name' }],
|
|
374
|
+
* })
|
|
375
|
+
* instance.parameters()
|
|
376
|
+
* ```
|
|
377
|
+
*/
|
|
378
|
+
parameters(): Readonly<Record<string, unknown>> | undefined;
|
|
537
379
|
}
|
|
538
380
|
|
|
539
381
|
/**
|
|
540
|
-
*
|
|
541
|
-
*
|
|
542
|
-
*
|
|
382
|
+
* Represents a named, versionable template record — pure data, no behavior.
|
|
383
|
+
*
|
|
384
|
+
* @remarks
|
|
385
|
+
* `content` is the raw string carrying `{{name}}` tokens (see
|
|
386
|
+
* `FILL_PATTERN` in `constants.ts`); `placeholders` declares every token's
|
|
387
|
+
* lookup rule. `category` and `tags` are the fields
|
|
388
|
+
* `TemplateManagerInterface#find` filters on, alongside `name`. `summary` and
|
|
389
|
+
* `description` are catalog metadata that `definition()` carries and no query
|
|
390
|
+
* reads.
|
|
543
391
|
*/
|
|
544
|
-
export declare
|
|
545
|
-
|
|
546
|
-
readonly
|
|
547
|
-
|
|
548
|
-
readonly
|
|
549
|
-
|
|
550
|
-
readonly
|
|
551
|
-
|
|
392
|
+
export declare interface TemplateDefinition {
|
|
393
|
+
readonly id: string;
|
|
394
|
+
readonly name: string;
|
|
395
|
+
readonly content: string;
|
|
396
|
+
readonly placeholders: readonly TemplatePlaceholder[];
|
|
397
|
+
readonly summary?: string;
|
|
398
|
+
readonly description?: string;
|
|
399
|
+
readonly category?: string;
|
|
400
|
+
readonly tags?: readonly string[];
|
|
401
|
+
}
|
|
552
402
|
|
|
553
403
|
/**
|
|
554
|
-
*
|
|
555
|
-
*
|
|
404
|
+
* Represents an error thrown by the template layer — a machine-readable
|
|
405
|
+
* {@link TemplateErrorCode} and an optional `context` record naming the
|
|
406
|
+
* offending id or placeholder name.
|
|
556
407
|
*
|
|
557
408
|
* @remarks
|
|
558
|
-
*
|
|
559
|
-
*
|
|
560
|
-
*
|
|
561
|
-
* `
|
|
562
|
-
*
|
|
563
|
-
* leaves the collection untouched and returns `false`.
|
|
409
|
+
* Thrown for: a required placeholder staying unresolved under the `error`
|
|
410
|
+
* {@link MissingPolicy} (`MISSING`), an unknown template id
|
|
411
|
+
* (`NOTFOUND`), `createTemplate` handed invalid data (`INVALID`), and
|
|
412
|
+
* `TemplateManagerInterface#register` handed an id already present without
|
|
413
|
+
* `options.replace` (`CONFLICT`).
|
|
564
414
|
*/
|
|
565
|
-
export declare
|
|
566
|
-
readonly
|
|
567
|
-
readonly
|
|
568
|
-
|
|
569
|
-
readonly replace?: boolean;
|
|
570
|
-
}): TemplateInterface;
|
|
571
|
-
template(id: string): TemplateInterface;
|
|
572
|
-
templates(): readonly TemplateInterface[];
|
|
573
|
-
find(query?: TemplateQuery): readonly TemplateInterface[];
|
|
574
|
-
has(id: string): boolean;
|
|
575
|
-
remove(ids: readonly string[]): boolean;
|
|
576
|
-
remove(id: string): boolean;
|
|
577
|
-
remove(): void;
|
|
578
|
-
clear(): void;
|
|
579
|
-
fill(id: string, values?: TemplateFillValues, options?: TemplateFillOptions): string;
|
|
580
|
-
validate(id: string, values?: TemplateFillValues): TemplateValidationResult;
|
|
581
|
-
parameters(id: string): Readonly<Record<string, unknown>> | undefined;
|
|
415
|
+
export declare class TemplateError extends Error {
|
|
416
|
+
readonly code: TemplateErrorCode;
|
|
417
|
+
readonly context?: Readonly<Record<string, unknown>>;
|
|
418
|
+
constructor(code: TemplateErrorCode, message: string, context?: Readonly<Record<string, unknown>>);
|
|
582
419
|
}
|
|
583
420
|
|
|
584
421
|
/**
|
|
585
|
-
*
|
|
422
|
+
* Names the coded misuse / failure conditions thrown as a {@link TemplateError}.
|
|
423
|
+
*
|
|
424
|
+
* @remarks
|
|
425
|
+
* `MISSING` — a required placeholder stayed unresolved under the `error`
|
|
426
|
+
* {@link MissingPolicy}. `NOTFOUND` — `TemplateManagerInterface#fill`,
|
|
427
|
+
* `#validate`, or `#parameters` was handed an unknown id.
|
|
428
|
+
* `INVALID` — `createTemplate` was handed data that fails validation.
|
|
429
|
+
* `CONFLICT` — `register` was handed an id already present without
|
|
430
|
+
* `options.replace`.
|
|
431
|
+
*/
|
|
432
|
+
export declare type TemplateErrorCode = 'MISSING' | 'NOTFOUND' | 'INVALID' | 'CONFLICT';
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Carries the full option bag `fillTemplate` takes — the per-call
|
|
436
|
+
* {@link TemplateFillOptions} plus the declared placeholders tokens resolve
|
|
437
|
+
* against.
|
|
586
438
|
*
|
|
587
439
|
* @remarks
|
|
588
|
-
* `
|
|
589
|
-
*
|
|
590
|
-
*
|
|
591
|
-
* `on` — initial event listeners (AGENTS §8/§13). `error` — the emitter's
|
|
592
|
-
* listener-error handler.
|
|
440
|
+
* `Template#fill` supplies `placeholders` from its own declaration; a direct
|
|
441
|
+
* `fillTemplate` caller supplies them per call, and omitting them fills
|
|
442
|
+
* against undeclared tokens alone.
|
|
593
443
|
*/
|
|
594
|
-
export declare interface
|
|
595
|
-
readonly
|
|
444
|
+
export declare interface TemplateFillContext extends TemplateFillOptions {
|
|
445
|
+
readonly placeholders?: readonly TemplatePlaceholder[];
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/** Carries the per-call options for `TemplateInterface#fill` / `TemplateManagerInterface#fill`. */
|
|
449
|
+
export declare interface TemplateFillOptions {
|
|
596
450
|
readonly missing?: MissingPolicy;
|
|
597
451
|
readonly locale?: string;
|
|
598
|
-
readonly on?: EmitterHooks<TemplateManagerEventMap>;
|
|
599
|
-
readonly error?: EmitterErrorHandler;
|
|
600
452
|
}
|
|
601
453
|
|
|
454
|
+
/** Represents the values a {@link TemplateInterface#fill} / `#validate` call resolves placeholders against. */
|
|
455
|
+
export declare type TemplateFillValues = Readonly<Record<string, unknown>>;
|
|
456
|
+
|
|
602
457
|
/**
|
|
603
|
-
*
|
|
458
|
+
* Declares the template contract a consumer holds — the readonly template
|
|
459
|
+
* record and the `definition`, `fill`, `validate`, and `parameters` calls
|
|
460
|
+
* over it.
|
|
604
461
|
*
|
|
605
462
|
* @remarks
|
|
606
|
-
* `
|
|
607
|
-
*
|
|
608
|
-
* {@link TemplateFillOptions}, overridable per-call.
|
|
463
|
+
* `Template` implements this contract exactly, so the class exposes these
|
|
464
|
+
* members and no other public behavior.
|
|
609
465
|
*/
|
|
610
|
-
export declare interface
|
|
611
|
-
readonly id
|
|
466
|
+
export declare interface TemplateInterface {
|
|
467
|
+
readonly id: string;
|
|
612
468
|
readonly name: string;
|
|
613
469
|
readonly content: string;
|
|
614
|
-
readonly placeholders
|
|
470
|
+
readonly placeholders: readonly TemplatePlaceholder[];
|
|
615
471
|
readonly summary?: string;
|
|
616
472
|
readonly description?: string;
|
|
617
473
|
readonly category?: string;
|
|
618
474
|
readonly tags?: readonly string[];
|
|
619
|
-
|
|
620
|
-
|
|
475
|
+
/** Returns the plain, JSON-serializable template data. */
|
|
476
|
+
definition(): TemplateDefinition;
|
|
477
|
+
/**
|
|
478
|
+
* Substitutes every `{{name}}` token in `content` against `values`.
|
|
479
|
+
*
|
|
480
|
+
* @remarks
|
|
481
|
+
* `options.missing` governs an unresolved required placeholder, defaulting
|
|
482
|
+
* to the instance's own {@link MissingPolicy}.
|
|
483
|
+
*/
|
|
484
|
+
fill(values?: TemplateFillValues, options?: TemplateFillOptions): string;
|
|
485
|
+
/**
|
|
486
|
+
* Reports which required placeholders would stay unresolved, and which
|
|
487
|
+
* `values` keys go unused.
|
|
488
|
+
*
|
|
489
|
+
* @remarks
|
|
490
|
+
* The unresolved names arrive as `missing` and the unused keys as `extra`,
|
|
491
|
+
* and neither reading produces output.
|
|
492
|
+
*/
|
|
493
|
+
validate(values?: TemplateFillValues): TemplateValidationResult;
|
|
494
|
+
/**
|
|
495
|
+
* Projects the declared placeholders to the open tool-parameters record
|
|
496
|
+
* shape.
|
|
497
|
+
*
|
|
498
|
+
* @remarks
|
|
499
|
+
* The shape is `schemaToParameters`'s return type from
|
|
500
|
+
* `@orkestrel/contract`.
|
|
501
|
+
*/
|
|
502
|
+
parameters(): Readonly<Record<string, unknown>> | undefined;
|
|
621
503
|
}
|
|
622
504
|
|
|
623
505
|
/**
|
|
624
|
-
*
|
|
625
|
-
*
|
|
626
|
-
*
|
|
506
|
+
* Represents the template registry — a self-owning, id-keyed record-holder for
|
|
507
|
+
* the {@link TemplateInterface} instances a consumer registers, looks up,
|
|
508
|
+
* fills, and validates by id — implementing `TemplateManagerInterface`
|
|
509
|
+
* exactly.
|
|
627
510
|
*
|
|
628
511
|
* @remarks
|
|
629
|
-
*
|
|
630
|
-
*
|
|
631
|
-
*
|
|
632
|
-
*
|
|
633
|
-
*
|
|
634
|
-
*
|
|
635
|
-
* `
|
|
512
|
+
* Singular and plural accessors, the batch `remove` overloads, and ownership
|
|
513
|
+
* of the emitter all sit here. `register` accepts either a constructed {@link TemplateInterface} (kept
|
|
514
|
+
* as-is, including its own `missing` / `locale` defaults) or a plain
|
|
515
|
+
* {@link TemplateOptions} bag — constructed into a `Template` with this
|
|
516
|
+
* manager's `missing` / `locale` defaults applied wherever the bag omits
|
|
517
|
+
* them. A duplicate `id` throws a {@link TemplateError} coded `CONFLICT`
|
|
518
|
+
* unless `options.replace` is `true`, in which case the existing entry is
|
|
519
|
+
* overwritten. `options.templates` seeds the registry at construction without
|
|
520
|
+
* emitting `register` — only calls to `register` after construction emit.
|
|
521
|
+
* The batch `remove(ids)` form removes every present id and returns
|
|
522
|
+
* `true` only when every listed id was present.
|
|
523
|
+
*
|
|
524
|
+
* @example
|
|
525
|
+
* ```ts
|
|
526
|
+
* import { TemplateManager } from '@src/core'
|
|
527
|
+
*
|
|
528
|
+
* const manager = new TemplateManager()
|
|
529
|
+
* const instance = manager.register({ name: 'greeting', content: 'Hi {{name}}' })
|
|
530
|
+
* manager.fill(instance.id, { name: 'Ada' }) // 'Hi Ada'
|
|
531
|
+
* ```
|
|
636
532
|
*/
|
|
637
|
-
export declare
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
533
|
+
export declare class TemplateManager implements TemplateManagerInterface {
|
|
534
|
+
#private;
|
|
535
|
+
constructor(options?: TemplateManagerOptions);
|
|
536
|
+
get emitter(): EmitterInterface<TemplateManagerEventMap>;
|
|
537
|
+
get count(): number;
|
|
538
|
+
/**
|
|
539
|
+
* Registers a template — a constructed {@link TemplateInterface} (kept
|
|
540
|
+
* as-is) or a plain {@link TemplateOptions} bag (constructed into a
|
|
541
|
+
* `Template` with this manager's `missing` / `locale` defaults applied
|
|
542
|
+
* wherever the bag omits them).
|
|
543
|
+
*
|
|
544
|
+
* @param template - The template instance or options to register
|
|
545
|
+
* @param options - `replace` — overwrite an existing entry sharing the same id instead of throwing
|
|
546
|
+
* @returns The registered {@link TemplateInterface}
|
|
547
|
+
* @throws {@link TemplateError} Thrown when the id is already registered and `options.replace` is not `true` (coded `CONFLICT`), or when an options bag declares a duplicate placeholder `name` or an empty `path` (coded `INVALID`)
|
|
548
|
+
*
|
|
549
|
+
* @example
|
|
550
|
+
* ```ts
|
|
551
|
+
* const instance = manager.register({ id: 'greeting', name: 'greeting', content: 'Hi {{name}}' })
|
|
552
|
+
* ```
|
|
553
|
+
*/
|
|
554
|
+
register(template: TemplateInterface | TemplateOptions, options?: TemplateRegisterOptions): TemplateInterface;
|
|
555
|
+
/**
|
|
556
|
+
* Returns one registered {@link TemplateInterface} by id.
|
|
557
|
+
*
|
|
558
|
+
* @param id - The template id
|
|
559
|
+
* @returns The registered {@link TemplateInterface}, or `undefined` when `id` is unregistered
|
|
560
|
+
*/
|
|
561
|
+
template(id: string): TemplateInterface | undefined;
|
|
562
|
+
/**
|
|
563
|
+
* Lists every registered template.
|
|
564
|
+
*
|
|
565
|
+
* @returns A snapshot array of every registered {@link TemplateInterface}
|
|
566
|
+
*/
|
|
567
|
+
templates(): readonly TemplateInterface[];
|
|
568
|
+
/**
|
|
569
|
+
* Filters registered templates by `name`, `category`, and `tag` — every
|
|
570
|
+
* supplied field must match.
|
|
571
|
+
*
|
|
572
|
+
* @param query - The {@link TemplateQuery} to filter by; omit for every registered template
|
|
573
|
+
* @returns The matching templates
|
|
574
|
+
*/
|
|
575
|
+
find(query?: TemplateQuery): readonly TemplateInterface[];
|
|
576
|
+
/**
|
|
577
|
+
* Tests whether a template id is registered.
|
|
578
|
+
*
|
|
579
|
+
* @param id - The template id
|
|
580
|
+
* @returns True if `id` is registered; false otherwise
|
|
581
|
+
*/
|
|
582
|
+
has(id: string): boolean;
|
|
583
|
+
/**
|
|
584
|
+
* Removes one, several, or every registered template.
|
|
585
|
+
*
|
|
586
|
+
* @remarks
|
|
587
|
+
* `remove()` removes every registered template, emitting `remove` once per
|
|
588
|
+
* instance. `remove(id)` removes one template by id, emitting `remove` and
|
|
589
|
+
* returning `true` when it existed, `false` otherwise. `remove(ids)`
|
|
590
|
+
* removes every listed id that is present, emitting `remove` once per
|
|
591
|
+
* removed instance, and returns `true` only when every listed id was
|
|
592
|
+
* present.
|
|
593
|
+
*
|
|
594
|
+
* @param target - Omit to remove all, a single id, or a list of ids
|
|
595
|
+
* @returns `boolean` for the single-id / list-of-ids forms; `void` for the remove-all form
|
|
596
|
+
*/
|
|
597
|
+
remove(ids: readonly string[]): boolean;
|
|
598
|
+
remove(id: string): boolean;
|
|
599
|
+
remove(): void;
|
|
600
|
+
/** Removes every registered template, emitting `clear`. */
|
|
601
|
+
clear(): void;
|
|
602
|
+
/**
|
|
603
|
+
* Tears down the registry: drops every registered template and destroys the
|
|
604
|
+
* owned emitter. Idempotent.
|
|
605
|
+
*
|
|
606
|
+
* @remarks
|
|
607
|
+
* Teardown is not an observable registry operation and the emitter is being
|
|
608
|
+
* released, so this emits neither `clear` nor `remove`. The emitter is torn
|
|
609
|
+
* down last, after the registry is dropped.
|
|
610
|
+
*
|
|
611
|
+
* @example
|
|
612
|
+
* ```ts
|
|
613
|
+
* const manager = new TemplateManager()
|
|
614
|
+
* manager.destroy()
|
|
615
|
+
* manager.emitter.destroyed // true
|
|
616
|
+
* ```
|
|
617
|
+
*/
|
|
618
|
+
destroy(): void;
|
|
619
|
+
/**
|
|
620
|
+
* Fills a registered template by id.
|
|
621
|
+
*
|
|
622
|
+
* @param id - The template id
|
|
623
|
+
* @param values - The values tokens resolve against
|
|
624
|
+
* @param options - Per-call overrides for the template's `missing` / `locale` defaults
|
|
625
|
+
* @returns The substituted content
|
|
626
|
+
* @throws {@link TemplateError} coded `NOTFOUND` when `id` is unknown
|
|
627
|
+
* @throws {@link TemplateError} Thrown when a required placeholder stays unresolved under the `'error'` policy (coded `MISSING`)
|
|
628
|
+
*/
|
|
629
|
+
fill(id: string, values?: TemplateFillValues, options?: TemplateFillOptions): string;
|
|
630
|
+
/**
|
|
631
|
+
* Validates values against a registered template by id.
|
|
632
|
+
*
|
|
633
|
+
* @param id - The template id
|
|
634
|
+
* @param values - The values to check
|
|
635
|
+
* @returns The {@link TemplateValidationResult}
|
|
636
|
+
* @throws {@link TemplateError} coded `NOTFOUND` when `id` is unknown
|
|
637
|
+
*/
|
|
638
|
+
validate(id: string, values?: TemplateFillValues): TemplateValidationResult;
|
|
639
|
+
/**
|
|
640
|
+
* Projects a registered template's parameters by id.
|
|
641
|
+
*
|
|
642
|
+
* @param id - The template id
|
|
643
|
+
* @returns The compiled parameters record, or `undefined` when the template has none
|
|
644
|
+
* @throws {@link TemplateError} coded `NOTFOUND` when `id` is unknown
|
|
645
|
+
*/
|
|
646
|
+
parameters(id: string): Readonly<Record<string, unknown>> | undefined;
|
|
647
|
+
}
|
|
644
648
|
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
649
|
+
/**
|
|
650
|
+
* Declares the push observation surface of a {@link TemplateManagerInterface}
|
|
651
|
+
* — an id-keyed registry, so `register` / `remove` are the events (never
|
|
652
|
+
* ordered-list `append`/`prepend`).
|
|
653
|
+
*/
|
|
654
|
+
export declare type TemplateManagerEventMap = {
|
|
655
|
+
/** Fires when a template is registered — carries the registered template. */
|
|
656
|
+
readonly register: readonly [template: TemplateInterface];
|
|
657
|
+
/** Fires when a template is removed — carries the removed template. */
|
|
658
|
+
readonly remove: readonly [template: TemplateInterface];
|
|
659
|
+
/** Fires when the registry is cleared. */
|
|
660
|
+
readonly clear: readonly [];
|
|
661
|
+
};
|
|
651
662
|
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
663
|
+
/**
|
|
664
|
+
* Declares the registry contract a consumer holds — a self-owning, id-keyed
|
|
665
|
+
* record-holder with singular and plural accessors over the templates it
|
|
666
|
+
* registers.
|
|
667
|
+
*
|
|
668
|
+
* @remarks
|
|
669
|
+
* `TemplateManager` implements this contract exactly, so the class exposes
|
|
670
|
+
* these members and no other public behavior. The by-id calls split on what
|
|
671
|
+
* an unknown id means to each: `template` reports its absence as `undefined`,
|
|
672
|
+
* while `fill`, `validate`, and `parameters` throw a {@link TemplateError}
|
|
673
|
+
* coded `NOTFOUND`, because each needs a template to proceed.
|
|
674
|
+
*/
|
|
675
|
+
export declare interface TemplateManagerInterface {
|
|
676
|
+
readonly emitter: EmitterInterface<TemplateManagerEventMap>;
|
|
677
|
+
readonly count: number;
|
|
678
|
+
/**
|
|
679
|
+
* Registers one template, or overwrites the entry sharing its id when
|
|
680
|
+
* `options.replace` is `true`, and emits `register`.
|
|
681
|
+
*
|
|
682
|
+
* @remarks
|
|
683
|
+
* The template arrives as a constructed {@link TemplateInterface} or as a
|
|
684
|
+
* plain {@link TemplateOptions} bag, which is constructed internally. A
|
|
685
|
+
* duplicate id without `options.replace` throws a {@link TemplateError}
|
|
686
|
+
* coded `CONFLICT`.
|
|
687
|
+
*/
|
|
688
|
+
register(template: TemplateInterface | TemplateOptions, options?: TemplateRegisterOptions): TemplateInterface;
|
|
689
|
+
/** Returns one registered template by id, or `undefined` when the id is unregistered. */
|
|
690
|
+
template(id: string): TemplateInterface | undefined;
|
|
691
|
+
/** Lists every registered template. */
|
|
692
|
+
templates(): readonly TemplateInterface[];
|
|
693
|
+
/** Filters registered templates by `name`, `category`, and `tag` — every supplied field must match. */
|
|
694
|
+
find(query?: TemplateQuery): readonly TemplateInterface[];
|
|
695
|
+
/** Reports whether a template with the given id is registered. */
|
|
696
|
+
has(id: string): boolean;
|
|
697
|
+
/**
|
|
698
|
+
* Removes the listed templates by id, one template by id, or every
|
|
699
|
+
* registered template, emitting `remove` once per removed template.
|
|
700
|
+
*
|
|
701
|
+
* @remarks
|
|
702
|
+
* The batch form removes every present id and reports `true` only when
|
|
703
|
+
* every listed id was present, so one absent id turns the answer `false`
|
|
704
|
+
* while the present ids still remove. The no-argument form removes every
|
|
705
|
+
* registered template and reports nothing.
|
|
706
|
+
*/
|
|
707
|
+
remove(ids: readonly string[]): boolean;
|
|
708
|
+
remove(id: string): boolean;
|
|
709
|
+
remove(): void;
|
|
710
|
+
/** Removes every registered template, emitting `clear`. */
|
|
711
|
+
clear(): void;
|
|
712
|
+
/**
|
|
713
|
+
* Tears the registry down: drops every registered template and destroys the
|
|
714
|
+
* owned emitter. Idempotent.
|
|
715
|
+
*
|
|
716
|
+
* @remarks
|
|
717
|
+
* Teardown emits nothing, because the emitter is being released.
|
|
718
|
+
*/
|
|
719
|
+
destroy(): void;
|
|
720
|
+
/** Fills a registered template by id. */
|
|
721
|
+
fill(id: string, values?: TemplateFillValues, options?: TemplateFillOptions): string;
|
|
722
|
+
/** Validates values against a registered template by id. */
|
|
723
|
+
validate(id: string, values?: TemplateFillValues): TemplateValidationResult;
|
|
724
|
+
/** Projects a registered template's parameters by id. */
|
|
725
|
+
parameters(id: string): Readonly<Record<string, unknown>> | undefined;
|
|
726
|
+
}
|
|
658
727
|
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
728
|
+
/**
|
|
729
|
+
* Carries the options for `createTemplateManager` / the `TemplateManager`
|
|
730
|
+
* constructor.
|
|
731
|
+
*
|
|
732
|
+
* @remarks
|
|
733
|
+
* `templates` seeds the registry — either constructed {@link TemplateInterface}
|
|
734
|
+
* instances or plain {@link TemplateOptions} bags. `missing` / `locale` are
|
|
735
|
+
* the manager-wide default {@link TemplateFillOptions}, overridable per-call.
|
|
736
|
+
* `on` — initial event listeners. `error` — the emitter's
|
|
737
|
+
* listener-error handler.
|
|
738
|
+
*/
|
|
739
|
+
export declare interface TemplateManagerOptions {
|
|
740
|
+
readonly templates?: ReadonlyArray<TemplateInterface | TemplateOptions>;
|
|
741
|
+
readonly missing?: MissingPolicy;
|
|
742
|
+
readonly locale?: string;
|
|
743
|
+
readonly on?: EmitterHooks<TemplateManagerEventMap>;
|
|
744
|
+
readonly error?: EmitterErrorHandler;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
/**
|
|
748
|
+
* Carries the options for `createTemplate` / the `Template` constructor.
|
|
749
|
+
*
|
|
750
|
+
* @remarks
|
|
751
|
+
* `id` defaults to a generated id when omitted. `placeholders` defaults to
|
|
752
|
+
* an empty list. `missing` / `locale` seed the instance's default
|
|
753
|
+
* {@link TemplateFillOptions}, overridable per-call.
|
|
754
|
+
*/
|
|
755
|
+
export declare interface TemplateOptions {
|
|
756
|
+
readonly id?: string;
|
|
757
|
+
readonly name: string;
|
|
758
|
+
readonly content: string;
|
|
759
|
+
readonly placeholders?: readonly TemplatePlaceholder[];
|
|
760
|
+
readonly summary?: string;
|
|
761
|
+
readonly description?: string;
|
|
762
|
+
readonly category?: string;
|
|
763
|
+
readonly tags?: readonly string[];
|
|
764
|
+
readonly missing?: MissingPolicy;
|
|
765
|
+
readonly locale?: string;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
/**
|
|
769
|
+
* Represents one placeholder a {@link TemplateDefinition}'s `content`
|
|
770
|
+
* declares — its lookup name, an optional field path into the values record,
|
|
771
|
+
* whether it is required, and a literal fallback.
|
|
772
|
+
*
|
|
773
|
+
* @remarks
|
|
774
|
+
* `name` is the `{{name}}` token as written in `content`. `path`, when
|
|
775
|
+
* present, resolves the fill value through a (possibly nested)
|
|
776
|
+
* {@link FieldPath} rather than a flat `name` lookup on the values record.
|
|
777
|
+
* `required` defaults to `true` when omitted — an unresolved required
|
|
778
|
+
* placeholder is governed by the active {@link MissingPolicy}. `fallback` is
|
|
779
|
+
* a literal substituted when the value is unresolved, regardless of
|
|
780
|
+
* `required`.
|
|
781
|
+
*/
|
|
782
|
+
export declare interface TemplatePlaceholder {
|
|
783
|
+
readonly name: string;
|
|
784
|
+
readonly path?: FieldPath;
|
|
785
|
+
readonly required?: boolean;
|
|
786
|
+
readonly fallback?: unknown;
|
|
787
|
+
readonly description?: string;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
/** Represents a query for `TemplateManagerInterface#find` — every supplied field must match. */
|
|
791
|
+
export declare interface TemplateQuery {
|
|
792
|
+
readonly name?: string;
|
|
793
|
+
readonly category?: string;
|
|
794
|
+
readonly tag?: string;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
/**
|
|
798
|
+
* Carries the options for `TemplateManagerInterface#register`.
|
|
799
|
+
*
|
|
800
|
+
* @remarks
|
|
801
|
+
* `replace` overwrites an existing entry sharing the registered id instead of
|
|
802
|
+
* throwing a {@link TemplateError} coded `CONFLICT`.
|
|
803
|
+
*/
|
|
804
|
+
export declare interface TemplateRegisterOptions {
|
|
805
|
+
readonly replace?: boolean;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
/**
|
|
809
|
+
* Represents one `{{name}}` token's resolution — the single token rule
|
|
810
|
+
* `fillTemplate` and `TemplateInterface#validate` share.
|
|
811
|
+
*
|
|
812
|
+
* @remarks
|
|
813
|
+
* `value` is the resolved fill value, `undefined` when the path is
|
|
814
|
+
* unresolved or refused by the prototype-pollution guard. `declared` is the
|
|
815
|
+
* matching {@link TemplatePlaceholder}, `undefined` for an undeclared token.
|
|
816
|
+
* `required` is `true` for an undeclared token and for a declared
|
|
817
|
+
* placeholder whose `required` is not `false`. A declared `fallback` is left
|
|
818
|
+
* on `declared` rather than applied here, because `fill` substitutes it and
|
|
819
|
+
* `validate` only counts it.
|
|
820
|
+
*/
|
|
821
|
+
export declare interface TemplateTokenResolution {
|
|
822
|
+
readonly value: unknown;
|
|
823
|
+
readonly declared: TemplatePlaceholder | undefined;
|
|
824
|
+
readonly required: boolean;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
/** Reports the outcome of `TemplateInterface#validate` — which required placeholders are unresolved, and which supplied values are unused. */
|
|
828
|
+
export declare interface TemplateValidationResult {
|
|
829
|
+
readonly valid: boolean;
|
|
830
|
+
readonly missing: readonly string[];
|
|
831
|
+
readonly extra: readonly string[];
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
/**
|
|
835
|
+
* Lists the prototype-pollution-unsafe field-path segments `'__proto__'`,
|
|
836
|
+
* `'constructor'`, and `'prototype'` — a fill lookup refuses to resolve a path
|
|
837
|
+
* containing one of them, treating the placeholder as unresolved.
|
|
838
|
+
*/
|
|
839
|
+
export declare const UNSAFE_FIELD_SEGMENTS: readonly string[];
|
|
664
840
|
|
|
665
|
-
|
|
841
|
+
export { }
|