@orkestrel/template 0.0.5 → 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.
- package/README.md +24 -4
- package/dist/src/core/index.cjs +147 -77
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +701 -598
- package/dist/src/core/index.d.ts +701 -598
- package/dist/src/core/index.js +147 -78
- package/dist/src/core/index.js.map +1 -1
- package/package.json +19 -15
package/dist/src/core/index.js
CHANGED
|
@@ -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
|
-
*
|
|
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
|
-
/**
|
|
25
|
+
/** Holds the default `missing` policy for `Template#fill` / `TemplateManager#fill` when unspecified. */
|
|
26
26
|
var DEFAULT_MISSING_POLICY = "error";
|
|
27
|
-
/**
|
|
27
|
+
/** Holds the default `locale` for `Template#fill` / `TemplateManager#fill` when unspecified. */
|
|
28
28
|
var DEFAULT_LOCALE = "en-US";
|
|
29
29
|
/**
|
|
30
|
-
*
|
|
31
|
-
* resolve ANY path containing one, treating the placeholder as
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
|
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.
|
|
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
|
-
*
|
|
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 (
|
|
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'`,
|
|
90
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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.
|
|
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
|
|
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
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
* class (`[^{}]`) excludes `{`,
|
|
164
|
-
*
|
|
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 =
|
|
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 (
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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`
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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):
|
|
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
|
-
* `.`)
|
|
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
|
|
369
|
-
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
|
407
|
-
*
|
|
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
|
|
419
|
-
*
|
|
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
|
|
495
|
+
get count() {
|
|
453
496
|
return this.#templates.size;
|
|
454
497
|
}
|
|
455
498
|
/**
|
|
456
|
-
*
|
|
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
|
-
*
|
|
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
|
-
|
|
486
|
-
if (instance === void 0) this.#throwNotFound(id);
|
|
487
|
-
return instance;
|
|
528
|
+
return this.#templates.get(id);
|
|
488
529
|
}
|
|
489
530
|
/**
|
|
490
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
555
|
+
* Tests whether a template id is registered.
|
|
515
556
|
*
|
|
516
557
|
* @param id - The template id
|
|
517
|
-
* @returns
|
|
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
|
-
|
|
576
|
+
let all = true;
|
|
536
577
|
for (const id of target) {
|
|
537
578
|
const instance = this.#templates.get(id);
|
|
538
|
-
if (instance === void 0)
|
|
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
|
|
586
|
+
return all;
|
|
543
587
|
}
|
|
544
|
-
/**
|
|
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
|
-
*
|
|
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
|
|
624
|
+
return this.#require(id).fill(values, options);
|
|
560
625
|
}
|
|
561
626
|
/**
|
|
562
|
-
*
|
|
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
|
|
635
|
+
return this.#require(id).validate(values);
|
|
571
636
|
}
|
|
572
637
|
/**
|
|
573
|
-
*
|
|
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
|
|
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
|
-
#
|
|
594
|
-
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|