@orkestrel/template 0.0.6 → 0.0.8

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.
@@ -1,11 +1,12 @@
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
- * Creates a template.
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
@@ -13,18 +14,31 @@ import { FieldPath } from '@orkestrel/contract';
13
14
  * @returns A working {@link TemplateInterface}
14
15
  * @throws {@link TemplateError} Thrown when `options.placeholders` declares a duplicate `name` or an empty `path` (coded `INVALID`)
15
16
  *
16
- * @example
17
+ * @example Create a template and a registry
17
18
  * ```ts
18
- * import { createTemplate } from '@src/core'
19
+ * import { createTemplate, createTemplateManager } from '@orkestrel/template'
19
20
  *
20
21
  * const greeting = createTemplate({ name: 'greeting', content: 'Hi {{name}}' })
21
22
  * greeting.fill({ name: 'Ada' }) // 'Hi Ada'
23
+ *
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
+ * ],
30
+ * })
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
22
35
  * ```
23
36
  */
24
37
  export declare function createTemplate(options: TemplateOptions): TemplateInterface;
25
38
 
26
39
  /**
27
- * Creates a template registry.
40
+ * Creates a working {@link TemplateManagerInterface}, optionally seeded with
41
+ * the templates the options carry, backed by the `TemplateManager` class.
28
42
  *
29
43
  * @param options - Optional initial `templates` seed collection and
30
44
  * manager-wide `missing` / `locale` fill defaults, emitter `on` hooks, and
@@ -44,10 +58,16 @@ import { FieldPath } from '@orkestrel/contract';
44
58
  */
45
59
  export declare function createTemplateManager(options?: TemplateManagerOptions): TemplateManagerInterface;
46
60
 
47
- /** Holds the default `locale` for `Template#fill` / `TemplateManager#fill` when unspecified. */
61
+ /**
62
+ * Holds `'en-US'`, the default `locale` for `Template#fill` /
63
+ * `TemplateManager#fill` when unspecified.
64
+ */
48
65
  export declare const DEFAULT_LOCALE = "en-US";
49
66
 
50
- /** Holds the default `missing` policy for `Template#fill` / `TemplateManager#fill` when unspecified. */
67
+ /**
68
+ * Holds `'error'`, the default `missing` policy for `Template#fill` /
69
+ * `TemplateManager#fill` when unspecified.
70
+ */
51
71
  export declare const DEFAULT_MISSING_POLICY: MissingPolicy;
52
72
 
