@orkestrel/template 0.0.3 → 0.0.4
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.
|
@@ -592,7 +592,7 @@ export declare class TemplateManager implements TemplateManagerInterface {
|
|
|
592
592
|
* listener-error handler.
|
|
593
593
|
*/
|
|
594
594
|
export declare interface TemplateManagerOptions {
|
|
595
|
-
readonly templates?:
|
|
595
|
+
readonly templates?: ReadonlyArray<TemplateInterface | TemplateOptions>;
|
|
596
596
|
readonly missing?: MissingPolicy;
|
|
597
597
|
readonly locale?: string;
|
|
598
598
|
readonly on?: EmitterHooks<TemplateManagerEventMap>;
|
package/dist/src/core/index.d.ts
CHANGED
|
@@ -592,7 +592,7 @@ export declare class TemplateManager implements TemplateManagerInterface {
|
|
|
592
592
|
* listener-error handler.
|
|
593
593
|
*/
|
|
594
594
|
export declare interface TemplateManagerOptions {
|
|
595
|
-
readonly templates?:
|
|
595
|
+
readonly templates?: ReadonlyArray<TemplateInterface | TemplateOptions>;
|
|
596
596
|
readonly missing?: MissingPolicy;
|
|
597
597
|
readonly locale?: string;
|
|
598
598
|
readonly on?: EmitterHooks<TemplateManagerEventMap>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orkestrel/template",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "Stateful template registry and filler with typed placeholders. Part of the @orkestrel line.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"template",
|
|
@@ -49,10 +49,11 @@
|
|
|
49
49
|
"format": "oxfmt --config .oxfmtrc.json --write .",
|
|
50
50
|
"format:check": "oxfmt --config .oxfmtrc.json --check .",
|
|
51
51
|
"lint:check": "oxlint --config .oxlintrc.json --deny-warnings .",
|
|
52
|
-
"test": "npm run test:src && npm run test:policy && npm run test:guides",
|
|
52
|
+
"test": "npm run test:src && npm run test:policy && npm run test:config && npm run test:guides",
|
|
53
53
|
"test:src": "vitest run --config vite.config.ts --no-cache --reporter=dot --project src:core",
|
|
54
54
|
"test:src:core": "vitest run --config vite.config.ts --no-cache --reporter=dot --project src:core",
|
|
55
55
|
"test:policy": "vitest run --config vite.config.ts --no-cache --reporter=dot --project policy",
|
|
56
|
+
"test:config": "vitest run --config vite.config.ts --no-cache --reporter=dot --project config",
|
|
56
57
|
"test:guides": "vitest run --config vite.config.ts --reporter=dot --project guides",
|
|
57
58
|
"build": "npm run clean && npm run build:src",
|
|
58
59
|
"build:src": "npm run build:src:core",
|
|
@@ -60,13 +61,14 @@
|
|
|
60
61
|
"prepublishOnly": "npm run format:check && npm run lint:check && npm run check && npm run build && npm test"
|
|
61
62
|
},
|
|
62
63
|
"dependencies": {
|
|
63
|
-
"@orkestrel/contract": "^0.0.
|
|
64
|
-
"@orkestrel/emitter": "^0.0.
|
|
64
|
+
"@orkestrel/contract": "^0.0.12",
|
|
65
|
+
"@orkestrel/emitter": "^0.0.7"
|
|
65
66
|
},
|
|
66
67
|
"devDependencies": {
|
|
67
68
|
"@microsoft/api-extractor": "^7.58.12",
|
|
68
|
-
"@orkestrel/guide": "^0.0.
|
|
69
|
-
"@orkestrel/scaffold": "^0.0.
|
|
69
|
+
"@orkestrel/guide": "^0.0.11",
|
|
70
|
+
"@orkestrel/scaffold": "^0.0.38",
|
|
71
|
+
"@orkestrel/test": "^0.0.6",
|
|
70
72
|
"@types/node": "^26.1.2",
|
|
71
73
|
"@vitest/browser-playwright": "^4.1.10",
|
|
72
74
|
"oxfmt": "^0.61.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["#missing","#locale","#contract","#templates","#emitter","#missing","#locale","#instantiate","#throwNotFound","#isInstance"],"sources":["../../../src/core/constants.ts","../../../src/core/errors.ts","../../../src/core/helpers.ts","../../../src/core/Template.ts","../../../src/core/TemplateManager.ts","../../../src/core/factories.ts"],"sourcesContent":["import type { MissingPolicy } from './types.js'\n\n// Frozen default data for the template module (AGENTS §5 — constants are\n// UPPER_SNAKE_CASE data, the sole home for module-scope literal defaults).\n\n/**\n * The single-pass `{{name}}` substitution pattern shared by `Template#fill`\n * 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/** Default `missing` policy for `Template#fill` / `TemplateManager#fill` when unspecified. */\nexport const DEFAULT_MISSING_POLICY: MissingPolicy = 'error'\n\n/** Default `locale` for `Template#fill` / `TemplateManager#fill` when unspecified. */\nexport const DEFAULT_LOCALE = 'en-US'\n\n/**\n * Prototype-pollution-unsafe field-path segments — a fill lookup refuses to\n * resolve ANY path containing one, treating the placeholder as 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// AGENTS §12: misuse of the template layer `throw`s a `TemplateError`\n// carrying a machine-readable `code`, so a `catch` branches on `error.code`.\n\n/**\n * 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 * Narrow an unknown caught value to a {@link TemplateError}.\n *\n * @param value - The value to test (typically a `catch` binding)\n * @returns `true` when `value` is a {@link TemplateError}\n *\n * @example\n * ```ts\n * import { isTemplateError } from '@src/core'\n *\n * try {\n * \tmanager.template('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 { ContractShape, FieldPath } from '@orkestrel/contract'\nimport type { TemplateFillOptions, TemplateFillValues, TemplatePlaceholder } from './types.js'\nimport {\n\tisFiniteNumber,\n\tobjectShape,\n\toptionalShape,\n\tresolveField,\n\tstringShape,\n} 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 (AGENTS §5/§7) — 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 * Format 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 (via\n * `toLocaleString`); every other value — including `null` — String-coerces.\n * `null` therefore renders as the literal string `'null'`, intentionally\n * mirroring `interpolateMessage`'s coercion parity (see `fillTemplate`). 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 * Resolve 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 * Substitute 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. For each token: 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 via `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 * PARITY: called with no declared `placeholders` and `{ missing: 'empty' }`,\n * this reproduces `interpolateMessage` (`@src/core` sibling\n * `interpret`) vector-for-vector. KNOWN DIVERGENCE: `FILL_PATTERN`'s token\n * class (`[^{}]`) excludes `{`, where `interpolateMessage`'s (`[^}]`) allows\n * it — a token containing `{` therefore behaves differently here.\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?: TemplateFillOptions & { readonly placeholders?: readonly TemplatePlaceholder[] },\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 declared = placeholders.find((placeholder) => placeholder.name === token)\n\t\tconst path = declared?.path ?? token.split('.')\n\t\tconst value = resolveSafeField(record, path)\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\tconst required = declared === undefined || declared.required !== false\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\n/**\n * Build 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, placeholderShape, resolveSafeField } from './helpers.js'\nimport { TemplateError } from './errors.js'\n\n/**\n * A named, versionable template — `{{name}}` tokens in `content`, filled\n * 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` via `placeholderShape`) compiles once, in the constructor.\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 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\treadonly #missing: MissingPolicy\n\treadonly #locale: string\n\treadonly #contract: ContractInterface<unknown>\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 * 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 * Substitute 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 *\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 * Report 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): a declared {@link TemplatePlaceholder}\n\t * sharing its `name` supplies `path` (falling back to the token split on\n\t * `.`); the value resolves via `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 declared = this.placeholders.find((placeholder) => placeholder.name === token)\n\t\t\tconst path = declared?.path ?? token.split('.')\n\t\t\tconst resolved = resolveSafeField(record, path)\n\n\t\t\tconst required = declared === undefined || declared.required !== false\n\t\t\tif (resolved === 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 * Project 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\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 * 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 (AGENTS §9.1 singular/plural accessors, §9.2 batch\n * `remove` overloads, §13 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 is ALL-OR-NOTHING: any id absent from\n * the registry leaves the collection untouched and returns `false`.\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 size(): number {\n\t\treturn this.#templates.size\n\t}\n\n\t/**\n\t * Register 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 *\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?: { readonly replace?: boolean },\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 * Look up a registered template by id.\n\t *\n\t * @param id - The template id\n\t * @returns The registered {@link TemplateInterface}\n\t * @throws {@link TemplateError} coded `NOTFOUND` when `id` is unknown\n\t */\n\ttemplate(id: string): TemplateInterface {\n\t\tconst instance = this.#templates.get(id)\n\t\tif (instance === undefined) this.#throwNotFound(id)\n\t\treturn instance\n\t}\n\n\t/**\n\t * List 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 * Filter 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 * Test whether a template id is registered.\n\t *\n\t * @param id - The template id\n\t * @returns `true` when `id` is registered\n\t */\n\thas(id: string): boolean {\n\t\treturn this.#templates.has(id)\n\t}\n\n\t/**\n\t * Remove one, several, or every registered template (AGENTS §9.2 batch\n\t * overloads) — array overload declared first so a list resolves to the\n\t * batch form.\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)` is\n\t * ALL-OR-NOTHING: if any id in the list is unregistered, the collection is\n\t * left untouched and `false` is returned; otherwise every listed template\n\t * is removed (each emitting `remove`) and `true` is returned.\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\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\tfor (const id of target) if (!this.#templates.has(id)) return false\n\t\tfor (const id of target) {\n\t\t\tconst instance = this.#templates.get(id)\n\t\t\tif (instance === undefined) continue\n\t\t\tthis.#templates.delete(id)\n\t\t\tthis.#emitter.emit('remove', instance)\n\t\t}\n\t\treturn true\n\t}\n\n\t/** Remove 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 * Fill 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 */\n\tfill(id: string, values?: TemplateFillValues, options?: TemplateFillOptions): string {\n\t\treturn this.template(id).fill(values, options)\n\t}\n\n\t/**\n\t * Validate 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.template(id).validate(values)\n\t}\n\n\t/**\n\t * Project 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.template(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#throwNotFound(id: string): never {\n\t\tthrow new TemplateError('NOTFOUND', `Unknown template id: ${id}`, { id })\n\t}\n}\n","import type {\n\tTemplateInterface,\n\tTemplateManagerInterface,\n\tTemplateManagerOptions,\n\tTemplateOptions,\n} from './types.js'\nimport { Template } from './Template.js'\nimport { TemplateManager } from './TemplateManager.js'\n\n/**\n * Create 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 *\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 * Create 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 *\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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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["#missing","#locale","#contract","#templates","#emitter","#missing","#locale","#instantiate","#throwNotFound","#isInstance"],"sources":["../../../src/core/constants.ts","../../../src/core/errors.ts","../../../src/core/helpers.ts","../../../src/core/Template.ts","../../../src/core/TemplateManager.ts","../../../src/core/factories.ts"],"sourcesContent":["import type { MissingPolicy } from './types.js'\n\n// Frozen default data for the template module (AGENTS §5 — constants are\n// UPPER_SNAKE_CASE data, the sole home for module-scope literal defaults).\n\n/**\n * The single-pass `{{name}}` substitution pattern shared by `Template#fill`\n * 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/** Default `missing` policy for `Template#fill` / `TemplateManager#fill` when unspecified. */\nexport const DEFAULT_MISSING_POLICY: MissingPolicy = 'error'\n\n/** Default `locale` for `Template#fill` / `TemplateManager#fill` when unspecified. */\nexport const DEFAULT_LOCALE = 'en-US'\n\n/**\n * Prototype-pollution-unsafe field-path segments — a fill lookup refuses to\n * resolve ANY path containing one, treating the placeholder as 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// AGENTS §12: misuse of the template layer `throw`s a `TemplateError`\n// carrying a machine-readable `code`, so a `catch` branches on `error.code`.\n\n/**\n * 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 * Narrow an unknown caught value to a {@link TemplateError}.\n *\n * @param value - The value to test (typically a `catch` binding)\n * @returns `true` when `value` is a {@link TemplateError}\n *\n * @example\n * ```ts\n * import { isTemplateError } from '@src/core'\n *\n * try {\n * \tmanager.template('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 { ContractShape, FieldPath } from '@orkestrel/contract'\nimport type { TemplateFillOptions, TemplateFillValues, TemplatePlaceholder } from './types.js'\nimport {\n\tisFiniteNumber,\n\tobjectShape,\n\toptionalShape,\n\tresolveField,\n\tstringShape,\n} 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 (AGENTS §5/§7) — 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 * Format 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 (via\n * `toLocaleString`); every other value — including `null` — String-coerces.\n * `null` therefore renders as the literal string `'null'`, intentionally\n * mirroring `interpolateMessage`'s coercion parity (see `fillTemplate`). 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 * Resolve 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 * Substitute 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. For each token: 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 via `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 * PARITY: called with no declared `placeholders` and `{ missing: 'empty' }`,\n * this reproduces `interpolateMessage` (`@src/core` sibling\n * `interpret`) vector-for-vector. KNOWN DIVERGENCE: `FILL_PATTERN`'s token\n * class (`[^{}]`) excludes `{`, where `interpolateMessage`'s (`[^}]`) allows\n * it — a token containing `{` therefore behaves differently here.\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?: TemplateFillOptions & { readonly placeholders?: readonly TemplatePlaceholder[] },\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 declared = placeholders.find((placeholder) => placeholder.name === token)\n\t\tconst path = declared?.path ?? token.split('.')\n\t\tconst value = resolveSafeField(record, path)\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\tconst required = declared === undefined || declared.required !== false\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\n/**\n * Build 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, placeholderShape, resolveSafeField } from './helpers.js'\nimport { TemplateError } from './errors.js'\n\n/**\n * A named, versionable template — `{{name}}` tokens in `content`, filled\n * 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` via `placeholderShape`) compiles once, in the constructor.\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 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\treadonly #missing: MissingPolicy\n\treadonly #locale: string\n\treadonly #contract: ContractInterface<unknown>\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 * 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 * Substitute 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 *\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 * Report 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): a declared {@link TemplatePlaceholder}\n\t * sharing its `name` supplies `path` (falling back to the token split on\n\t * `.`); the value resolves via `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 declared = this.placeholders.find((placeholder) => placeholder.name === token)\n\t\t\tconst path = declared?.path ?? token.split('.')\n\t\t\tconst resolved = resolveSafeField(record, path)\n\n\t\t\tconst required = declared === undefined || declared.required !== false\n\t\t\tif (resolved === 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 * Project 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\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 * 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 (AGENTS §9.1 singular/plural accessors, §9.2 batch\n * `remove` overloads, §13 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 is ALL-OR-NOTHING: any id absent from\n * the registry leaves the collection untouched and returns `false`.\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 size(): number {\n\t\treturn this.#templates.size\n\t}\n\n\t/**\n\t * Register 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 *\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?: { readonly replace?: boolean },\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 * Look up a registered template by id.\n\t *\n\t * @param id - The template id\n\t * @returns The registered {@link TemplateInterface}\n\t * @throws {@link TemplateError} coded `NOTFOUND` when `id` is unknown\n\t */\n\ttemplate(id: string): TemplateInterface {\n\t\tconst instance = this.#templates.get(id)\n\t\tif (instance === undefined) this.#throwNotFound(id)\n\t\treturn instance\n\t}\n\n\t/**\n\t * List 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 * Filter 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 * Test whether a template id is registered.\n\t *\n\t * @param id - The template id\n\t * @returns `true` when `id` is registered\n\t */\n\thas(id: string): boolean {\n\t\treturn this.#templates.has(id)\n\t}\n\n\t/**\n\t * Remove one, several, or every registered template (AGENTS §9.2 batch\n\t * overloads) — array overload declared first so a list resolves to the\n\t * batch form.\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)` is\n\t * ALL-OR-NOTHING: if any id in the list is unregistered, the collection is\n\t * left untouched and `false` is returned; otherwise every listed template\n\t * is removed (each emitting `remove`) and `true` is returned.\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\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\tfor (const id of target) if (!this.#templates.has(id)) return false\n\t\tfor (const id of target) {\n\t\t\tconst instance = this.#templates.get(id)\n\t\t\tif (instance === undefined) continue\n\t\t\tthis.#templates.delete(id)\n\t\t\tthis.#emitter.emit('remove', instance)\n\t\t}\n\t\treturn true\n\t}\n\n\t/** Remove 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 * Fill 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 */\n\tfill(id: string, values?: TemplateFillValues, options?: TemplateFillOptions): string {\n\t\treturn this.template(id).fill(values, options)\n\t}\n\n\t/**\n\t * Validate 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.template(id).validate(values)\n\t}\n\n\t/**\n\t * Project 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.template(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#throwNotFound(id: string): never {\n\t\tthrow new TemplateError('NOTFOUND', `Unknown template id: ${id}`, { id })\n\t}\n}\n","import type {\n\tTemplateInterface,\n\tTemplateManagerInterface,\n\tTemplateManagerOptions,\n\tTemplateOptions,\n} from './types.js'\nimport { Template } from './Template.js'\nimport { TemplateManager } from './TemplateManager.js'\n\n/**\n * Create 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 *\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 * Create 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 *\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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"}
|