@orkestrel/template 0.0.4 → 0.0.6

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.
@@ -2,8 +2,8 @@ import { createContract, isFiniteNumber, objectShape, optionalShape, resolveFiel
2
2
  import { Emitter } from "@orkestrel/emitter";
3
3
  //#region src/core/constants.ts
4
4
  /**
5
- * The single-pass `{{name}}` substitution pattern shared by `Template#fill`
6
- * and `Template#validate`.
5
+ * Holds the single-pass `{{name}}` substitution pattern shared by
6
+ * `Template#fill` and `Template#validate`.
7
7
  *
8
8
  * @remarks
9
9
  * Global-flagged, two-alternative pattern: a match of the FIRST alternative
@@ -22,13 +22,14 @@ import { Emitter } from "@orkestrel/emitter";
22
22
  * instance's mutable `lastIndex` across scans.
23
23
  */
24
24
  var FILL_PATTERN = /\\\{\{|\{\{([^{}]+?)\}\}/g;
25
- /** Default `missing` policy for `Template#fill` / `TemplateManager#fill` when unspecified. */
25
+ /** Holds the default `missing` policy for `Template#fill` / `TemplateManager#fill` when unspecified. */
26
26
  var DEFAULT_MISSING_POLICY = "error";
27
- /** Default `locale` for `Template#fill` / `TemplateManager#fill` when unspecified. */
27
+ /** Holds the default `locale` for `Template#fill` / `TemplateManager#fill` when unspecified. */
28
28
  var DEFAULT_LOCALE = "en-US";
29
29
  /**
30
- * Prototype-pollution-unsafe field-path segments — a fill lookup refuses to
31
- * resolve ANY path containing one, treating the placeholder as unresolved.
30
+ * Lists the prototype-pollution-unsafe field-path segments — a fill lookup
31
+ * refuses to resolve ANY path containing one, treating the placeholder as
32
+ * unresolved.
32
33
  */
33
34
  var UNSAFE_FIELD_SEGMENTS = Object.freeze([
34
35
  "__proto__",
@@ -38,7 +39,7 @@ var UNSAFE_FIELD_SEGMENTS = Object.freeze([
38
39
  //#endregion
39
40
  //#region src/core/errors.ts
40
41
  /**
41
- * An error thrown by the template layer.
42
+ * Represents an error thrown by the template layer.
42
43
  *
43
44
  * @remarks
44
45
  * Thrown for: a required placeholder staying unresolved under the `error`
@@ -59,17 +60,17 @@ var TemplateError = class extends Error {
59
60
  }
60
61
  };
61
62
  /**
62
- * Narrow an unknown caught value to a {@link TemplateError}.
63
+ * Narrows an unknown caught value to a {@link TemplateError}.
63
64
  *
64
65
  * @param value - The value to test (typically a `catch` binding)
65
- * @returns `true` when `value` is a {@link TemplateError}
66
+ * @returns True if `value` is a {@link TemplateError}; false otherwise
66
67
  *
67
68
  * @example
68
69
  * ```ts
69
70
  * import { isTemplateError } from '@src/core'
70
71
  *
71
72
  * try {
72
- * manager.template('missing')
73
+ * manager.fill('missing')
73
74
  * } catch (error) {
74
75
  * if (isTemplateError(error) && error.code === 'NOTFOUND') return
75
76
  * }
@@ -81,13 +82,14 @@ function isTemplateError(value) {
81
82
  //#endregion
82
83
  //#region src/core/helpers.ts
83
84
  /**
84
- * Format a resolved fill value for substitution into a template's `content`.
85
+ * Formats a resolved fill value for substitution into a template's `content`.
85
86
  *
86
87
  * @remarks
87
- * A finite number renders with the given locale's thousand grouping (via
88
+ * A finite number renders with the given locale's thousand grouping (through
88
89
  * `toLocaleString`); every other value — including `null` — String-coerces.
89
- * `null` therefore renders as the literal string `'null'`, intentionally
90
- * mirroring `interpolateMessage`'s coercion parity (see `fillTemplate`). An
90
+ * `null` therefore renders as the literal string `'null'`, matching
91
+ * `String(value)` exactly, so a resolved `null` is visible in the output
92
+ * rather than silently empty. An
91
93
  * invalid BCP-47 `locale` tag throws a `RangeError` from the underlying
92
94
  * `toLocaleString` call when `value` is a finite number — this is a caller
93
95
  * error (an invalid locale argument), by design, and is not caught here.
@@ -109,7 +111,7 @@ function formatValue(value, locale) {
109
111
  return String(value);
110
112
  }
111
113
  /**
112
- * Resolve a field path against a fill-values record, refusing any path that
114
+ * Resolves a field path against a fill-values record, refusing any path that
113
115
  * touches a prototype-pollution-unsafe segment.
114
116
  *
115
117
  * @remarks
@@ -139,16 +141,55 @@ function resolveSafeField(record, path) {
139
141
  return resolveField(record, path);
140
142
  }
141
143
  /**
142
- * Substitute every `{{name}}` token in `content` in a single pass.
144
+ * Resolves one `{{name}}` token against the declared placeholders and the
145
+ * fill-values record.
146
+ *
147
+ * @remarks
148
+ * The single implementation of the token rule `fillTemplate` and
149
+ * `Template#validate` both apply, so the two can never drift: the declared
150
+ * {@link TemplatePlaceholder} sharing the token's `name` (exact match)
151
+ * supplies its `path`, falling back to the token split on `.`; the value
152
+ * resolves through `resolveSafeField`, so any segment in
153
+ * `UNSAFE_FIELD_SEGMENTS` yields `undefined` without ever calling
154
+ * `resolveField`; `required` is `true` for an undeclared token and for a
155
+ * declared placeholder whose `required` is not `false`. The token is passed
156
+ * already trimmed. `fallback` is not applied here — it is read from
157
+ * `declared` by each caller, because `fill` substitutes it and `validate`
158
+ * only counts it.
159
+ *
160
+ * @param record - The fill-values record the token resolves against
161
+ * @param placeholders - The declared placeholders the token matches by name
162
+ * @param token - The trimmed token text, without its `{{` / `}}` delimiters
163
+ * @returns The {@link TemplateTokenResolution} for the token
164
+ *
165
+ * @example
166
+ * ```ts
167
+ * import { resolveToken } from '@src/core'
168
+ *
169
+ * resolveToken({ name: 'Ada' }, [], 'name').value // 'Ada'
170
+ * resolveToken({}, [{ name: 'nickname', required: false }], 'nickname').required // false
171
+ * ```
172
+ */
173
+ function resolveToken(record, placeholders, token) {
174
+ const declared = placeholders.find((placeholder) => placeholder.name === token);
175
+ return {
176
+ value: resolveSafeField(record, declared?.path ?? token.split(".")),
177
+ declared,
178
+ required: declared === void 0 || declared.required !== false
179
+ };
180
+ }
181
+ /**
182
+ * Substitutes every `{{name}}` token in `content` in a single pass.
143
183
  *
144
184
  * @remarks
145
185
  * Uses a fresh `RegExp` clone of `FILL_PATTERN` per call (never sharing its
146
186
  * `lastIndex`) and a single `String#replace` scan — substituted output is
147
- * never re-scanned. For each token: the matching declared
187
+ * never re-scanned. Each token resolves through `resolveToken`, the one rule
188
+ * `Template#validate` also applies: the matching declared
148
189
  * {@link TemplatePlaceholder} (exact `name`) supplies its `path` (falling
149
190
  * back to the token split on `.`); ANY path segment in `UNSAFE_FIELD_SEGMENTS`
150
191
  * makes the token unresolved without ever calling `resolveField` (a
151
- * prototype-pollution guard). A resolved value formats via `formatValue`; an
192
+ * prototype-pollution guard). A resolved value formats through `formatValue`; an
152
193
  * unresolved value falls back to the placeholder's `fallback` when declared;
153
194
  * otherwise `options.missing` governs — `'literal'` re-emits the original
154
195
  * `{{name}}` text, `'empty'` emits `''`, and `'error'` emits `''` for every
@@ -157,11 +198,11 @@ function resolveSafeField(record, path) {
157
198
  * {@link TemplateError} coded `MISSING` listing them all, in first-appearance
158
199
  * order, once the scan completes. An escaped `\{{` emits a literal `{{`.
159
200
  *
160
- * PARITY: called with no declared `placeholders` and `{ missing: 'empty' }`,
161
- * this reproduces `interpolateMessage` (`@src/core` sibling
162
- * `interpret`) vector-for-vector. KNOWN DIVERGENCE: `FILL_PATTERN`'s token
163
- * class (`[^{}]`) excludes `{`, where `interpolateMessage`'s (`[^}]`) allows
164
- * it a token containing `{` therefore behaves differently here.
201
+ * Called with no declared `placeholders` and `{ missing: 'empty' }`, this is a
202
+ * bare interpolation over `content` every token resolves by dotted path
203
+ * against the values record and every unresolved token emits `''`.
204
+ * `FILL_PATTERN`'s token class (`[^{}]`) excludes `{`, so a token containing
205
+ * `{` never matches and the surrounding `{{` stays literal.
165
206
  *
166
207
  * @param content - The template content carrying `{{name}}` tokens
167
208
  * @param values - The values tokens resolve against
@@ -187,14 +228,12 @@ function fillTemplate(content, values, options) {
187
228
  const result = content.replace(pattern, (matchText, rawToken) => {
188
229
  if (rawToken === void 0) return "{{";
189
230
  const token = rawToken.trim();
190
- const declared = placeholders.find((placeholder) => placeholder.name === token);
191
- const path = declared?.path ?? token.split(".");
192
- const value = resolveSafeField(record, path);
231
+ const { value, declared, required } = resolveToken(record, placeholders, token);
193
232
  if (value !== void 0) return formatValue(value, locale);
194
233
  if (declared?.fallback !== void 0) return formatValue(declared.fallback, locale);
195
234
  if (missing === "literal") return matchText;
196
235
  if (missing === "empty") return "";
197
- if ((declared === void 0 || declared.required !== false) && !seen.has(token)) {
236
+ if (required && !seen.has(token)) {
198
237
  seen.add(token);
199
238
  missingNames.push(token);
200
239
  }
@@ -203,8 +242,10 @@ function fillTemplate(content, values, options) {
203
242
  if (missing === "error" && missingNames.length > 0) throw new TemplateError("MISSING", `Missing required placeholder(s): ${missingNames.join(", ")}`, { missing: missingNames });
204
243
  return result;
205
244
  }
245
+ //#endregion
246
+ //#region src/core/shapers.ts
206
247
  /**
207
- * Build the `@orkestrel/contract` object shape describing a template's
248
+ * Builds the `@orkestrel/contract` object shape describing a template's
208
249
  * declared placeholders.
209
250
  *
210
251
  * @remarks
@@ -233,15 +274,17 @@ function placeholderShape(placeholders) {
233
274
  return objectShape(properties);
234
275
  }
235
276
  //#endregion
236
- //#region src/core/Template.ts
277
+ //#region src/core/templates/Template.ts
237
278
  /**
238
- * A named, versionable template — `{{name}}` tokens in `content`, filled
239
- * against a values record.
279
+ * Represents a named, versionable template — `{{name}}` tokens in `content`,
280
+ * filled against a values record.
240
281
  *
241
282
  * @remarks
242
283
  * `missing` / `locale` seed this instance's default {@link TemplateFillOptions},
243
284
  * overridable per `fill` call. Its `parameters()` contract (built from
244
- * `placeholders` via `placeholderShape`) compiles once, in the constructor.
285
+ * `placeholders` through `placeholderShape`) compiles once, in the constructor.
286
+ *
287
+ * @throws {@link TemplateError} Thrown when `options.placeholders` declares a duplicate `name` or an empty `path` (coded `INVALID`)
245
288
  *
246
289
  * @example
247
290
  * ```ts
@@ -250,6 +293,9 @@ function placeholderShape(placeholders) {
250
293
  * ```
251
294
  */
252
295
  var Template = class {
296
+ #missing;
297
+ #locale;
298
+ #contract;
253
299
  id;
254
300
  name;
255
301
  content;
@@ -258,9 +304,6 @@ var Template = class {
258
304
  description;
259
305
  category;
260
306
  tags;
261
- #missing;
262
- #locale;
263
- #contract;
264
307
  constructor(options) {
265
308
  const placeholders = options.placeholders ?? [];
266
309
  const seenNames = /* @__PURE__ */ new Set();
@@ -282,7 +325,7 @@ var Template = class {
282
325
  this.#contract = createContract(placeholderShape(this.placeholders));
283
326
  }
284
327
  /**
285
- * The plain, JSON-serializable data this template carries.
328
+ * Returns the plain, JSON-serializable data this template carries.
286
329
  *
287
330
  * @returns The {@link TemplateDefinition} record
288
331
  *
@@ -305,11 +348,12 @@ var Template = class {
305
348
  };
306
349
  }
307
350
  /**
308
- * Substitute every `{{name}}` token in `content` against `values`.
351
+ * Substitutes every `{{name}}` token in `content` against `values`.
309
352
  *
310
353
  * @param values - The values tokens resolve against
311
354
  * @param options - Per-call overrides for this instance's `missing` / `locale` defaults
312
355
  * @returns The substituted content
356
+ * @throws {@link TemplateError} Thrown when a required placeholder stays unresolved under the `'error'` policy (coded `MISSING`)
313
357
  *
314
358
  * @example
315
359
  * ```ts
@@ -325,7 +369,7 @@ var Template = class {
325
369
  });
326
370
  }
327
371
  /**
328
- * Report which required placeholders would stay unresolved, and which
372
+ * Reports which required placeholders would stay unresolved, and which
329
373
  * `values` keys go unused, without producing output.
330
374
  *
331
375
  * @remarks
@@ -334,9 +378,10 @@ var Template = class {
334
378
  * predicts `fill`'s `'error'`-{@link MissingPolicy} outcome exactly — a
335
379
  * token reported here as missing is precisely a token that would throw
336
380
  * under `fill(values, { missing: 'error' })`. For each distinct token
337
- * (first-appearance order, trimmed): a declared {@link TemplatePlaceholder}
381
+ * (first-appearance order, trimmed): `resolveToken` applies the one shared
382
+ * token rule `fill` also applies — a declared {@link TemplatePlaceholder}
338
383
  * sharing its `name` supplies `path` (falling back to the token split on
339
- * `.`); the value resolves via `resolveSafeField`. The token is `missing`
384
+ * `.`), and the value resolves through `resolveSafeField`. The token is `missing`
340
385
  * only when the value is unresolved AND no `fallback` is declared AND the
341
386
  * placeholder is required (`required !== false`, including undeclared
342
387
  * tokens). `extra` lists every `values` key with no declared placeholder.
@@ -365,10 +410,8 @@ var Template = class {
365
410
  const token = rawToken.trim();
366
411
  if (seen.has(token)) continue;
367
412
  seen.add(token);
368
- const declared = this.placeholders.find((placeholder) => placeholder.name === token);
369
- const resolved = resolveSafeField(record, declared?.path ?? token.split("."));
370
- const required = declared === void 0 || declared.required !== false;
371
- if (resolved === void 0 && declared?.fallback === void 0 && required) missing.push(token);
413
+ const { value, declared, required } = resolveToken(record, this.placeholders, token);
414
+ if (value === void 0 && declared?.fallback === void 0 && required) missing.push(token);
372
415
  }
373
416
  const declaredNames = new Set(this.placeholders.map((placeholder) => placeholder.name));
374
417
  const extra = Object.keys(record).filter((key) => !declaredNames.has(key));
@@ -379,7 +422,7 @@ var Template = class {
379
422
  };
380
423
  }
381
424
  /**
382
- * Project this template's placeholders to the open tool-parameters record
425
+ * Projects this template's placeholders to the open tool-parameters record
383
426
  * shape.
384
427
  *
385
428
  * @returns The compiled parameters record, or `undefined` when `schemaToParameters` yields none
@@ -399,12 +442,12 @@ var Template = class {
399
442
  }
400
443
  };
401
444
  //#endregion
402
- //#region src/core/TemplateManager.ts
445
+ //#region src/core/templates/TemplateManager.ts
403
446
  /**
404
- * The template registry — a self-owning, id-keyed record-holder for the
447
+ * Represents the template registry — a self-owning, id-keyed record-holder for the
405
448
  * {@link TemplateInterface} instances a consumer registers, looks up, fills,
406
- * and validates by id (AGENTS §9.1 singular/plural accessors, §9.2 batch
407
- * `remove` overloads, §13 emitter ownership).
449
+ * and validates by id, with singular/plural accessors, batch `remove`
450
+ * overloads, and emitter ownership.
408
451
  *
409
452
  * @remarks
410
453
  * `register` accepts either a constructed {@link TemplateInterface} (kept
@@ -415,8 +458,8 @@ var Template = class {
415
458
  * unless `options.replace` is `true`, in which case the existing entry is
416
459
  * overwritten. `options.templates` SEEDS the registry at construction
417
460
  * WITHOUT emitting `register` — only calls to `register` after construction
418
- * emit. The batch `remove(ids)` form is ALL-OR-NOTHING: any id absent from
419
- * the registry leaves the collection untouched and returns `false`.
461
+ * emit. The batch `remove(ids)` form removes every present id and returns
462
+ * `true` only when every listed id was present.
420
463
  *
421
464
  * @example
422
465
  * ```ts
@@ -449,11 +492,11 @@ var TemplateManager = class {
449
492
  get emitter() {
450
493
  return this.#emitter;
451
494
  }
452
- get size() {
495
+ get count() {
453
496
  return this.#templates.size;
454
497
  }
455
498
  /**
456
- * Register a template — a constructed {@link TemplateInterface} (kept
499
+ * Registers a template — a constructed {@link TemplateInterface} (kept
457
500
  * as-is) or a plain {@link TemplateOptions} bag (constructed into a
458
501
  * `Template` with this manager's `missing` / `locale` defaults applied
459
502
  * wherever the bag omits them).
@@ -461,6 +504,7 @@ var TemplateManager = class {
461
504
  * @param template - The template instance or options to register
462
505
  * @param options - `replace` — overwrite an existing entry sharing the same id instead of throwing
463
506
  * @returns The registered {@link TemplateInterface}
507
+ * @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`)
464
508
  *
465
509
  * @example
466
510
  * ```ts
@@ -475,19 +519,16 @@ var TemplateManager = class {
475
519
  return instance;
476
520
  }
477
521
  /**
478
- * Look up a registered template by id.
522
+ * Returns one registered {@link TemplateInterface} by id.
479
523
  *
480
524
  * @param id - The template id
481
- * @returns The registered {@link TemplateInterface}
482
- * @throws {@link TemplateError} coded `NOTFOUND` when `id` is unknown
525
+ * @returns The registered {@link TemplateInterface}, or `undefined` when `id` is unregistered
483
526
  */
484
527
  template(id) {
485
- const instance = this.#templates.get(id);
486
- if (instance === void 0) this.#throwNotFound(id);
487
- return instance;
528
+ return this.#templates.get(id);
488
529
  }
489
530
  /**
490
- * List every registered template.
531
+ * Lists every registered template.
491
532
  *
492
533
  * @returns A snapshot array of every registered {@link TemplateInterface}
493
534
  */
@@ -495,7 +536,7 @@ var TemplateManager = class {
495
536
  return [...this.#templates.values()];
496
537
  }
497
538
  /**
498
- * Filter registered templates by name / category / tag — every supplied
539
+ * Filters registered templates by name / category / tag — every supplied
499
540
  * field must match (logical AND).
500
541
  *
501
542
  * @param query - The {@link TemplateQuery} to filter by; omit for every registered template
@@ -511,10 +552,10 @@ var TemplateManager = class {
511
552
  });
512
553
  }
513
554
  /**
514
- * Test whether a template id is registered.
555
+ * Tests whether a template id is registered.
515
556
  *
516
557
  * @param id - The template id
517
- * @returns `true` when `id` is registered
558
+ * @returns True if `id` is registered; false otherwise
518
559
  */
519
560
  has(id) {
520
561
  return this.#templates.has(id);
@@ -532,34 +573,58 @@ var TemplateManager = class {
532
573
  this.#emitter.emit("remove", instance);
533
574
  return true;
534
575
  }
535
- for (const id of target) if (!this.#templates.has(id)) return false;
576
+ let all = true;
536
577
  for (const id of target) {
537
578
  const instance = this.#templates.get(id);
538
- if (instance === void 0) continue;
579
+ if (instance === void 0) {
580
+ all = false;
581
+ continue;
582
+ }
539
583
  this.#templates.delete(id);
540
584
  this.#emitter.emit("remove", instance);
541
585
  }
542
- return true;
586
+ return all;
543
587
  }
544
- /** Remove every registered template, emitting `clear`. */
588
+ /** Removes every registered template, emitting `clear`. */
545
589
  clear() {
546
590
  this.#templates.clear();
547
591
  this.#emitter.emit("clear");
548
592
  }
549
593
  /**
550
- * Fill a registered template by id.
594
+ * Tears down the registry: drops every registered template and destroys the
595
+ * owned emitter. Idempotent.
596
+ *
597
+ * @remarks
598
+ * Teardown is not an observable registry operation and the emitter is being
599
+ * released, so this emits neither `clear` nor `remove`. The emitter is torn
600
+ * down last, after the registry is dropped.
601
+ *
602
+ * @example
603
+ * ```ts
604
+ * const manager = new TemplateManager()
605
+ * manager.destroy()
606
+ * manager.emitter.destroyed // true
607
+ * ```
608
+ */
609
+ destroy() {
610
+ this.#templates.clear();
611
+ this.#emitter.destroy();
612
+ }
613
+ /**
614
+ * Fills a registered template by id.
551
615
  *
552
616
  * @param id - The template id
553
617
  * @param values - The values tokens resolve against
554
618
  * @param options - Per-call overrides for the template's `missing` / `locale` defaults
555
619
  * @returns The substituted content
556
620
  * @throws {@link TemplateError} coded `NOTFOUND` when `id` is unknown
621
+ * @throws {@link TemplateError} Thrown when a required placeholder stays unresolved under the `'error'` policy (coded `MISSING`)
557
622
  */
558
623
  fill(id, values, options) {
559
- return this.template(id).fill(values, options);
624
+ return this.#require(id).fill(values, options);
560
625
  }
561
626
  /**
562
- * Validate values against a registered template by id.
627
+ * Validates values against a registered template by id.
563
628
  *
564
629
  * @param id - The template id
565
630
  * @param values - The values to check
@@ -567,17 +632,17 @@ var TemplateManager = class {
567
632
  * @throws {@link TemplateError} coded `NOTFOUND` when `id` is unknown
568
633
  */
569
634
  validate(id, values) {
570
- return this.template(id).validate(values);
635
+ return this.#require(id).validate(values);
571
636
  }
572
637
  /**
573
- * Project a registered template's parameters by id.
638
+ * Projects a registered template's parameters by id.
574
639
  *
575
640
  * @param id - The template id
576
641
  * @returns The compiled parameters record, or `undefined` when the template has none
577
642
  * @throws {@link TemplateError} coded `NOTFOUND` when `id` is unknown
578
643
  */
579
644
  parameters(id) {
580
- return this.template(id).parameters();
645
+ return this.#require(id).parameters();
581
646
  }
582
647
  #instantiate(template) {
583
648
  if (this.#isInstance(template)) return template;
@@ -590,19 +655,22 @@ var TemplateManager = class {
590
655
  #isInstance(template) {
591
656
  return "fill" in template && typeof template.fill === "function" && "validate" in template && typeof template.validate === "function" && "parameters" in template && typeof template.parameters === "function";
592
657
  }
593
- #throwNotFound(id) {
594
- throw new TemplateError("NOTFOUND", `Unknown template id: ${id}`, { id });
658
+ #require(id) {
659
+ const instance = this.#templates.get(id);
660
+ if (instance === void 0) throw new TemplateError("NOTFOUND", `Unknown template id: ${id}`, { id });
661
+ return instance;
595
662
  }
596
663
  };
597
664
  //#endregion
598
665
  //#region src/core/factories.ts
599
666
  /**
600
- * Create a template.
667
+ * Creates a template.
601
668
  *
602
669
  * @param options - The template's `name` / `content`, an optional `id`
603
670
  * (defaults to a generated UUID), `placeholders`, catalog metadata, and
604
671
  * `missing` / `locale` fill defaults
605
672
  * @returns A working {@link TemplateInterface}
673
+ * @throws {@link TemplateError} Thrown when `options.placeholders` declares a duplicate `name` or an empty `path` (coded `INVALID`)
606
674
  *
607
675
  * @example
608
676
  * ```ts
@@ -616,12 +684,13 @@ function createTemplate(options) {
616
684
  return new Template(options);
617
685
  }
618
686
  /**
619
- * Create a template registry.
687
+ * Creates a template registry.
620
688
  *
621
689
  * @param options - Optional initial `templates` seed collection and
622
690
  * manager-wide `missing` / `locale` fill defaults, emitter `on` hooks, and
623
691
  * an `error` handler
624
692
  * @returns A working {@link TemplateManagerInterface}
693
+ * @throws {@link TemplateError} Thrown when a seeded `options.templates` bag declares a duplicate placeholder `name` or an empty `path` (coded `INVALID`)
625
694
  *
626
695
  * @example
627
696
  * ```ts
@@ -637,6 +706,6 @@ function createTemplateManager(options) {
637
706
  return new TemplateManager(options);
638
707
  }
639
708
  //#endregion
640
- export { DEFAULT_LOCALE, DEFAULT_MISSING_POLICY, FILL_PATTERN, Template, TemplateError, TemplateManager, UNSAFE_FIELD_SEGMENTS, createTemplate, createTemplateManager, fillTemplate, formatValue, isTemplateError, placeholderShape, resolveSafeField };
709
+ export { DEFAULT_LOCALE, DEFAULT_MISSING_POLICY, FILL_PATTERN, Template, TemplateError, TemplateManager, UNSAFE_FIELD_SEGMENTS, createTemplate, createTemplateManager, fillTemplate, formatValue, isTemplateError, placeholderShape, resolveSafeField, resolveToken };
641
710
 
642
711
  //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["#missing","#locale","#contract","#templates","#emitter","#missing","#locale","#instantiate","#require","#isInstance"],"sources":["../../../src/core/constants.ts","../../../src/core/errors.ts","../../../src/core/helpers.ts","../../../src/core/shapers.ts","../../../src/core/templates/Template.ts","../../../src/core/templates/TemplateManager.ts","../../../src/core/factories.ts"],"sourcesContent":["import type { MissingPolicy } from './types.js'\n\n// Frozen default data for the template module — constants are\n// UPPER_SNAKE_CASE data, the sole home for module-scope literal defaults.\n\n/**\n * Holds the single-pass `{{name}}` substitution pattern shared by\n * `Template#fill` and `Template#validate`.\n *\n * @remarks\n * Global-flagged, two-alternative pattern: a match of the FIRST alternative\n * (`\\{{` — a literal backslash followed by `{{`) means \"emit a literal\n * `{{`\" — the escape hatch for content that must show `{{` without\n * triggering substitution. A match that instead populates capture group 1\n * (`\\{{([^{}]+?)\\}\\}`) means \"substitute the named token\" — group 1 is the\n * RAW (untrimmed) token text between the braces; every call site trims it\n * (`token.trim()`) before using it as a lookup name, so `'{{ name }}'` still\n * resolves `'name'`. The pattern intentionally does NOT wrap the token in\n * `\\s*` — an unclosed `'{{' + ' '.repeat(n)` with no closing `}}` would\n * otherwise force the regex engine into catastrophic backtracking over the\n * whitespace run (O(n^2)); trimming after the match keeps the same\n * whitespace tolerance without the backtracking hazard. Every call site\n * builds a fresh `RegExp` from `.source` / `.flags` rather than sharing this\n * instance's mutable `lastIndex` across scans.\n */\nexport const FILL_PATTERN = /\\\\\\{\\{|\\{\\{([^{}]+?)\\}\\}/g\n\n/** Holds the default `missing` policy for `Template#fill` / `TemplateManager#fill` when unspecified. */\nexport const DEFAULT_MISSING_POLICY: MissingPolicy = 'error'\n\n/** Holds the default `locale` for `Template#fill` / `TemplateManager#fill` when unspecified. */\nexport const DEFAULT_LOCALE = 'en-US'\n\n/**\n * Lists the prototype-pollution-unsafe field-path segments — a fill lookup\n * refuses to resolve ANY path containing one, treating the placeholder as\n * unresolved.\n */\nexport const UNSAFE_FIELD_SEGMENTS: readonly string[] = Object.freeze([\n\t'__proto__',\n\t'constructor',\n\t'prototype',\n])\n","import type { TemplateErrorCode } from './types.js'\n\n// Misuse of the template layer `throw`s a `TemplateError` carrying a\n// machine-readable `code`, so a `catch` branches on `error.code`.\n\n/**\n * Represents an error thrown by the template layer.\n *\n * @remarks\n * Thrown for: a required placeholder staying unresolved under the `error`\n * {@link MissingPolicy} (`MISSING`), an unknown template id\n * (`NOTFOUND`), `createTemplate` handed invalid data (`INVALID`), and\n * `TemplateManagerInterface#register` handed an id already present without\n * `options.replace` (`CONFLICT`). `context`, when present, carries the\n * offending id / name.\n */\nexport class TemplateError extends Error {\n\treadonly code: TemplateErrorCode\n\treadonly context?: Readonly<Record<string, unknown>>\n\n\tconstructor(\n\t\tcode: TemplateErrorCode,\n\t\tmessage: string,\n\t\tcontext?: Readonly<Record<string, unknown>>,\n\t) {\n\t\tsuper(message)\n\t\tthis.name = 'TemplateError'\n\t\tthis.code = code\n\t\tif (context !== undefined) this.context = context\n\t}\n}\n\n/**\n * Narrows an unknown caught value to a {@link TemplateError}.\n *\n * @param value - The value to test (typically a `catch` binding)\n * @returns True if `value` is a {@link TemplateError}; false otherwise\n *\n * @example\n * ```ts\n * import { isTemplateError } from '@src/core'\n *\n * try {\n * \tmanager.fill('missing')\n * } catch (error) {\n * \tif (isTemplateError(error) && error.code === 'NOTFOUND') return\n * }\n * ```\n */\nexport function isTemplateError(value: unknown): value is TemplateError {\n\treturn value instanceof TemplateError\n}\n","import type { FieldPath } from '@orkestrel/contract'\nimport type {\n\tTemplateFillContext,\n\tTemplateFillValues,\n\tTemplatePlaceholder,\n\tTemplateTokenResolution,\n} from './types.js'\nimport { isFiniteNumber, resolveField } from '@orkestrel/contract'\nimport {\n\tDEFAULT_LOCALE,\n\tDEFAULT_MISSING_POLICY,\n\tFILL_PATTERN,\n\tUNSAFE_FIELD_SEGMENTS,\n} from './constants.js'\nimport { TemplateError } from './errors.js'\n\n// The templates pure-leaf inventory — every function here is a\n// referentially-transparent computation with no instance state, exported and\n// independently unit-testable. `Template#fill` / `#validate` route through\n// these leaves rather than duplicating the substitution logic.\n\n/**\n * Formats a resolved fill value for substitution into a template's `content`.\n *\n * @remarks\n * A finite number renders with the given locale's thousand grouping (through\n * `toLocaleString`); every other value — including `null` — String-coerces.\n * `null` therefore renders as the literal string `'null'`, matching\n * `String(value)` exactly, so a resolved `null` is visible in the output\n * rather than silently empty. An\n * invalid BCP-47 `locale` tag throws a `RangeError` from the underlying\n * `toLocaleString` call when `value` is a finite number — this is a caller\n * error (an invalid locale argument), by design, and is not caught here.\n *\n * @param value - The resolved value to format\n * @param locale - The locale used for finite-number formatting\n * @returns The formatted string\n *\n * @example\n * ```ts\n * import { formatValue } from '@src/core'\n *\n * formatValue(5010, 'en-US') // '5,010'\n * formatValue(null, 'en-US') // 'null'\n * ```\n */\nexport function formatValue(value: unknown, locale: string): string {\n\tif (isFiniteNumber(value)) return value.toLocaleString(locale)\n\treturn String(value)\n}\n\n/**\n * Resolves a field path against a fill-values record, refusing any path that\n * touches a prototype-pollution-unsafe segment.\n *\n * @remarks\n * A prototype-pollution guard shared by `fillTemplate` and `Template#validate`\n * so the two stay in lockstep: `path` normalizes to a segment array (a bare\n * string `path` becomes a single-segment array); if ANY segment appears in\n * `UNSAFE_FIELD_SEGMENTS` (`'__proto__'`, `'constructor'`, `'prototype'`), the\n * lookup is refused and `undefined` is returned WITHOUT ever calling\n * `resolveField` — a path like `['__proto__', 'polluted']` can never reach\n * the record's actual prototype chain through this function. Every other\n * path resolves through `@orkestrel/contract`'s `resolveField`.\n *\n * @param record - The fill-values record to resolve against\n * @param path - The field path — a single segment or a segment array\n * @returns The resolved value, or `undefined` when unresolved or the path is unsafe\n *\n * @example\n * ```ts\n * import { resolveSafeField } from '@src/core'\n *\n * resolveSafeField({ a: { b: 1 } }, ['a', 'b']) // 1\n * resolveSafeField({}, ['__proto__', 'polluted']) // undefined\n * ```\n */\nexport function resolveSafeField(record: TemplateFillValues, path: FieldPath): unknown {\n\tconst segments = Array.isArray(path) ? path : [path]\n\tif (segments.some((segment) => UNSAFE_FIELD_SEGMENTS.includes(segment))) return undefined\n\treturn resolveField(record, path)\n}\n\n/**\n * Resolves one `{{name}}` token against the declared placeholders and the\n * fill-values record.\n *\n * @remarks\n * The single implementation of the token rule `fillTemplate` and\n * `Template#validate` both apply, so the two can never drift: the declared\n * {@link TemplatePlaceholder} sharing the token's `name` (exact match)\n * supplies its `path`, falling back to the token split on `.`; the value\n * resolves through `resolveSafeField`, so any segment in\n * `UNSAFE_FIELD_SEGMENTS` yields `undefined` without ever calling\n * `resolveField`; `required` is `true` for an undeclared token and for a\n * declared placeholder whose `required` is not `false`. The token is passed\n * already trimmed. `fallback` is not applied here — it is read from\n * `declared` by each caller, because `fill` substitutes it and `validate`\n * only counts it.\n *\n * @param record - The fill-values record the token resolves against\n * @param placeholders - The declared placeholders the token matches by name\n * @param token - The trimmed token text, without its `{{` / `}}` delimiters\n * @returns The {@link TemplateTokenResolution} for the token\n *\n * @example\n * ```ts\n * import { resolveToken } from '@src/core'\n *\n * resolveToken({ name: 'Ada' }, [], 'name').value // 'Ada'\n * resolveToken({}, [{ name: 'nickname', required: false }], 'nickname').required // false\n * ```\n */\nexport function resolveToken(\n\trecord: TemplateFillValues,\n\tplaceholders: readonly TemplatePlaceholder[],\n\ttoken: string,\n): TemplateTokenResolution {\n\tconst declared = placeholders.find((placeholder) => placeholder.name === token)\n\tconst path = declared?.path ?? token.split('.')\n\treturn {\n\t\tvalue: resolveSafeField(record, path),\n\t\tdeclared,\n\t\trequired: declared === undefined || declared.required !== false,\n\t}\n}\n\n/**\n * Substitutes every `{{name}}` token in `content` in a single pass.\n *\n * @remarks\n * Uses a fresh `RegExp` clone of `FILL_PATTERN` per call (never sharing its\n * `lastIndex`) and a single `String#replace` scan — substituted output is\n * never re-scanned. Each token resolves through `resolveToken`, the one rule\n * `Template#validate` also applies: the matching declared\n * {@link TemplatePlaceholder} (exact `name`) supplies its `path` (falling\n * back to the token split on `.`); ANY path segment in `UNSAFE_FIELD_SEGMENTS`\n * makes the token unresolved without ever calling `resolveField` (a\n * prototype-pollution guard). A resolved value formats through `formatValue`; an\n * unresolved value falls back to the placeholder's `fallback` when declared;\n * otherwise `options.missing` governs — `'literal'` re-emits the original\n * `{{name}}` text, `'empty'` emits `''`, and `'error'` emits `''` for every\n * token but collects EVERY unresolved required token (an undeclared token, or\n * a declared token with `required !== false`) and throws one\n * {@link TemplateError} coded `MISSING` listing them all, in first-appearance\n * order, once the scan completes. An escaped `\\{{` emits a literal `{{`.\n *\n * Called with no declared `placeholders` and `{ missing: 'empty' }`, this is a\n * bare interpolation over `content` — every token resolves by dotted path\n * against the values record and every unresolved token emits `''`.\n * `FILL_PATTERN`'s token class (`[^{}]`) excludes `{`, so a token containing\n * `{` never matches and the surrounding `{{` stays literal.\n *\n * @param content - The template content carrying `{{name}}` tokens\n * @param values - The values tokens resolve against\n * @param options - `missing` (default `'error'`), `locale` (default `'en-US'`), and the declared `placeholders` (default none) tokens resolve against\n * @returns The substituted content\n *\n * @example\n * ```ts\n * import { fillTemplate } from '@src/core'\n *\n * fillTemplate('Hi {{name}}', { name: 'Ada' }) // 'Hi Ada'\n * fillTemplate('Limit {{limit}}', { limit: 5010 }, { missing: 'empty' }) // 'Limit 5,010'\n * ```\n */\nexport function fillTemplate(\n\tcontent: string,\n\tvalues?: TemplateFillValues,\n\toptions?: TemplateFillContext,\n): string {\n\tconst placeholders = options?.placeholders ?? []\n\tconst missing = options?.missing ?? DEFAULT_MISSING_POLICY\n\tconst locale = options?.locale ?? DEFAULT_LOCALE\n\tconst record = values ?? {}\n\n\tconst missingNames: string[] = []\n\tconst seen = new Set<string>()\n\n\tconst pattern = new RegExp(FILL_PATTERN.source, FILL_PATTERN.flags)\n\tconst result = content.replace(pattern, (matchText: string, rawToken: string | undefined) => {\n\t\tif (rawToken === undefined) return '{{'\n\t\tconst token = rawToken.trim()\n\n\t\tconst { value, declared, required } = resolveToken(record, placeholders, token)\n\n\t\tif (value !== undefined) return formatValue(value, locale)\n\t\tif (declared?.fallback !== undefined) return formatValue(declared.fallback, locale)\n\n\t\tif (missing === 'literal') return matchText\n\t\tif (missing === 'empty') return ''\n\n\t\tif (required && !seen.has(token)) {\n\t\t\tseen.add(token)\n\t\t\tmissingNames.push(token)\n\t\t}\n\t\treturn ''\n\t})\n\n\tif (missing === 'error' && missingNames.length > 0) {\n\t\tthrow new TemplateError(\n\t\t\t'MISSING',\n\t\t\t`Missing required placeholder(s): ${missingNames.join(', ')}`,\n\t\t\t{ missing: missingNames },\n\t\t)\n\t}\n\n\treturn result\n}\n","import type { ContractShape } from '@orkestrel/contract'\nimport type { TemplatePlaceholder } from './types.js'\nimport { objectShape, optionalShape, stringShape } from '@orkestrel/contract'\n\n// The templates shape-value inventory — every function here builds an\n// `@orkestrel/contract` shape from declared template data. Shapers sit above\n// the `helpers.ts` leaf pair: they consume it, and it never consumes them.\n\n/**\n * Builds the `@orkestrel/contract` object shape describing a template's\n * declared placeholders.\n *\n * @remarks\n * Each placeholder becomes a `stringShape` carrying its `description`;\n * `required === false` wraps it in `optionalShape`. Used by `Template` to\n * compile its `parameters()` contract once per instance.\n *\n * @param placeholders - The declared placeholders to shape\n * @returns The contract shape for `createContract`\n *\n * @example\n * ```ts\n * import { placeholderShape } from '@src/core'\n * import { createContract } from '@orkestrel/contract'\n *\n * const contract = createContract(placeholderShape([{ name: 'city' }]))\n * ```\n */\nexport function placeholderShape(placeholders: readonly TemplatePlaceholder[]): ContractShape {\n\tconst properties: Record<string, ContractShape> = {}\n\tfor (const placeholder of placeholders) {\n\t\tconst description = placeholder.description\n\t\tconst field = stringShape({\n\t\t\t...(description !== undefined ? { description } : {}),\n\t\t})\n\t\tproperties[placeholder.name] = placeholder.required === false ? optionalShape(field) : field\n\t}\n\treturn objectShape(properties)\n}\n","import type { ContractInterface } from '@orkestrel/contract'\nimport type {\n\tMissingPolicy,\n\tTemplateDefinition,\n\tTemplateFillOptions,\n\tTemplateFillValues,\n\tTemplateInterface,\n\tTemplateOptions,\n\tTemplatePlaceholder,\n\tTemplateValidationResult,\n} from '../types.js'\nimport { createContract, schemaToParameters } from '@orkestrel/contract'\nimport { DEFAULT_LOCALE, DEFAULT_MISSING_POLICY, FILL_PATTERN } from '../constants.js'\nimport { fillTemplate, resolveToken } from '../helpers.js'\nimport { placeholderShape } from '../shapers.js'\nimport { TemplateError } from '../errors.js'\n\n/**\n * Represents a named, versionable template — `{{name}}` tokens in `content`,\n * filled against a values record.\n *\n * @remarks\n * `missing` / `locale` seed this instance's default {@link TemplateFillOptions},\n * overridable per `fill` call. Its `parameters()` contract (built from\n * `placeholders` through `placeholderShape`) compiles once, in the constructor.\n *\n * @throws {@link TemplateError} Thrown when `options.placeholders` declares a duplicate `name` or an empty `path` (coded `INVALID`)\n *\n * @example\n * ```ts\n * const greeting = new Template({ name: 'greeting', content: 'Hi {{name}}' })\n * greeting.fill({ name: 'Ada' }) // 'Hi Ada'\n * ```\n */\nexport class Template implements TemplateInterface {\n\treadonly #missing: MissingPolicy\n\treadonly #locale: string\n\treadonly #contract: ContractInterface<unknown>\n\treadonly id: string\n\treadonly name: string\n\treadonly content: string\n\treadonly placeholders: readonly TemplatePlaceholder[]\n\treadonly summary?: string\n\treadonly description?: string\n\treadonly category?: string\n\treadonly tags?: readonly string[]\n\n\tconstructor(options: TemplateOptions) {\n\t\tconst placeholders = options.placeholders ?? []\n\t\tconst seenNames = new Set<string>()\n\t\tfor (const placeholder of placeholders) {\n\t\t\tif (seenNames.has(placeholder.name)) {\n\t\t\t\tthrow new TemplateError('INVALID', `Duplicate placeholder name: ${placeholder.name}`, {\n\t\t\t\t\tname: placeholder.name,\n\t\t\t\t})\n\t\t\t}\n\t\t\tseenNames.add(placeholder.name)\n\t\t\tif (Array.isArray(placeholder.path) && placeholder.path.length === 0) {\n\t\t\t\tthrow new TemplateError(\n\t\t\t\t\t'INVALID',\n\t\t\t\t\t`Placeholder path must not be empty: ${placeholder.name}`,\n\t\t\t\t\t{ name: placeholder.name },\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\tthis.id = typeof options.id === 'string' ? options.id : crypto.randomUUID()\n\t\tthis.name = options.name\n\t\tthis.content = options.content\n\t\tthis.placeholders = placeholders\n\t\tif (options.summary !== undefined) this.summary = options.summary\n\t\tif (options.description !== undefined) this.description = options.description\n\t\tif (options.category !== undefined) this.category = options.category\n\t\tif (options.tags !== undefined) this.tags = options.tags\n\t\tthis.#missing = options.missing ?? DEFAULT_MISSING_POLICY\n\t\tthis.#locale = options.locale ?? DEFAULT_LOCALE\n\t\tthis.#contract = createContract(placeholderShape(this.placeholders))\n\t}\n\n\t/**\n\t * Returns the plain, JSON-serializable data this template carries.\n\t *\n\t * @returns The {@link TemplateDefinition} record\n\t *\n\t * @example\n\t * ```ts\n\t * const instance = new Template({ name: 'greeting', content: 'Hi {{name}}' })\n\t * instance.definition().name // 'greeting'\n\t * ```\n\t */\n\tdefinition(): TemplateDefinition {\n\t\treturn {\n\t\t\tid: this.id,\n\t\t\tname: this.name,\n\t\t\tcontent: this.content,\n\t\t\tplaceholders: this.placeholders,\n\t\t\t...(this.summary !== undefined ? { summary: this.summary } : {}),\n\t\t\t...(this.description !== undefined ? { description: this.description } : {}),\n\t\t\t...(this.category !== undefined ? { category: this.category } : {}),\n\t\t\t...(this.tags !== undefined ? { tags: this.tags } : {}),\n\t\t}\n\t}\n\n\t/**\n\t * Substitutes every `{{name}}` token in `content` against `values`.\n\t *\n\t * @param values - The values tokens resolve against\n\t * @param options - Per-call overrides for this instance's `missing` / `locale` defaults\n\t * @returns The substituted content\n\t * @throws {@link TemplateError} Thrown when a required placeholder stays unresolved under the `'error'` policy (coded `MISSING`)\n\t *\n\t * @example\n\t * ```ts\n\t * const instance = new Template({ name: 'greeting', content: 'Hi {{name}}' })\n\t * instance.fill({ name: 'Ada' }) // 'Hi Ada'\n\t * ```\n\t */\n\tfill(values?: TemplateFillValues, options?: TemplateFillOptions): string {\n\t\treturn fillTemplate(this.content, values, {\n\t\t\tmissing: options?.missing ?? this.#missing,\n\t\t\tlocale: options?.locale ?? this.#locale,\n\t\t\tplaceholders: this.placeholders,\n\t\t})\n\t}\n\n\t/**\n\t * Reports which required placeholders would stay unresolved, and which\n\t * `values` keys go unused, without producing output.\n\t *\n\t * @remarks\n\t * Content-token driven: scans `this.content` for every `{{name}}` token\n\t * (skipping escaped `\\{{` matches) the same way `fill` does, so `validate`\n\t * predicts `fill`'s `'error'`-{@link MissingPolicy} outcome exactly — a\n\t * token reported here as missing is precisely a token that would throw\n\t * under `fill(values, { missing: 'error' })`. For each distinct token\n\t * (first-appearance order, trimmed): `resolveToken` applies the one shared\n\t * token rule `fill` also applies — a declared {@link TemplatePlaceholder}\n\t * sharing its `name` supplies `path` (falling back to the token split on\n\t * `.`), and the value resolves through `resolveSafeField`. The token is `missing`\n\t * only when the value is unresolved AND no `fallback` is declared AND the\n\t * placeholder is required (`required !== false`, including undeclared\n\t * tokens). `extra` lists every `values` key with no declared placeholder.\n\t *\n\t * @param values - The values to check\n\t * @returns The {@link TemplateValidationResult}\n\t *\n\t * @example\n\t * ```ts\n\t * const instance = new Template({\n\t * \tname: 'greeting',\n\t * \tcontent: 'Hi {{name}}',\n\t * \tplaceholders: [{ name: 'name' }],\n\t * })\n\t * instance.validate({}).missing // ['name']\n\t * ```\n\t */\n\tvalidate(values?: TemplateFillValues): TemplateValidationResult {\n\t\tconst record = values ?? {}\n\t\tconst missing: string[] = []\n\t\tconst seen = new Set<string>()\n\n\t\tconst pattern = new RegExp(FILL_PATTERN.source, FILL_PATTERN.flags)\n\t\tfor (const match of this.content.matchAll(pattern)) {\n\t\t\tconst rawToken = match[1]\n\t\t\tif (rawToken === undefined) continue\n\t\t\tconst token = rawToken.trim()\n\t\t\tif (seen.has(token)) continue\n\t\t\tseen.add(token)\n\n\t\t\tconst { value, declared, required } = resolveToken(record, this.placeholders, token)\n\n\t\t\tif (value === undefined && declared?.fallback === undefined && required) {\n\t\t\t\tmissing.push(token)\n\t\t\t}\n\t\t}\n\n\t\tconst declaredNames = new Set(this.placeholders.map((placeholder) => placeholder.name))\n\t\tconst extra = Object.keys(record).filter((key) => !declaredNames.has(key))\n\n\t\treturn { valid: missing.length === 0, missing, extra }\n\t}\n\n\t/**\n\t * Projects this template's placeholders to the open tool-parameters record\n\t * shape.\n\t *\n\t * @returns The compiled parameters record, or `undefined` when `schemaToParameters` yields none\n\t *\n\t * @example\n\t * ```ts\n\t * const instance = new Template({\n\t * \tname: 'greeting',\n\t * \tcontent: 'Hi {{name}}',\n\t * \tplaceholders: [{ name: 'name' }],\n\t * })\n\t * instance.parameters()\n\t * ```\n\t */\n\tparameters(): Readonly<Record<string, unknown>> | undefined {\n\t\treturn schemaToParameters(this.#contract.schema)\n\t}\n}\n","import type {\n\tMissingPolicy,\n\tTemplateFillValues,\n\tTemplateFillOptions,\n\tTemplateInterface,\n\tTemplateManagerEventMap,\n\tTemplateManagerInterface,\n\tTemplateManagerOptions,\n\tTemplateOptions,\n\tTemplateQuery,\n\tTemplateRegisterOptions,\n\tTemplateValidationResult,\n} from '../types.js'\nimport type { EmitterInterface } from '@orkestrel/emitter'\nimport { Emitter } from '@orkestrel/emitter'\nimport { DEFAULT_LOCALE, DEFAULT_MISSING_POLICY } from '../constants.js'\nimport { TemplateError } from '../errors.js'\nimport { Template } from './Template.js'\n\n/**\n * Represents the template registry — a self-owning, id-keyed record-holder for the\n * {@link TemplateInterface} instances a consumer registers, looks up, fills,\n * and validates by id, with singular/plural accessors, batch `remove`\n * overloads, and emitter ownership.\n *\n * @remarks\n * `register` accepts either a constructed {@link TemplateInterface} (kept\n * as-is, including its own `missing` / `locale` defaults) or a plain\n * {@link TemplateOptions} bag — constructed into a `Template` with this\n * manager's `missing` / `locale` defaults applied wherever the bag omits\n * them. A duplicate `id` throws a {@link TemplateError} coded `CONFLICT`\n * unless `options.replace` is `true`, in which case the existing entry is\n * overwritten. `options.templates` SEEDS the registry at construction\n * WITHOUT emitting `register` — only calls to `register` after construction\n * emit. The batch `remove(ids)` form removes every present id and returns\n * `true` only when every listed id was present.\n *\n * @example\n * ```ts\n * import { TemplateManager } from '@src/core'\n *\n * const manager = new TemplateManager()\n * const instance = manager.register({ name: 'greeting', content: 'Hi {{name}}' })\n * manager.fill(instance.id, { name: 'Ada' }) // 'Hi Ada'\n * ```\n */\nexport class TemplateManager implements TemplateManagerInterface {\n\treadonly #templates = new Map<string, TemplateInterface>()\n\treadonly #emitter: Emitter<TemplateManagerEventMap>\n\treadonly #missing: MissingPolicy\n\treadonly #locale: string\n\n\tconstructor(options?: TemplateManagerOptions) {\n\t\tconst on = options?.on\n\t\tconst error = options?.error\n\t\tthis.#emitter = new Emitter<TemplateManagerEventMap>({\n\t\t\t...(on !== undefined ? { on } : {}),\n\t\t\t...(error !== undefined ? { error } : {}),\n\t\t})\n\t\tthis.#missing = options?.missing ?? DEFAULT_MISSING_POLICY\n\t\tthis.#locale = options?.locale ?? DEFAULT_LOCALE\n\t\tfor (const template of options?.templates ?? []) {\n\t\t\tconst instance = this.#instantiate(template)\n\t\t\tthis.#templates.set(instance.id, instance)\n\t\t}\n\t}\n\n\tget emitter(): EmitterInterface<TemplateManagerEventMap> {\n\t\treturn this.#emitter\n\t}\n\n\tget count(): number {\n\t\treturn this.#templates.size\n\t}\n\n\t/**\n\t * Registers a template — a constructed {@link TemplateInterface} (kept\n\t * as-is) or a plain {@link TemplateOptions} bag (constructed into a\n\t * `Template` with this manager's `missing` / `locale` defaults applied\n\t * wherever the bag omits them).\n\t *\n\t * @param template - The template instance or options to register\n\t * @param options - `replace` — overwrite an existing entry sharing the same id instead of throwing\n\t * @returns The registered {@link TemplateInterface}\n\t * @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`)\n\t *\n\t * @example\n\t * ```ts\n\t * const instance = manager.register({ id: 'greeting', name: 'greeting', content: 'Hi {{name}}' })\n\t * ```\n\t */\n\tregister(\n\t\ttemplate: TemplateInterface | TemplateOptions,\n\t\toptions?: TemplateRegisterOptions,\n\t): TemplateInterface {\n\t\tconst instance = this.#instantiate(template)\n\t\tconst existing = this.#templates.get(instance.id)\n\t\tif (existing !== undefined && options?.replace !== true) {\n\t\t\tthrow new TemplateError('CONFLICT', `Template already registered: ${instance.id}`, {\n\t\t\t\tid: instance.id,\n\t\t\t})\n\t\t}\n\t\tthis.#templates.set(instance.id, instance)\n\t\tthis.#emitter.emit('register', instance)\n\t\treturn instance\n\t}\n\n\t/**\n\t * Returns one registered {@link TemplateInterface} by id.\n\t *\n\t * @param id - The template id\n\t * @returns The registered {@link TemplateInterface}, or `undefined` when `id` is unregistered\n\t */\n\ttemplate(id: string): TemplateInterface | undefined {\n\t\treturn this.#templates.get(id)\n\t}\n\n\t/**\n\t * Lists every registered template.\n\t *\n\t * @returns A snapshot array of every registered {@link TemplateInterface}\n\t */\n\ttemplates(): readonly TemplateInterface[] {\n\t\treturn [...this.#templates.values()]\n\t}\n\n\t/**\n\t * Filters registered templates by name / category / tag — every supplied\n\t * field must match (logical AND).\n\t *\n\t * @param query - The {@link TemplateQuery} to filter by; omit for every registered template\n\t * @returns The matching templates\n\t */\n\tfind(query?: TemplateQuery): readonly TemplateInterface[] {\n\t\tif (query === undefined) return this.templates()\n\t\treturn this.templates().filter((instance) => {\n\t\t\tif (query.name !== undefined && instance.name !== query.name) return false\n\t\t\tif (query.category !== undefined && instance.category !== query.category) return false\n\t\t\tif (query.tag !== undefined && !(instance.tags ?? []).includes(query.tag)) return false\n\t\t\treturn true\n\t\t})\n\t}\n\n\t/**\n\t * Tests whether a template id is registered.\n\t *\n\t * @param id - The template id\n\t * @returns True if `id` is registered; false otherwise\n\t */\n\thas(id: string): boolean {\n\t\treturn this.#templates.has(id)\n\t}\n\n\t/**\n\t * Removes one, several, or every registered template.\n\t *\n\t * @remarks\n\t * `remove()` removes every registered template, emitting `remove` once per\n\t * instance. `remove(id)` removes one template by id, emitting `remove` and\n\t * returning `true` when it existed, `false` otherwise. `remove(ids)`\n\t * removes every listed id that is present, emitting `remove` once per\n\t * removed instance, and returns `true` only when every listed id was\n\t * present.\n\t *\n\t * @param target - Omit to remove all, a single id, or a list of ids\n\t * @returns `boolean` for the single-id / list-of-ids forms; `void` for the remove-all form\n\t */\n\t// `readonly string[]` is not assignable to `id: string`, so a list resolves to the\n\t// batch signature whatever order the signatures are declared in.\n\tremove(ids: readonly string[]): boolean\n\tremove(id: string): boolean\n\tremove(): void\n\tremove(target?: string | readonly string[]): boolean | void {\n\t\tif (target === undefined) {\n\t\t\tfor (const instance of this.#templates.values()) this.#emitter.emit('remove', instance)\n\t\t\tthis.#templates.clear()\n\t\t\treturn\n\t\t}\n\t\tif (typeof target === 'string') {\n\t\t\tconst instance = this.#templates.get(target)\n\t\t\tif (instance === undefined) return false\n\t\t\tthis.#templates.delete(target)\n\t\t\tthis.#emitter.emit('remove', instance)\n\t\t\treturn true\n\t\t}\n\t\tlet all = true\n\t\tfor (const id of target) {\n\t\t\tconst instance = this.#templates.get(id)\n\t\t\tif (instance === undefined) {\n\t\t\t\tall = false\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tthis.#templates.delete(id)\n\t\t\tthis.#emitter.emit('remove', instance)\n\t\t}\n\t\treturn all\n\t}\n\n\t/** Removes every registered template, emitting `clear`. */\n\tclear(): void {\n\t\tthis.#templates.clear()\n\t\tthis.#emitter.emit('clear')\n\t}\n\n\t/**\n\t * Tears down the registry: drops every registered template and destroys the\n\t * owned emitter. Idempotent.\n\t *\n\t * @remarks\n\t * Teardown is not an observable registry operation and the emitter is being\n\t * released, so this emits neither `clear` nor `remove`. The emitter is torn\n\t * down last, after the registry is dropped.\n\t *\n\t * @example\n\t * ```ts\n\t * const manager = new TemplateManager()\n\t * manager.destroy()\n\t * manager.emitter.destroyed // true\n\t * ```\n\t */\n\tdestroy(): void {\n\t\tthis.#templates.clear()\n\t\tthis.#emitter.destroy()\n\t}\n\n\t/**\n\t * Fills a registered template by id.\n\t *\n\t * @param id - The template id\n\t * @param values - The values tokens resolve against\n\t * @param options - Per-call overrides for the template's `missing` / `locale` defaults\n\t * @returns The substituted content\n\t * @throws {@link TemplateError} coded `NOTFOUND` when `id` is unknown\n\t * @throws {@link TemplateError} Thrown when a required placeholder stays unresolved under the `'error'` policy (coded `MISSING`)\n\t */\n\tfill(id: string, values?: TemplateFillValues, options?: TemplateFillOptions): string {\n\t\treturn this.#require(id).fill(values, options)\n\t}\n\n\t/**\n\t * Validates values against a registered template by id.\n\t *\n\t * @param id - The template id\n\t * @param values - The values to check\n\t * @returns The {@link TemplateValidationResult}\n\t * @throws {@link TemplateError} coded `NOTFOUND` when `id` is unknown\n\t */\n\tvalidate(id: string, values?: TemplateFillValues): TemplateValidationResult {\n\t\treturn this.#require(id).validate(values)\n\t}\n\n\t/**\n\t * Projects a registered template's parameters by id.\n\t *\n\t * @param id - The template id\n\t * @returns The compiled parameters record, or `undefined` when the template has none\n\t * @throws {@link TemplateError} coded `NOTFOUND` when `id` is unknown\n\t */\n\tparameters(id: string): Readonly<Record<string, unknown>> | undefined {\n\t\treturn this.#require(id).parameters()\n\t}\n\n\t#instantiate(template: TemplateInterface | TemplateOptions): TemplateInterface {\n\t\tif (this.#isInstance(template)) return template\n\t\treturn new Template({\n\t\t\t...template,\n\t\t\tmissing: template.missing ?? this.#missing,\n\t\t\tlocale: template.locale ?? this.#locale,\n\t\t})\n\t}\n\n\t// A TemplateOptions bag is plain data with no `fill` / `validate` /\n\t// `parameters` methods; a TemplateInterface instance always exposes all three.\n\t#isInstance(template: TemplateInterface | TemplateOptions): template is TemplateInterface {\n\t\treturn (\n\t\t\t'fill' in template &&\n\t\t\ttypeof template.fill === 'function' &&\n\t\t\t'validate' in template &&\n\t\t\ttypeof template.validate === 'function' &&\n\t\t\t'parameters' in template &&\n\t\t\ttypeof template.parameters === 'function'\n\t\t)\n\t}\n\n\t// Every by-id operation that needs a template to proceed shares this lookup.\n\t// The `template` accessor deliberately does not, and returns `undefined`.\n\t#require(id: string): TemplateInterface {\n\t\tconst instance = this.#templates.get(id)\n\t\tif (instance === undefined) {\n\t\t\tthrow new TemplateError('NOTFOUND', `Unknown template id: ${id}`, { id })\n\t\t}\n\t\treturn instance\n\t}\n}\n","import type {\n\tTemplateInterface,\n\tTemplateManagerInterface,\n\tTemplateManagerOptions,\n\tTemplateOptions,\n} from './types.js'\nimport { Template } from './templates/Template.js'\nimport { TemplateManager } from './templates/TemplateManager.js'\n\n/**\n * Creates a template.\n *\n * @param options - The template's `name` / `content`, an optional `id`\n * (defaults to a generated UUID), `placeholders`, catalog metadata, and\n * `missing` / `locale` fill defaults\n * @returns A working {@link TemplateInterface}\n * @throws {@link TemplateError} Thrown when `options.placeholders` declares a duplicate `name` or an empty `path` (coded `INVALID`)\n *\n * @example\n * ```ts\n * import { createTemplate } from '@src/core'\n *\n * const greeting = createTemplate({ name: 'greeting', content: 'Hi {{name}}' })\n * greeting.fill({ name: 'Ada' }) // 'Hi Ada'\n * ```\n */\nexport function createTemplate(options: TemplateOptions): TemplateInterface {\n\treturn new Template(options)\n}\n\n/**\n * Creates a template registry.\n *\n * @param options - Optional initial `templates` seed collection and\n * manager-wide `missing` / `locale` fill defaults, emitter `on` hooks, and\n * an `error` handler\n * @returns A working {@link TemplateManagerInterface}\n * @throws {@link TemplateError} Thrown when a seeded `options.templates` bag declares a duplicate placeholder `name` or an empty `path` (coded `INVALID`)\n *\n * @example\n * ```ts\n * import { createTemplateManager } from '@src/core'\n *\n * const templates = createTemplateManager({\n * \ttemplates: [{ id: 'greeting', name: 'greeting', content: 'Hi {{name}}' }],\n * })\n * templates.fill('greeting', { name: 'Ada' }) // 'Hi Ada'\n * ```\n */\nexport function createTemplateManager(options?: TemplateManagerOptions): TemplateManagerInterface {\n\treturn new TemplateManager(options)\n}\n"],"mappings":"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"}