53
73
  /**
@@ -55,14 +75,14 @@ import { FieldPath } from '@orkestrel/contract';
55
75
  * `Template#fill` and `Template#validate`.
56
76
  *
57
77
  * @remarks
58
- * Global-flagged, two-alternative pattern: a match of the FIRST alternative
78
+ * Global-flagged, two-alternative pattern: a match of the first alternative
59
79
  * (`\{{` — a literal backslash followed by `{{`) means "emit a literal
60
80
  * `{{`" — the escape hatch for content that must show `{{` without
61
81
  * triggering substitution. A match that instead populates capture group 1
62
82
  * (`\{{([^{}]+?)\}\}`) means "substitute the named token" — group 1 is the
63
- * RAW (untrimmed) token text between the braces; every call site trims it
83
+ * untrimmed token text between the braces; every call site trims it
64
84
  * (`token.trim()`) before using it as a lookup name, so `'{{ name }}'` still
65
- * resolves `'name'`. The pattern intentionally does NOT wrap the token in
85
+ * resolves `'name'`. The pattern deliberately does not wrap the token in
66
86
  * `\s*` — an unclosed `'{{' + ' '.repeat(n)` with no closing `}}` would
67
87
  * otherwise force the regex engine into catastrophic backtracking over the
68
88
  * whitespace run (O(n^2)); trimming after the match keeps the same
@@ -81,13 +101,13 @@ import { FieldPath } from '@orkestrel/contract';
81
101
  * never re-scanned. Each token resolves through `resolveToken`, the one rule
82
102
  * `Template#validate` also applies: the matching declared
83
103
  * {@link TemplatePlaceholder} (exact `name`) supplies its `path` (falling
84
- * back to the token split on `.`); ANY path segment in `UNSAFE_FIELD_SEGMENTS`
104
+ * back to the token split on `.`); any path segment in `UNSAFE_FIELD_SEGMENTS`
85
105
  * makes the token unresolved without ever calling `resolveField` (a
86
106
  * prototype-pollution guard). A resolved value formats through `formatValue`; an
87
107
  * unresolved value falls back to the placeholder's `fallback` when declared;
88
108
  * otherwise `options.missing` governs — `'literal'` re-emits the original
89
109
  * `{{name}}` text, `'empty'` emits `''`, and `'error'` emits `''` for every
90
- * token but collects EVERY unresolved required token (an undeclared token, or
110
+ * token but collects every unresolved required token (an undeclared token, or
91
111
  * a declared token with `required !== false`) and throws one
92
112
  * {@link TemplateError} coded `MISSING` listing them all, in first-appearance
93
113
  * order, once the scan completes. An escaped `\{{` emits a literal `{{`.
@@ -199,9 +219,9 @@ import { FieldPath } from '@orkestrel/contract';
199
219
  * @remarks
200
220
  * A prototype-pollution guard shared by `fillTemplate` and `Template#validate`
201
221
  * so the two stay in lockstep: `path` normalizes to a segment array (a bare
202
- * string `path` becomes a single-segment array); if ANY segment appears in
222
+ * string `path` becomes a single-segment array); if any segment appears in
203
223
  * `UNSAFE_FIELD_SEGMENTS` (`'__proto__'`, `'constructor'`, `'prototype'`), the
204
- * lookup is refused and `undefined` is returned WITHOUT ever calling
224
+ * lookup is refused and `undefined` is returned without ever calling
205
225
  * `resolveField` — a path like `['__proto__', 'polluted']` can never reach
206
226
  * the record's actual prototype chain through this function. Every other
207
227
  * path resolves through `@orkestrel/contract`'s `resolveField`.
@@ -254,7 +274,7 @@ import { FieldPath } from '@orkestrel/contract';
254
274
 
255
275
  /**
256
276
  * Represents a named, versionable template — `{{name}}` tokens in `content`,
257
- * filled against a values record.
277
+ * filled against a values record — implementing `TemplateInterface` exactly.
258
278
  *
259
279
  * @remarks
260
280
  * `missing` / `locale` seed this instance's default {@link TemplateFillOptions},
@@ -321,7 +341,7 @@ import { FieldPath } from '@orkestrel/contract';
321
341
  * token rule `fill` also applies — a declared {@link TemplatePlaceholder}
322
342
  * sharing its `name` supplies `path` (falling back to the token split on
323
343
  * `.`), and the value resolves through `resolveSafeField`. The token is `missing`
324
- * only when the value is unresolved AND no `fallback` is declared AND the
344
+ * only when the value is unresolved, no `fallback` is declared, and the
325
345
  * placeholder is required (`required !== false`, including undeclared
326
346
  * tokens). `extra` lists every `values` key with no declared placeholder.
327
347
  *
@@ -381,15 +401,16 @@ import { FieldPath } from '@orkestrel/contract';
381
401
  }
382
402
 
383
403
  /**
384
- * Represents an error thrown by the template layer.
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.
385
407
  *
386
408
  * @remarks
387
409
  * Thrown for: a required placeholder staying unresolved under the `error`
388
410
  * {@link MissingPolicy} (`MISSING`), an unknown template id
389
411
  * (`NOTFOUND`), `createTemplate` handed invalid data (`INVALID`), and
390
412
  * `TemplateManagerInterface#register` handed an id already present without
391
- * `options.replace` (`CONFLICT`). `context`, when present, carries the
392
- * offending id / name.
413
+ * `options.replace` (`CONFLICT`).
393
414
  */
