@orkestrel/template 0.0.1

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.
@@ -0,0 +1,665 @@
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';
6
+
7
+ /**
8
+ * Create a template.
9
+ *
10
+ * @param options - The template's `name` / `content`, an optional `id`
11
+ * (defaults to a generated UUID), `placeholders`, catalog metadata, and
12
+ * `missing` / `locale` fill defaults
13
+ * @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.
289
+ *
290
+ * @param values - The values to check
291
+ * @returns The {@link TemplateValidationResult}
292
+ *
293
+ * @example
294
+ * ```ts
295
+ * const instance = new Template({
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.
307
+ *
308
+ * @returns The compiled parameters record, or `undefined` when `schemaToParameters` yields none
309
+ *
310
+ * @example
311
+ * ```ts
312
+ * const instance = new Template({
313
+ * name: 'greeting',
314
+ * content: 'Hi {{name}}',
315
+ * placeholders: [{ name: 'name' }],
316
+ * })
317
+ * instance.parameters()
318
+ * ```
319
+ */
320
+ parameters(): Readonly<Record<string, unknown>> | undefined;
321
+ }
322
+
323
+ /**
324
+ * A named, versionable template record — pure data, no behavior.
325
+ *
326
+ * @remarks
327
+ * `content` is the raw string carrying `{{name}}` tokens (see
328
+ * `FILL_PATTERN` in `constants.ts`); `placeholders` declares every token's
329
+ * lookup rule. `summary` / `description` / `category` / `tags` are optional
330
+ * catalog metadata for `TemplateManagerInterface#find`.
331
+ */
332
+ export declare interface TemplateDefinition {
333
+ readonly id: string;
334
+ readonly name: string;
335
+ readonly content: string;
336
+ readonly placeholders: readonly TemplatePlaceholder[];
337
+ readonly summary?: string;
338
+ readonly description?: string;
339
+ readonly category?: string;
340
+ readonly tags?: readonly string[];
341
+ }
342
+
343
+ /**
344
+ * An error thrown by the template layer.
345
+ *
346
+ * @remarks
347
+ * Thrown for: a required placeholder staying unresolved under the `error`
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
+ }
359
+
360
+ /**
361
+ * Coded misuse / failure conditions thrown as a {@link TemplateError}.
362
+ *
363
+ * @remarks
364
+ * `MISSING` — a required placeholder stayed unresolved under the `error`
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>>;
381
+
382
+ /**
383
+ * The template contract (AGENTS §22 — exact bijection with `Template`).
384
+ *
385
+ * @remarks
386
+ * `definition` returns the plain {@link TemplateDefinition} data. `fill`
387
+ * substitutes every `{{name}}` token in `content` against `values`,
388
+ * honoring `options.missing` for unresolved required placeholders. `validate`
389
+ * reports which required placeholders are unresolved (`missing`) and which
390
+ * supplied `values` keys are unused (`extra`) without producing output.
391
+ * `parameters` projects this template's placeholders to the open
392
+ * tool-parameters record shape (`schemaToParameters`'s return type from
393
+ * `@orkestrel/contract`).
394
+ */
395
+ export declare interface TemplateInterface {
396
+ readonly id: string;
397
+ readonly name: string;
398
+ readonly content: string;
399
+ readonly placeholders: readonly TemplatePlaceholder[];
400
+ readonly summary?: string;
401
+ readonly description?: string;
402
+ readonly category?: string;
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
+ }
409
+
410
+ /**
411
+ * The template registry — a self-owning, id-keyed record-holder for the
412
+ * {@link TemplateInterface} instances a consumer registers, looks up, fills,
413
+ * and validates by id (AGENTS §9.1 singular/plural accessors, §9.2 batch
414
+ * `remove` overloads, §13 emitter ownership).
415
+ *
416
+ * @remarks
417
+ * `register` accepts either a constructed {@link TemplateInterface} (kept
418
+ * as-is, including its own `missing` / `locale` defaults) or a plain
419
+ * {@link TemplateOptions} bag — constructed into a `Template` with this
420
+ * manager's `missing` / `locale` defaults applied wherever the bag omits
421
+ * them. A duplicate `id` throws a {@link TemplateError} coded `CONFLICT`
422
+ * unless `options.replace` is `true`, in which case the existing entry is
423
+ * overwritten. `options.templates` SEEDS the registry at construction
424
+ * WITHOUT emitting `register` — only calls to `register` after construction
425
+ * emit. The batch `remove(ids)` form is ALL-OR-NOTHING: any id absent from
426
+ * the registry leaves the collection untouched and returns `false`.
427
+ *
428
+ * @example
429
+ * ```ts
430
+ * import { TemplateManager } from '@src/core'
431
+ *
432
+ * const manager = new TemplateManager()
433
+ * const instance = manager.register({ name: 'greeting', content: 'Hi {{name}}' })
434
+ * manager.fill(instance.id, { name: 'Ada' }) // 'Hi Ada'
435
+ * ```
436
+ */
437
+ export declare class TemplateManager implements TemplateManagerInterface {
438
+ #private;
439
+ constructor(options?: TemplateManagerOptions);
440
+ get emitter(): EmitterInterface<TemplateManagerEventMap>;
441
+ get size(): number;
442
+ /**
443
+ * Register a template — a constructed {@link TemplateInterface} (kept
444
+ * as-is) or a plain {@link TemplateOptions} bag (constructed into a
445
+ * `Template` with this manager's `missing` / `locale` defaults applied
446
+ * wherever the bag omits them).
447
+ *
448
+ * @param template - The template instance or options to register
449
+ * @param options - `replace` — overwrite an existing entry sharing the same id instead of throwing
450
+ * @returns The registered {@link TemplateInterface}
451
+ *
452
+ * @example
453
+ * ```ts
454
+ * const instance = manager.register({ id: 'greeting', name: 'greeting', content: 'Hi {{name}}' })
455
+ * ```
456
+ */
457
+ register(template: TemplateInterface | TemplateOptions, options?: {
458
+ readonly replace?: boolean;
459
+ }): TemplateInterface;
460
+ /**
461
+ * Look up a registered template by id.
462
+ *
463
+ * @param id - The template id
464
+ * @returns The registered {@link TemplateInterface}
465
+ * @throws {@link TemplateError} coded `NOTFOUND` when `id` is unknown
466
+ */
467
+ template(id: string): TemplateInterface;
468
+ /**
469
+ * List every registered template.
470
+ *
471
+ * @returns A snapshot array of every registered {@link TemplateInterface}
472
+ */
473
+ templates(): readonly TemplateInterface[];
474
+ /**
475
+ * Filter registered templates by name / category / tag — every supplied
476
+ * field must match (logical AND).
477
+ *
478
+ * @param query - The {@link TemplateQuery} to filter by; omit for every registered template
479
+ * @returns The matching templates
480
+ */
481
+ find(query?: TemplateQuery): readonly TemplateInterface[];
482
+ /**
483
+ * Test whether a template id is registered.
484
+ *
485
+ * @param id - The template id
486
+ * @returns `true` when `id` is registered
487
+ */
488
+ has(id: string): boolean;
489
+ /**
490
+ * Remove one, several, or every registered template (AGENTS §9.2 batch
491
+ * overloads) — array overload declared first so a list resolves to the
492
+ * batch form.
493
+ *
494
+ * @remarks
495
+ * `remove()` removes every registered template, emitting `remove` once per
496
+ * instance. `remove(id)` removes one template by id, emitting `remove` and
497
+ * returning `true` when it existed, `false` otherwise. `remove(ids)` is
498
+ * ALL-OR-NOTHING: if any id in the list is unregistered, the collection is
499
+ * left untouched and `false` is returned; otherwise every listed template
500
+ * is removed (each emitting `remove`) and `true` is returned.
501
+ *
502
+ * @param target - Omit to remove all, a single id, or a list of ids
503
+ * @returns `boolean` for the single-id / list-of-ids forms; `void` for the remove-all form
504
+ */
505
+ remove(ids: readonly string[]): boolean;
506
+ remove(id: string): boolean;
507
+ remove(): void;
508
+ /** Remove every registered template, emitting `clear`. */
509
+ clear(): void;
510
+ /**
511
+ * Fill a registered template by id.
512
+ *
513
+ * @param id - The template id
514
+ * @param values - The values tokens resolve against
515
+ * @param options - Per-call overrides for the template's `missing` / `locale` defaults
516
+ * @returns The substituted content
517
+ * @throws {@link TemplateError} coded `NOTFOUND` when `id` is unknown
518
+ */
519
+ fill(id: string, values?: TemplateFillValues, options?: TemplateFillOptions): string;
520
+ /**
521
+ * Validate values against a registered template by id.
522
+ *
523
+ * @param id - The template id
524
+ * @param values - The values to check
525
+ * @returns The {@link TemplateValidationResult}
526
+ * @throws {@link TemplateError} coded `NOTFOUND` when `id` is unknown
527
+ */
528
+ validate(id: string, values?: TemplateFillValues): TemplateValidationResult;
529
+ /**
530
+ * Project a registered template's parameters by id.
531
+ *
532
+ * @param id - The template id
533
+ * @returns The compiled parameters record, or `undefined` when the template has none
534
+ * @throws {@link TemplateError} coded `NOTFOUND` when `id` is unknown
535
+ */
536
+ parameters(id: string): Readonly<Record<string, unknown>> | undefined;
537
+ }
538
+
539
+ /**
540
+ * The push observation surface of a {@link TemplateManagerInterface} (AGENTS
541
+ * §13) — an id-keyed registry, so `register` / `remove` are the events
542
+ * (never ordered-list `append`/`prepend`).
543
+ */
544
+ export declare type TemplateManagerEventMap = {
545
+ /** A template was registered — carries the registered template. */
546
+ readonly register: readonly [template: TemplateInterface];
547
+ /** A template was removed — carries the removed template. */
548
+ readonly remove: readonly [template: TemplateInterface];
549
+ /** The registry was cleared. */
550
+ readonly clear: readonly [];
551
+ };
552
+
553
+ /**
554
+ * The template registry — a self-owning, id-keyed record-holder (AGENTS §9.1
555
+ * singular/plural accessors, §9.2 batch overloads).
556
+ *
557
+ * @remarks
558
+ * `register` accepts either a constructed {@link TemplateInterface} or a
559
+ * plain {@link TemplateOptions} bag (constructed internally), and throws a
560
+ * {@link TemplateError} coded `CONFLICT` when the id already exists unless
561
+ * `options.replace` is `true`. `template` throws `NOTFOUND` for an unknown
562
+ * id. `remove`'s batch form is all-or-nothing: any missing id in the list
563
+ * leaves the collection untouched and returns `false`.
564
+ */
565
+ export declare interface TemplateManagerInterface {
566
+ readonly emitter: EmitterInterface<TemplateManagerEventMap>;
567
+ readonly size: number;
568
+ register(template: TemplateInterface | TemplateOptions, options?: {
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;
582
+ }
583
+
584
+ /**
585
+ * Options for `createTemplateManager` / the `TemplateManager` constructor.
586
+ *
587
+ * @remarks
588
+ * `templates` seeds the registry — either constructed {@link TemplateInterface}
589
+ * instances or plain {@link TemplateOptions} bags. `missing` / `locale` are
590
+ * the manager-wide default {@link TemplateFillOptions}, overridable per-call.
591
+ * `on` — initial event listeners (AGENTS §8/§13). `error` — the emitter's
592
+ * listener-error handler.
593
+ */
594
+ export declare interface TemplateManagerOptions {
595
+ readonly templates?: readonly (TemplateInterface | TemplateOptions)[];
596
+ readonly missing?: MissingPolicy;
597
+ readonly locale?: string;
598
+ readonly on?: EmitterHooks<TemplateManagerEventMap>;
599
+ readonly error?: EmitterErrorHandler;
600
+ }
601
+
602
+ /**
603
+ * Options for `createTemplate` / the `Template` constructor.
604
+ *
605
+ * @remarks
606
+ * `id` defaults to a generated id when omitted. `placeholders` defaults to
607
+ * an empty list. `missing` / `locale` seed the instance's default
608
+ * {@link TemplateFillOptions}, overridable per-call.
609
+ */
610
+ export declare interface TemplateOptions {
611
+ readonly id?: string;
612
+ readonly name: string;
613
+ readonly content: string;
614
+ readonly placeholders?: readonly TemplatePlaceholder[];
615
+ readonly summary?: string;
616
+ readonly description?: string;
617
+ readonly category?: string;
618
+ readonly tags?: readonly string[];
619
+ readonly missing?: MissingPolicy;
620
+ readonly locale?: string;
621
+ }
622
+
623
+ /**
624
+ * One placeholder a {@link TemplateDefinition}'s `content` declares — its
625
+ * lookup name, an optional field path into the values record, whether it is
626
+ * required, and a literal fallback.
627
+ *
628
+ * @remarks
629
+ * `name` is the `{{name}}` token as written in `content`. `path`, when
630
+ * present, resolves the fill value through a (possibly nested)
631
+ * {@link FieldPath} rather than a flat `name` lookup on the values record.
632
+ * `required` defaults to `true` when omitted — an unresolved required
633
+ * placeholder is governed by the active {@link MissingPolicy}. `fallback` is
634
+ * a literal substituted when the value is unresolved, regardless of
635
+ * `required`.
636
+ */
637
+ export declare interface TemplatePlaceholder {
638
+ readonly name: string;
639
+ readonly path?: FieldPath;
640
+ readonly required?: boolean;
641
+ readonly fallback?: unknown;
642
+ readonly description?: string;
643
+ }
644
+
645
+ /** A query for `TemplateManagerInterface#find` — every supplied field must match. */
646
+ export declare interface TemplateQuery {
647
+ readonly name?: string;
648
+ readonly category?: string;
649
+ readonly tag?: string;
650
+ }
651
+
652
+ /** The outcome of `TemplateInterface#validate` — which required placeholders are unresolved, and which supplied values are unused. */
653
+ export declare interface TemplateValidationResult {
654
+ readonly valid: boolean;
655
+ readonly missing: readonly string[];
656
+ readonly extra: readonly string[];
657
+ }
658
+
659
+ /**
660
+ * Prototype-pollution-unsafe field-path segments — a fill lookup refuses to
661
+ * resolve ANY path containing one, treating the placeholder as unresolved.
662
+ */
663
+ export declare const UNSAFE_FIELD_SEGMENTS: readonly string[];
664
+
665
+ export { }