394
415
  export declare class TemplateError extends Error {
395
416
  readonly code: TemplateErrorCode;
@@ -434,17 +455,13 @@ import { FieldPath } from '@orkestrel/contract';
434
455
  export declare type TemplateFillValues = Readonly<Record<string, unknown>>;
435
456
 
436
457
  /**
437
- * Declares the template contract — exact bijection with `Template`.
458
+ * Declares the template contract a consumer holds the readonly template
459
+ * record and the `definition`, `fill`, `validate`, and `parameters` calls
460
+ * over it.
438
461
  *
439
462
  * @remarks
440
- * `definition` returns the plain {@link TemplateDefinition} data. `fill`
441
- * substitutes every `{{name}}` token in `content` against `values`,
442
- * honoring `options.missing` for unresolved required placeholders. `validate`
443
- * reports which required placeholders are unresolved (`missing`) and which
444
- * supplied `values` keys are unused (`extra`) without producing output.
445
- * `parameters` projects this template's placeholders to the open
446
- * tool-parameters record shape (`schemaToParameters`'s return type from
447
- * `@orkestrel/contract`).
463
+ * `Template` implements this contract exactly, so the class exposes these
464
+ * members and no other public behavior.
448
465
  */
449
466
  export declare interface TemplateInterface {
450
467
  readonly id: string;
@@ -455,28 +472,53 @@ import { FieldPath } from '@orkestrel/contract';
455
472
  readonly description?: string;
456
473
  readonly category?: string;
457
474
  readonly tags?: readonly string[];
475
+ /** Returns the plain, JSON-serializable template data. */
458
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
+ */
459
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
+ */
460
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
+ */
461
502
  parameters(): Readonly<Record<string, unknown>> | undefined;
462
503
  }
463
504
 
464
505
  /**
465
- * Represents the template registry — a self-owning, id-keyed record-holder for the
466
- * {@link TemplateInterface} instances a consumer registers, looks up, fills,
467
- * and validates by id, with singular/plural accessors, batch `remove`
468
- * overloads, and emitter ownership.
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.
469
510
  *
470
511
  * @remarks
471
- * `register` accepts either a constructed {@link TemplateInterface} (kept
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
472
514
  * as-is, including its own `missing` / `locale` defaults) or a plain
473
515
  * {@link TemplateOptions} bag — constructed into a `Template` with this
474
516
  * manager's `missing` / `locale` defaults applied wherever the bag omits
475
517
  * them. A duplicate `id` throws a {@link TemplateError} coded `CONFLICT`
476
518
  * unless `options.replace` is `true`, in which case the existing entry is
477
- * overwritten. `options.templates` SEEDS the registry at construction
478
- * WITHOUT emitting `register` — only calls to `register` after construction
479
- * emit. The batch `remove(ids)` form removes every present id and returns
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
480
522
  * `true` only when every listed id was present.
481
523
  *
482
524
  * @example
@@ -524,8 +566,8 @@ import { FieldPath } from '@orkestrel/contract';
524
566
  */
525
567
  templates(): readonly TemplateInterface[];
526
568
  /**
527
- * Filters registered templates by name / category / tag — every supplied
528
- * field must match (logical AND).
569
+ * Filters registered templates by `name`, `category`, and `tag` — every
570
+ * supplied field must match.
529
571
  *
530
572
  * @param query - The {@link TemplateQuery} to filter by; omit for every registered template
531
573
  * @returns The matching templates
@@ -619,36 +661,67 @@ import { FieldPath } from '@orkestrel/contract';
619
661
  };
620
662
 
621
663
  /**
622
- * Declares the template registry — a self-owning, id-keyed record-holder with
623
- * singular/plural accessors and batch overloads.
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.
624
667
  *
625
668
  * @remarks
626
- * `register` accepts either a constructed {@link TemplateInterface} or a
627
- * plain {@link TemplateOptions} bag (constructed internally), and throws a
628
- * {@link TemplateError} coded `CONFLICT` when the id already exists unless
629
- * `options.replace` is `true`. `template` returns `undefined` for an unknown
630
- * id; `fill`, `validate`, and `parameters` throw `NOTFOUND` for one, because
631
- * each needs a template to proceed. `remove()` removes every registered
632
- * template. `remove`'s batch form removes every present id and reports
633
- * `true` only when all listed ids were present. `destroy` tears the registry
634
- * down — it drops every registered template, destroys the owned emitter, emits
635
- * nothing, and is idempotent.
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.
636
674
  */
637
675
  export declare interface TemplateManagerInterface {
638
676
  readonly emitter: EmitterInterface<TemplateManagerEventMap>;
639
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
+ */
640
688
  register(template: TemplateInterface | TemplateOptions, options?: TemplateRegisterOptions): TemplateInterface;
689
+ /** Returns one registered template by id, or `undefined` when the id is unregistered. */
641
690
  template(id: string): TemplateInterface | undefined;
691
+ /** Lists every registered template. */
642
692
  templates(): readonly TemplateInterface[];
693
+ /** Filters registered templates by `name`, `category`, and `tag` — every supplied field must match. */
643
694
  find(query?: TemplateQuery): readonly TemplateInterface[];
695
+ /** Reports whether a template with the given id is registered. */
644
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
+ */
645
707
  remove(ids: readonly string[]): boolean;
646
708
  remove(id: string): boolean;
647
709
  remove(): void;
710
+ /** Removes every registered template, emitting `clear`. */
648
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
+ */
649
719
  destroy(): void;
720
+ /** Fills a registered template by id. */
650
721
  fill(id: string, values?: TemplateFillValues, options?: TemplateFillOptions): string;
722
+ /** Validates values against a registered template by id. */
651
723
  validate(id: string, values?: TemplateFillValues): TemplateValidationResult;
724
+ /** Projects a registered template's parameters by id. */
652
725
  parameters(id: string): Readonly<Record<string, unknown>> | undefined;
653
726
  }
654
727
 
@@ -759,9 +832,9 @@ import { FieldPath } from '@orkestrel/contract';
759
832
  }
760
833
 
761
834
  /**
762
- * Lists the prototype-pollution-unsafe field-path segments — a fill lookup
763
- * refuses to resolve ANY path containing one, treating the placeholder as
764
- * unresolved.
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.
765
838
  */
766
839
  export declare const UNSAFE_FIELD_SEGMENTS: readonly string[];
767
840