typebars 1.0.7 → 1.0.9

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 +1 @@
1
- {"version":3,"sources":["../../src/executor.ts"],"sourcesContent":["import Handlebars from \"handlebars\";\nimport { TemplateRuntimeError } from \"./errors.ts\";\nimport {\n\tcanUseFastPath,\n\tcoerceLiteral,\n\textractExpressionIdentifier,\n\textractPathSegments,\n\tgetEffectivelySingleBlock,\n\tgetEffectivelySingleExpression,\n\tisSingleExpression,\n\tisThisExpression,\n\tparse,\n} from \"./parser.ts\";\nimport type { TemplateInput, TemplateInputObject } from \"./types.ts\";\nimport { isLiteralInput, isObjectInput } from \"./types.ts\";\nimport { LRUCache } from \"./utils.ts\";\n\n// ─── Template Executor ───────────────────────────────────────────────────────\n// Executes a Handlebars template with real data.\n//\n// Four execution modes (from fastest to most general):\n//\n// 1. **Single expression** (`{{value}}` or ` {{value}} `) → returns the raw\n// value without converting to string. This preserves the original type\n// (number, boolean, object, array, null).\n//\n// 2. **Fast-path** (text + simple expressions, no blocks or helpers) →\n// direct concatenation without going through Handlebars.compile(). Up to\n// 10-100x faster for simple templates like `Hello {{name}}`.\n//\n// 3. **Single block** (`{{#if x}}10{{else}}20{{/if}}` possibly surrounded\n// by whitespace) → rendered via Handlebars then intelligently coerced\n// (detecting number, boolean, null literals).\n//\n// 4. **Mixed template** (text + multiple blocks, helpers, …) →\n// delegates to Handlebars which always produces a string.\n//\n// ─── Caching ─────────────────────────────────────────────────────────────────\n// Handlebars-compiled templates are cached in an LRU cache to avoid costly\n// recompilation on repeated calls.\n//\n// Two cache levels:\n// - **Global cache** (module-level) for standalone `execute()` calls\n// - **Instance cache** for `Typebars` (passed via `ExecutorContext`)\n//\n// ─── Template Identifiers ────────────────────────────────────────────────────\n// The `{{key:N}}` syntax allows resolving a variable from a specific data\n// source, identified by an integer N. The optional `identifierData` parameter\n// provides a mapping `{ [id]: { key: value, ... } }`.\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n/** Optional context for execution (used by Typebars/CompiledTemplate) */\nexport interface ExecutorContext {\n\t/** Data by identifier `{ [id]: { key: value } }` */\n\tidentifierData?: Record<number, Record<string, unknown>>;\n\t/** Pre-compiled Handlebars template (for CompiledTemplate) */\n\tcompiledTemplate?: HandlebarsTemplateDelegate;\n\t/** Isolated Handlebars environment (for custom helpers) */\n\thbs?: typeof Handlebars;\n\t/** Compilation cache shared by the engine */\n\tcompilationCache?: LRUCache<string, HandlebarsTemplateDelegate>;\n}\n\n// ─── Global Compilation Cache ────────────────────────────────────────────────\n// Used by the standalone `execute()` function and `renderWithHandlebars()`.\n// `Typebars` instances use their own cache.\nconst globalCompilationCache = new LRUCache<string, HandlebarsTemplateDelegate>(\n\t128,\n);\n\n// ─── Public API (backward-compatible) ────────────────────────────────────────\n\n/**\n * Executes a template with the provided data and returns the result.\n *\n * The return type depends on the template structure:\n * - Single expression `{{expr}}` → raw value (any)\n * - Single block → coerced value (number, boolean, null, or string)\n * - Mixed template → `string`\n *\n * @param template - The template string\n * @param data - The main context data\n * @param identifierData - (optional) Data by identifier `{ [id]: { key: value } }`\n */\nexport function execute(\n\ttemplate: TemplateInput,\n\tdata: Record<string, unknown>,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): unknown {\n\tif (isObjectInput(template)) {\n\t\treturn executeObjectTemplate(template, data, identifierData);\n\t}\n\tif (isLiteralInput(template)) return template;\n\tconst ast = parse(template);\n\treturn executeFromAst(ast, template, data, { identifierData });\n}\n\n/**\n * Executes an object template recursively (standalone version).\n * Each property is executed individually and the result is an object\n * with the same structure but resolved values.\n */\nfunction executeObjectTemplate(\n\ttemplate: TemplateInputObject,\n\tdata: Record<string, unknown>,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): Record<string, unknown> {\n\tconst result: Record<string, unknown> = {};\n\tfor (const [key, value] of Object.entries(template)) {\n\t\tresult[key] = execute(value, data, identifierData);\n\t}\n\treturn result;\n}\n\n// ─── Internal API (for Typebars / CompiledTemplate) ──────────────────────\n\n/**\n * Executes a template from an already-parsed AST.\n *\n * This function is the core of execution. It is used by:\n * - `execute()` (backward-compatible wrapper)\n * - `CompiledTemplate.execute()` (with pre-parsed AST and cache)\n * - `Typebars.execute()` (with cache and helpers)\n *\n * @param ast - The already-parsed Handlebars AST\n * @param template - The template source (for Handlebars compilation if needed)\n * @param data - The main context data\n * @param ctx - Optional execution context\n */\nexport function executeFromAst(\n\tast: hbs.AST.Program,\n\ttemplate: string,\n\tdata: Record<string, unknown>,\n\tctx?: ExecutorContext,\n): unknown {\n\tconst identifierData = ctx?.identifierData;\n\n\t// ── Case 1: strict single expression `{{expr}}` ──────────────────────\n\t// Exclude helper calls (params > 0 or hash) because they must go\n\t// through Handlebars for correct execution.\n\tif (isSingleExpression(ast)) {\n\t\tconst stmt = ast.body[0] as hbs.AST.MustacheStatement;\n\t\tif (stmt.params.length === 0 && !stmt.hash) {\n\t\t\treturn resolveExpression(stmt.path, data, identifierData);\n\t\t}\n\t}\n\n\t// ── Case 1b: single expression with surrounding whitespace ` {{expr}} `\n\tconst singleExpr = getEffectivelySingleExpression(ast);\n\tif (singleExpr && singleExpr.params.length === 0 && !singleExpr.hash) {\n\t\treturn resolveExpression(singleExpr.path, data, identifierData);\n\t}\n\n\t// ── Case 1c: single expression with helper (params > 0) ──────────────\n\t// E.g. `{{ divide accountIds.length 10 }}` or `{{ math a \"+\" b }}`\n\t// The helper returns a typed value but Handlebars converts it to a\n\t// string. We render via Handlebars then coerce the result to recover\n\t// the original type (number, boolean, null).\n\tif (singleExpr && (singleExpr.params.length > 0 || singleExpr.hash)) {\n\t\tconst merged = mergeDataWithIdentifiers(data, identifierData);\n\t\tconst raw = renderWithHandlebars(template, merged, ctx);\n\t\treturn coerceLiteral(raw);\n\t}\n\n\t// ── Case 2: fast-path for simple templates (text + expressions) ──────\n\t// If the template only contains text and simple expressions (no blocks,\n\t// no helpers with parameters), we can do direct concatenation without\n\t// going through Handlebars.compile().\n\tif (canUseFastPath(ast) && ast.body.length > 1) {\n\t\treturn executeFastPath(ast, data, identifierData);\n\t}\n\n\t// ── Case 3: single block (possibly surrounded by whitespace) ─────────\n\t// Render via Handlebars then attempt to coerce the result to the\n\t// detected literal type (number, boolean, null).\n\tconst singleBlock = getEffectivelySingleBlock(ast);\n\tif (singleBlock) {\n\t\tconst merged = mergeDataWithIdentifiers(data, identifierData);\n\t\tconst raw = renderWithHandlebars(template, merged, ctx);\n\t\treturn coerceLiteral(raw);\n\t}\n\n\t// ── Case 4: mixed template → string ──────────────────────────────────\n\tconst merged = mergeDataWithIdentifiers(data, identifierData);\n\treturn renderWithHandlebars(template, merged, ctx);\n}\n\n// ─── Fast-Path Execution ─────────────────────────────────────────────────────\n// For templates consisting only of text and simple expressions (no blocks,\n// no helpers), we bypass Handlebars and do direct concatenation.\n// This is significantly faster.\n\n/**\n * Executes a template via the fast-path (direct concatenation).\n *\n * Precondition: `canUseFastPath(ast)` must return `true`.\n *\n * @param ast - The template AST (only ContentStatement and simple MustacheStatement)\n * @param data - The context data\n * @param identifierData - Data by identifier (optional)\n * @returns The resulting string\n */\nfunction executeFastPath(\n\tast: hbs.AST.Program,\n\tdata: Record<string, unknown>,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): string {\n\tlet result = \"\";\n\n\tfor (const stmt of ast.body) {\n\t\tif (stmt.type === \"ContentStatement\") {\n\t\t\tresult += (stmt as hbs.AST.ContentStatement).value;\n\t\t} else if (stmt.type === \"MustacheStatement\") {\n\t\t\tconst value = resolveExpression(\n\t\t\t\t(stmt as hbs.AST.MustacheStatement).path,\n\t\t\t\tdata,\n\t\t\t\tidentifierData,\n\t\t\t);\n\t\t\t// Handlebars converts values to strings for rendering.\n\t\t\t// We replicate this behavior: null/undefined → \"\", otherwise String(value).\n\t\t\tif (value != null) {\n\t\t\t\tresult += String(value);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn result;\n}\n\n// ─── Direct Expression Resolution ────────────────────────────────────────────\n// Used for single-expression templates and the fast-path, to return the raw\n// value without going through the Handlebars engine.\n\n/**\n * Resolves an AST expression by following the path through the data.\n *\n * If the expression contains an identifier (e.g. `meetingId:1`), resolution\n * is performed in `identifierData[1]` instead of `data`.\n *\n * @param expr - The AST expression to resolve\n * @param data - The main data context\n * @param identifierData - Data by identifier (optional)\n * @returns The raw value pointed to by the expression\n */\nfunction resolveExpression(\n\texpr: hbs.AST.Expression,\n\tdata: Record<string, unknown>,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): unknown {\n\t// this / . → return the entire context\n\tif (isThisExpression(expr)) {\n\t\treturn data;\n\t}\n\n\t// Literals\n\tif (expr.type === \"StringLiteral\")\n\t\treturn (expr as hbs.AST.StringLiteral).value;\n\tif (expr.type === \"NumberLiteral\")\n\t\treturn (expr as hbs.AST.NumberLiteral).value;\n\tif (expr.type === \"BooleanLiteral\")\n\t\treturn (expr as hbs.AST.BooleanLiteral).value;\n\tif (expr.type === \"NullLiteral\") return null;\n\tif (expr.type === \"UndefinedLiteral\") return undefined;\n\n\t// PathExpression — navigate through segments in the data object\n\tconst segments = extractPathSegments(expr);\n\tif (segments.length === 0) {\n\t\tthrow new TemplateRuntimeError(\n\t\t\t`Cannot resolve expression of type \"${expr.type}\"`,\n\t\t);\n\t}\n\n\t// Extract the potential identifier from the last segment\n\tconst { cleanSegments, identifier } = extractExpressionIdentifier(segments);\n\n\tif (identifier !== null && identifierData) {\n\t\tconst source = identifierData[identifier];\n\t\tif (source) {\n\t\t\treturn resolveDataPath(source, cleanSegments);\n\t\t}\n\t\t// Source does not exist → undefined (like a missing key)\n\t\treturn undefined;\n\t}\n\n\tif (identifier !== null && !identifierData) {\n\t\t// Template uses an identifier but no identifierData was provided\n\t\treturn undefined;\n\t}\n\n\treturn resolveDataPath(data, cleanSegments);\n}\n\n/**\n * Navigates through a data object by following a path of segments.\n *\n * @param data - The data object\n * @param segments - The path segments (e.g. `[\"user\", \"address\", \"city\"]`)\n * @returns The value at the end of the path, or `undefined` if an\n * intermediate segment is null/undefined\n */\nexport function resolveDataPath(data: unknown, segments: string[]): unknown {\n\tlet current: unknown = data;\n\n\tfor (const segment of segments) {\n\t\tif (current === null || current === undefined) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif (typeof current !== \"object\") {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tcurrent = (current as Record<string, unknown>)[segment];\n\t}\n\n\treturn current;\n}\n\n// ─── Data Merging ────────────────────────────────────────────────────────────\n// For Handlebars rendering (mixed templates / blocks), we cannot intercept\n// resolution on a per-expression basis. Instead, we merge identifier data\n// into the main object using the format `\"key:N\"`.\n//\n// Handlebars parses `{{meetingId:1}}` as a PathExpression with a single\n// segment `\"meetingId:1\"`, so it looks up the key `\"meetingId:1\"` in the\n// data object — which matches our flattened format exactly.\n\n/**\n * Merges the main data with identifier data.\n *\n * @param data - Main data\n * @param identifierData - Data by identifier\n * @returns A merged object where identifier data appears as `\"key:N\"` keys\n *\n * @example\n * ```\n * mergeDataWithIdentifiers(\n * { name: \"Alice\" },\n * { 1: { meetingId: \"val1\" }, 2: { meetingId: \"val2\" } }\n * )\n * // → { name: \"Alice\", \"meetingId:1\": \"val1\", \"meetingId:2\": \"val2\" }\n * ```\n */\nfunction mergeDataWithIdentifiers(\n\tdata: Record<string, unknown>,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): Record<string, unknown> {\n\tif (!identifierData) return data;\n\n\tconst merged: Record<string, unknown> = { ...data };\n\n\tfor (const [id, idData] of Object.entries(identifierData)) {\n\t\tfor (const [key, value] of Object.entries(idData)) {\n\t\t\tmerged[`${key}:${id}`] = value;\n\t\t}\n\t}\n\n\treturn merged;\n}\n\n// ─── Handlebars Rendering ────────────────────────────────────────────────────\n// For complex templates (blocks, helpers), we delegate to Handlebars.\n// Compilation is cached to avoid costly recompilations.\n\n/**\n * Compiles and executes a template via Handlebars.\n *\n * Uses a compilation cache (LRU) to avoid recompiling the same template\n * on repeated calls. The cache is either:\n * - The global cache (for the standalone `execute()` function)\n * - The instance cache provided via `ExecutorContext` (for `Typebars`)\n *\n * @param template - The template string\n * @param data - The context data\n * @param ctx - Optional execution context (cache, Handlebars env)\n * @returns Always a string\n */\nfunction renderWithHandlebars(\n\ttemplate: string,\n\tdata: Record<string, unknown>,\n\tctx?: ExecutorContext,\n): string {\n\ttry {\n\t\t// 1. Use the pre-compiled template if available (CompiledTemplate)\n\t\tif (ctx?.compiledTemplate) {\n\t\t\treturn ctx.compiledTemplate(data);\n\t\t}\n\n\t\t// 2. Look up in the cache (instance or global)\n\t\tconst cache = ctx?.compilationCache ?? globalCompilationCache;\n\t\tconst hbs = ctx?.hbs ?? Handlebars;\n\n\t\tlet compiled = cache.get(template);\n\t\tif (!compiled) {\n\t\t\tcompiled = hbs.compile(template, {\n\t\t\t\t// Disable HTML-escaping by default — this engine is not\n\t\t\t\t// HTML-specific, we want raw values.\n\t\t\t\tnoEscape: true,\n\t\t\t\t// Strict mode: throws if a path does not exist in the data.\n\t\t\t\tstrict: false,\n\t\t\t});\n\t\t\tcache.set(template, compiled);\n\t\t}\n\n\t\treturn compiled(data);\n\t} catch (error: unknown) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tthrow new TemplateRuntimeError(message);\n\t}\n}\n\n/**\n * Clears the global Handlebars compilation cache.\n * Useful for tests or to free memory.\n */\nexport function clearCompilationCache(): void {\n\tglobalCompilationCache.clear();\n}\n"],"names":["clearCompilationCache","execute","executeFromAst","resolveDataPath","globalCompilationCache","LRUCache","template","data","identifierData","isObjectInput","executeObjectTemplate","isLiteralInput","ast","parse","result","key","value","Object","entries","ctx","isSingleExpression","stmt","body","params","length","hash","resolveExpression","path","singleExpr","getEffectivelySingleExpression","merged","mergeDataWithIdentifiers","raw","renderWithHandlebars","coerceLiteral","canUseFastPath","executeFastPath","singleBlock","getEffectivelySingleBlock","type","String","expr","isThisExpression","undefined","segments","extractPathSegments","TemplateRuntimeError","cleanSegments","identifier","extractExpressionIdentifier","source","current","segment","id","idData","compiledTemplate","cache","compilationCache","hbs","Handlebars","compiled","get","compile","noEscape","strict","set","error","message","Error","clear"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/executor.ts"],"sourcesContent":["import Handlebars from \"handlebars\";\nimport { TemplateRuntimeError } from \"./errors.ts\";\nimport {\n\tcanUseFastPath,\n\tcoerceLiteral,\n\textractExpressionIdentifier,\n\textractPathSegments,\n\tgetEffectivelySingleBlock,\n\tgetEffectivelySingleExpression,\n\tisSingleExpression,\n\tisThisExpression,\n\tparse,\n} from \"./parser.ts\";\nimport type {\n\tTemplateInput,\n\tTemplateInputArray,\n\tTemplateInputObject,\n} from \"./types.ts\";\nimport { isArrayInput, isLiteralInput, isObjectInput } from \"./types.ts\";\nimport { LRUCache } from \"./utils.ts\";\n\n// ─── Template Executor ───────────────────────────────────────────────────────\n// Executes a Handlebars template with real data.\n//\n// Four execution modes (from fastest to most general):\n//\n// 1. **Single expression** (`{{value}}` or ` {{value}} `) → returns the raw\n// value without converting to string. This preserves the original type\n// (number, boolean, object, array, null).\n//\n// 2. **Fast-path** (text + simple expressions, no blocks or helpers) →\n// direct concatenation without going through Handlebars.compile(). Up to\n// 10-100x faster for simple templates like `Hello {{name}}`.\n//\n// 3. **Single block** (`{{#if x}}10{{else}}20{{/if}}` possibly surrounded\n// by whitespace) → rendered via Handlebars then intelligently coerced\n// (detecting number, boolean, null literals).\n//\n// 4. **Mixed template** (text + multiple blocks, helpers, …) →\n// delegates to Handlebars which always produces a string.\n//\n// ─── Caching ─────────────────────────────────────────────────────────────────\n// Handlebars-compiled templates are cached in an LRU cache to avoid costly\n// recompilation on repeated calls.\n//\n// Two cache levels:\n// - **Global cache** (module-level) for standalone `execute()` calls\n// - **Instance cache** for `Typebars` (passed via `ExecutorContext`)\n//\n// ─── Template Identifiers ────────────────────────────────────────────────────\n// The `{{key:N}}` syntax allows resolving a variable from a specific data\n// source, identified by an integer N. The optional `identifierData` parameter\n// provides a mapping `{ [id]: { key: value, ... } }`.\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n/** Optional context for execution (used by Typebars/CompiledTemplate) */\nexport interface ExecutorContext {\n\t/** Data by identifier `{ [id]: { key: value } }` */\n\tidentifierData?: Record<number, Record<string, unknown>>;\n\t/** Pre-compiled Handlebars template (for CompiledTemplate) */\n\tcompiledTemplate?: HandlebarsTemplateDelegate;\n\t/** Isolated Handlebars environment (for custom helpers) */\n\thbs?: typeof Handlebars;\n\t/** Compilation cache shared by the engine */\n\tcompilationCache?: LRUCache<string, HandlebarsTemplateDelegate>;\n}\n\n// ─── Global Compilation Cache ────────────────────────────────────────────────\n// Used by the standalone `execute()` function and `renderWithHandlebars()`.\n// `Typebars` instances use their own cache.\nconst globalCompilationCache = new LRUCache<string, HandlebarsTemplateDelegate>(\n\t128,\n);\n\n// ─── Public API (backward-compatible) ────────────────────────────────────────\n\n/**\n * Executes a template with the provided data and returns the result.\n *\n * The return type depends on the template structure:\n * - Single expression `{{expr}}` → raw value (any)\n * - Single block → coerced value (number, boolean, null, or string)\n * - Mixed template → `string`\n *\n * @param template - The template string\n * @param data - The main context data\n * @param identifierData - (optional) Data by identifier `{ [id]: { key: value } }`\n */\nexport function execute(\n\ttemplate: TemplateInput,\n\tdata: Record<string, unknown>,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): unknown {\n\tif (isArrayInput(template)) {\n\t\treturn executeArrayTemplate(template, data, identifierData);\n\t}\n\tif (isObjectInput(template)) {\n\t\treturn executeObjectTemplate(template, data, identifierData);\n\t}\n\tif (isLiteralInput(template)) return template;\n\tconst ast = parse(template);\n\treturn executeFromAst(ast, template, data, { identifierData });\n}\n\n/**\n * Executes an array template recursively (standalone version).\n * Each element is executed individually and the result is an array\n * with resolved values.\n */\nfunction executeArrayTemplate(\n\ttemplate: TemplateInputArray,\n\tdata: Record<string, unknown>,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): unknown[] {\n\tconst result: unknown[] = [];\n\tfor (const element of template) {\n\t\tresult.push(execute(element, data, identifierData));\n\t}\n\treturn result;\n}\n\n/**\n * Executes an object template recursively (standalone version).\n * Each property is executed individually and the result is an object\n * with the same structure but resolved values.\n */\nfunction executeObjectTemplate(\n\ttemplate: TemplateInputObject,\n\tdata: Record<string, unknown>,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): Record<string, unknown> {\n\tconst result: Record<string, unknown> = {};\n\tfor (const [key, value] of Object.entries(template)) {\n\t\tresult[key] = execute(value, data, identifierData);\n\t}\n\treturn result;\n}\n\n// ─── Internal API (for Typebars / CompiledTemplate) ──────────────────────\n\n/**\n * Executes a template from an already-parsed AST.\n *\n * This function is the core of execution. It is used by:\n * - `execute()` (backward-compatible wrapper)\n * - `CompiledTemplate.execute()` (with pre-parsed AST and cache)\n * - `Typebars.execute()` (with cache and helpers)\n *\n * @param ast - The already-parsed Handlebars AST\n * @param template - The template source (for Handlebars compilation if needed)\n * @param data - The main context data\n * @param ctx - Optional execution context\n */\nexport function executeFromAst(\n\tast: hbs.AST.Program,\n\ttemplate: string,\n\tdata: Record<string, unknown>,\n\tctx?: ExecutorContext,\n): unknown {\n\tconst identifierData = ctx?.identifierData;\n\n\t// ── Case 1: strict single expression `{{expr}}` ──────────────────────\n\t// Exclude helper calls (params > 0 or hash) because they must go\n\t// through Handlebars for correct execution.\n\tif (isSingleExpression(ast)) {\n\t\tconst stmt = ast.body[0] as hbs.AST.MustacheStatement;\n\t\tif (stmt.params.length === 0 && !stmt.hash) {\n\t\t\treturn resolveExpression(stmt.path, data, identifierData);\n\t\t}\n\t}\n\n\t// ── Case 1b: single expression with surrounding whitespace ` {{expr}} `\n\tconst singleExpr = getEffectivelySingleExpression(ast);\n\tif (singleExpr && singleExpr.params.length === 0 && !singleExpr.hash) {\n\t\treturn resolveExpression(singleExpr.path, data, identifierData);\n\t}\n\n\t// ── Case 1c: single expression with helper (params > 0) ──────────────\n\t// E.g. `{{ divide accountIds.length 10 }}` or `{{ math a \"+\" b }}`\n\t// The helper returns a typed value but Handlebars converts it to a\n\t// string. We render via Handlebars then coerce the result to recover\n\t// the original type (number, boolean, null).\n\tif (singleExpr && (singleExpr.params.length > 0 || singleExpr.hash)) {\n\t\tconst merged = mergeDataWithIdentifiers(data, identifierData);\n\t\tconst raw = renderWithHandlebars(template, merged, ctx);\n\t\treturn coerceLiteral(raw);\n\t}\n\n\t// ── Case 2: fast-path for simple templates (text + expressions) ──────\n\t// If the template only contains text and simple expressions (no blocks,\n\t// no helpers with parameters), we can do direct concatenation without\n\t// going through Handlebars.compile().\n\tif (canUseFastPath(ast) && ast.body.length > 1) {\n\t\treturn executeFastPath(ast, data, identifierData);\n\t}\n\n\t// ── Case 3: single block (possibly surrounded by whitespace) ─────────\n\t// Render via Handlebars then attempt to coerce the result to the\n\t// detected literal type (number, boolean, null).\n\tconst singleBlock = getEffectivelySingleBlock(ast);\n\tif (singleBlock) {\n\t\tconst merged = mergeDataWithIdentifiers(data, identifierData);\n\t\tconst raw = renderWithHandlebars(template, merged, ctx);\n\t\treturn coerceLiteral(raw);\n\t}\n\n\t// ── Case 4: mixed template → string ──────────────────────────────────\n\tconst merged = mergeDataWithIdentifiers(data, identifierData);\n\treturn renderWithHandlebars(template, merged, ctx);\n}\n\n// ─── Fast-Path Execution ─────────────────────────────────────────────────────\n// For templates consisting only of text and simple expressions (no blocks,\n// no helpers), we bypass Handlebars and do direct concatenation.\n// This is significantly faster.\n\n/**\n * Executes a template via the fast-path (direct concatenation).\n *\n * Precondition: `canUseFastPath(ast)` must return `true`.\n *\n * @param ast - The template AST (only ContentStatement and simple MustacheStatement)\n * @param data - The context data\n * @param identifierData - Data by identifier (optional)\n * @returns The resulting string\n */\nfunction executeFastPath(\n\tast: hbs.AST.Program,\n\tdata: Record<string, unknown>,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): string {\n\tlet result = \"\";\n\n\tfor (const stmt of ast.body) {\n\t\tif (stmt.type === \"ContentStatement\") {\n\t\t\tresult += (stmt as hbs.AST.ContentStatement).value;\n\t\t} else if (stmt.type === \"MustacheStatement\") {\n\t\t\tconst value = resolveExpression(\n\t\t\t\t(stmt as hbs.AST.MustacheStatement).path,\n\t\t\t\tdata,\n\t\t\t\tidentifierData,\n\t\t\t);\n\t\t\t// Handlebars converts values to strings for rendering.\n\t\t\t// We replicate this behavior: null/undefined → \"\", otherwise String(value).\n\t\t\tif (value != null) {\n\t\t\t\tresult += String(value);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn result;\n}\n\n// ─── Direct Expression Resolution ────────────────────────────────────────────\n// Used for single-expression templates and the fast-path, to return the raw\n// value without going through the Handlebars engine.\n\n/**\n * Resolves an AST expression by following the path through the data.\n *\n * If the expression contains an identifier (e.g. `meetingId:1`), resolution\n * is performed in `identifierData[1]` instead of `data`.\n *\n * @param expr - The AST expression to resolve\n * @param data - The main data context\n * @param identifierData - Data by identifier (optional)\n * @returns The raw value pointed to by the expression\n */\nfunction resolveExpression(\n\texpr: hbs.AST.Expression,\n\tdata: Record<string, unknown>,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): unknown {\n\t// this / . → return the entire context\n\tif (isThisExpression(expr)) {\n\t\treturn data;\n\t}\n\n\t// Literals\n\tif (expr.type === \"StringLiteral\")\n\t\treturn (expr as hbs.AST.StringLiteral).value;\n\tif (expr.type === \"NumberLiteral\")\n\t\treturn (expr as hbs.AST.NumberLiteral).value;\n\tif (expr.type === \"BooleanLiteral\")\n\t\treturn (expr as hbs.AST.BooleanLiteral).value;\n\tif (expr.type === \"NullLiteral\") return null;\n\tif (expr.type === \"UndefinedLiteral\") return undefined;\n\n\t// PathExpression — navigate through segments in the data object\n\tconst segments = extractPathSegments(expr);\n\tif (segments.length === 0) {\n\t\tthrow new TemplateRuntimeError(\n\t\t\t`Cannot resolve expression of type \"${expr.type}\"`,\n\t\t);\n\t}\n\n\t// Extract the potential identifier from the last segment\n\tconst { cleanSegments, identifier } = extractExpressionIdentifier(segments);\n\n\tif (identifier !== null && identifierData) {\n\t\tconst source = identifierData[identifier];\n\t\tif (source) {\n\t\t\treturn resolveDataPath(source, cleanSegments);\n\t\t}\n\t\t// Source does not exist → undefined (like a missing key)\n\t\treturn undefined;\n\t}\n\n\tif (identifier !== null && !identifierData) {\n\t\t// Template uses an identifier but no identifierData was provided\n\t\treturn undefined;\n\t}\n\n\treturn resolveDataPath(data, cleanSegments);\n}\n\n/**\n * Navigates through a data object by following a path of segments.\n *\n * @param data - The data object\n * @param segments - The path segments (e.g. `[\"user\", \"address\", \"city\"]`)\n * @returns The value at the end of the path, or `undefined` if an\n * intermediate segment is null/undefined\n */\nexport function resolveDataPath(data: unknown, segments: string[]): unknown {\n\tlet current: unknown = data;\n\n\tfor (const segment of segments) {\n\t\tif (current === null || current === undefined) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif (typeof current !== \"object\") {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tcurrent = (current as Record<string, unknown>)[segment];\n\t}\n\n\treturn current;\n}\n\n// ─── Data Merging ────────────────────────────────────────────────────────────\n// For Handlebars rendering (mixed templates / blocks), we cannot intercept\n// resolution on a per-expression basis. Instead, we merge identifier data\n// into the main object using the format `\"key:N\"`.\n//\n// Handlebars parses `{{meetingId:1}}` as a PathExpression with a single\n// segment `\"meetingId:1\"`, so it looks up the key `\"meetingId:1\"` in the\n// data object — which matches our flattened format exactly.\n\n/**\n * Merges the main data with identifier data.\n *\n * @param data - Main data\n * @param identifierData - Data by identifier\n * @returns A merged object where identifier data appears as `\"key:N\"` keys\n *\n * @example\n * ```\n * mergeDataWithIdentifiers(\n * { name: \"Alice\" },\n * { 1: { meetingId: \"val1\" }, 2: { meetingId: \"val2\" } }\n * )\n * // → { name: \"Alice\", \"meetingId:1\": \"val1\", \"meetingId:2\": \"val2\" }\n * ```\n */\nfunction mergeDataWithIdentifiers(\n\tdata: Record<string, unknown>,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): Record<string, unknown> {\n\tif (!identifierData) return data;\n\n\tconst merged: Record<string, unknown> = { ...data };\n\n\tfor (const [id, idData] of Object.entries(identifierData)) {\n\t\tfor (const [key, value] of Object.entries(idData)) {\n\t\t\tmerged[`${key}:${id}`] = value;\n\t\t}\n\t}\n\n\treturn merged;\n}\n\n// ─── Handlebars Rendering ────────────────────────────────────────────────────\n// For complex templates (blocks, helpers), we delegate to Handlebars.\n// Compilation is cached to avoid costly recompilations.\n\n/**\n * Compiles and executes a template via Handlebars.\n *\n * Uses a compilation cache (LRU) to avoid recompiling the same template\n * on repeated calls. The cache is either:\n * - The global cache (for the standalone `execute()` function)\n * - The instance cache provided via `ExecutorContext` (for `Typebars`)\n *\n * @param template - The template string\n * @param data - The context data\n * @param ctx - Optional execution context (cache, Handlebars env)\n * @returns Always a string\n */\nfunction renderWithHandlebars(\n\ttemplate: string,\n\tdata: Record<string, unknown>,\n\tctx?: ExecutorContext,\n): string {\n\ttry {\n\t\t// 1. Use the pre-compiled template if available (CompiledTemplate)\n\t\tif (ctx?.compiledTemplate) {\n\t\t\treturn ctx.compiledTemplate(data);\n\t\t}\n\n\t\t// 2. Look up in the cache (instance or global)\n\t\tconst cache = ctx?.compilationCache ?? globalCompilationCache;\n\t\tconst hbs = ctx?.hbs ?? Handlebars;\n\n\t\tlet compiled = cache.get(template);\n\t\tif (!compiled) {\n\t\t\tcompiled = hbs.compile(template, {\n\t\t\t\t// Disable HTML-escaping by default — this engine is not\n\t\t\t\t// HTML-specific, we want raw values.\n\t\t\t\tnoEscape: true,\n\t\t\t\t// Strict mode: throws if a path does not exist in the data.\n\t\t\t\tstrict: false,\n\t\t\t});\n\t\t\tcache.set(template, compiled);\n\t\t}\n\n\t\treturn compiled(data);\n\t} catch (error: unknown) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tthrow new TemplateRuntimeError(message);\n\t}\n}\n\n/**\n * Clears the global Handlebars compilation cache.\n * Useful for tests or to free memory.\n */\nexport function clearCompilationCache(): void {\n\tglobalCompilationCache.clear();\n}\n"],"names":["clearCompilationCache","execute","executeFromAst","resolveDataPath","globalCompilationCache","LRUCache","template","data","identifierData","isArrayInput","executeArrayTemplate","isObjectInput","executeObjectTemplate","isLiteralInput","ast","parse","result","element","push","key","value","Object","entries","ctx","isSingleExpression","stmt","body","params","length","hash","resolveExpression","path","singleExpr","getEffectivelySingleExpression","merged","mergeDataWithIdentifiers","raw","renderWithHandlebars","coerceLiteral","canUseFastPath","executeFastPath","singleBlock","getEffectivelySingleBlock","type","String","expr","isThisExpression","undefined","segments","extractPathSegments","TemplateRuntimeError","cleanSegments","identifier","extractExpressionIdentifier","source","current","segment","id","idData","compiledTemplate","cache","compilationCache","hbs","Handlebars","compiled","get","compile","noEscape","strict","set","error","message","Error","clear"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
1
  export * from "./errors.js";
2
2
  export { Typebars } from "./typebars.js";
3
- export { defineHelper, type TemplateInput } from "./types.js";
3
+ export { defineHelper, isArrayInput, type TemplateInput, type TemplateInputArray, } from "./types.js";
package/dist/cjs/index.js CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});function _export(target,all){for(var name in all)Object.defineProperty(target,name,{enumerable:true,get:Object.getOwnPropertyDescriptor(all,name).get})}_export(exports,{get Typebars(){return _typebars.Typebars},get defineHelper(){return _types.defineHelper}});_export_star(require("./errors.js"),exports);const _typebars=require("./typebars.js");const _types=require("./types.js");function _export_star(from,to){Object.keys(from).forEach(function(k){if(k!=="default"&&!Object.prototype.hasOwnProperty.call(to,k)){Object.defineProperty(to,k,{enumerable:true,get:function(){return from[k]}})}});return from}
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});function _export(target,all){for(var name in all)Object.defineProperty(target,name,{enumerable:true,get:Object.getOwnPropertyDescriptor(all,name).get})}_export(exports,{get Typebars(){return _typebars.Typebars},get defineHelper(){return _types.defineHelper},get isArrayInput(){return _types.isArrayInput}});_export_star(require("./errors.js"),exports);const _typebars=require("./typebars.js");const _types=require("./types.js");function _export_star(from,to){Object.keys(from).forEach(function(k){if(k!=="default"&&!Object.prototype.hasOwnProperty.call(to,k)){Object.defineProperty(to,k,{enumerable:true,get:function(){return from[k]}})}});return from}
2
2
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/index.ts"],"sourcesContent":["export * from \"./errors\";\nexport { Typebars } from \"./typebars\";\nexport { defineHelper, type TemplateInput } from \"./types\";\n"],"names":["Typebars","defineHelper"],"mappings":"mPACSA,kBAAAA,kBAAQ,MACRC,sBAAAA,mBAAY,yBAFP,6CACW,mCACwB"}
1
+ {"version":3,"sources":["../../src/index.ts"],"sourcesContent":["export * from \"./errors\";\nexport { Typebars } from \"./typebars\";\nexport {\n\tdefineHelper,\n\tisArrayInput,\n\ttype TemplateInput,\n\ttype TemplateInputArray,\n} from \"./types\";\n"],"names":["Typebars","defineHelper","isArrayInput"],"mappings":"mPACSA,kBAAAA,kBAAQ,MAEhBC,sBAAAA,mBAAY,MACZC,sBAAAA,mBAAY,yBAJC,6CACW,mCAMlB"}
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});Object.defineProperty(exports,"Typebars",{enumerable:true,get:function(){return Typebars}});const _handlebars=/*#__PURE__*/_interop_require_default(require("handlebars"));const _analyzerts=require("./analyzer.js");const _compiledtemplatets=require("./compiled-template.js");const _errorsts=require("./errors.js");const _executorts=require("./executor.js");const _indexts=require("./helpers/index.js");const _parserts=require("./parser.js");const _typests=require("./types.js");const _utils=require("./utils.js");function _define_property(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true})}else{obj[key]=value}return obj}function _interop_require_default(obj){return obj&&obj.__esModule?obj:{default:obj}}class Typebars{compile(template){if((0,_typests.isObjectInput)(template)){const children={};for(const[key,value]of Object.entries(template)){children[key]=this.compile(value)}return _compiledtemplatets.CompiledTemplate.fromObject(children,{helpers:this.helpers,hbs:this.hbs,compilationCache:this.compilationCache})}if((0,_typests.isLiteralInput)(template)){return _compiledtemplatets.CompiledTemplate.fromLiteral(template,{helpers:this.helpers,hbs:this.hbs,compilationCache:this.compilationCache})}const ast=this.getCachedAst(template);const options={helpers:this.helpers,hbs:this.hbs,compilationCache:this.compilationCache};return _compiledtemplatets.CompiledTemplate.fromTemplate(ast,template,options)}analyze(template,inputSchema,identifierSchemas){if((0,_typests.isObjectInput)(template)){return(0,_utils.aggregateObjectAnalysis)(Object.keys(template),key=>this.analyze(template[key],inputSchema,identifierSchemas))}if((0,_typests.isLiteralInput)(template)){return{valid:true,diagnostics:[],outputSchema:(0,_typests.inferPrimitiveSchema)(template)}}const ast=this.getCachedAst(template);return(0,_analyzerts.analyzeFromAst)(ast,template,inputSchema,{identifierSchemas,helpers:this.helpers})}validate(template,inputSchema,identifierSchemas){const analysis=this.analyze(template,inputSchema,identifierSchemas);return{valid:analysis.valid,diagnostics:analysis.diagnostics}}isValidSyntax(template){if((0,_typests.isObjectInput)(template)){return Object.values(template).every(v=>this.isValidSyntax(v))}if((0,_typests.isLiteralInput)(template))return true;try{this.getCachedAst(template);return true}catch{return false}}execute(template,data,options){if((0,_typests.isObjectInput)(template)){const result={};for(const[key,value]of Object.entries(template)){result[key]=this.execute(value,data,options)}return result}if((0,_typests.isLiteralInput)(template))return template;const ast=this.getCachedAst(template);if(options?.schema){const analysis=(0,_analyzerts.analyzeFromAst)(ast,template,options.schema,{identifierSchemas:options.identifierSchemas,helpers:this.helpers});if(!analysis.valid){throw new _errorsts.TemplateAnalysisError(analysis.diagnostics)}}return(0,_executorts.executeFromAst)(ast,template,data,{identifierData:options?.identifierData,hbs:this.hbs,compilationCache:this.compilationCache})}analyzeAndExecute(template,inputSchema,data,options){if((0,_typests.isObjectInput)(template)){return(0,_utils.aggregateObjectAnalysisAndExecution)(Object.keys(template),key=>this.analyzeAndExecute(template[key],inputSchema,data,options))}if((0,_typests.isLiteralInput)(template)){return{analysis:{valid:true,diagnostics:[],outputSchema:(0,_typests.inferPrimitiveSchema)(template)},value:template}}const ast=this.getCachedAst(template);const analysis=(0,_analyzerts.analyzeFromAst)(ast,template,inputSchema,{identifierSchemas:options?.identifierSchemas,helpers:this.helpers});if(!analysis.valid){return{analysis,value:undefined}}const value=(0,_executorts.executeFromAst)(ast,template,data,{identifierData:options?.identifierData,hbs:this.hbs,compilationCache:this.compilationCache});return{analysis,value}}registerHelper(name,definition){this.helpers.set(name,definition);this.hbs.registerHelper(name,definition.fn);this.compilationCache.clear();return this}unregisterHelper(name){this.helpers.delete(name);this.hbs.unregisterHelper(name);this.compilationCache.clear();return this}hasHelper(name){return this.helpers.has(name)}clearCaches(){this.astCache.clear();this.compilationCache.clear()}getCachedAst(template){let ast=this.astCache.get(template);if(!ast){ast=(0,_parserts.parse)(template);this.astCache.set(template,ast)}return ast}constructor(options={}){_define_property(this,"hbs",void 0);_define_property(this,"astCache",void 0);_define_property(this,"compilationCache",void 0);_define_property(this,"helpers",new Map);this.hbs=_handlebars.default.create();this.astCache=new _utils.LRUCache(options.astCacheSize??256);this.compilationCache=new _utils.LRUCache(options.compilationCacheSize??256);new _indexts.MathHelpers().register(this);new _indexts.LogicalHelpers().register(this);if(options.helpers){for(const helper of options.helpers){const{name,...definition}=helper;this.registerHelper(name,definition)}}}}
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});Object.defineProperty(exports,"Typebars",{enumerable:true,get:function(){return Typebars}});const _handlebars=/*#__PURE__*/_interop_require_default(require("handlebars"));const _analyzerts=require("./analyzer.js");const _compiledtemplatets=require("./compiled-template.js");const _errorsts=require("./errors.js");const _executorts=require("./executor.js");const _indexts=require("./helpers/index.js");const _parserts=require("./parser.js");const _typests=require("./types.js");const _utils=require("./utils.js");function _define_property(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true})}else{obj[key]=value}return obj}function _interop_require_default(obj){return obj&&obj.__esModule?obj:{default:obj}}class Typebars{compile(template){if((0,_typests.isArrayInput)(template)){const children=[];for(const element of template){children.push(this.compile(element))}return _compiledtemplatets.CompiledTemplate.fromArray(children,{helpers:this.helpers,hbs:this.hbs,compilationCache:this.compilationCache})}if((0,_typests.isObjectInput)(template)){const children={};for(const[key,value]of Object.entries(template)){children[key]=this.compile(value)}return _compiledtemplatets.CompiledTemplate.fromObject(children,{helpers:this.helpers,hbs:this.hbs,compilationCache:this.compilationCache})}if((0,_typests.isLiteralInput)(template)){return _compiledtemplatets.CompiledTemplate.fromLiteral(template,{helpers:this.helpers,hbs:this.hbs,compilationCache:this.compilationCache})}const ast=this.getCachedAst(template);const options={helpers:this.helpers,hbs:this.hbs,compilationCache:this.compilationCache};return _compiledtemplatets.CompiledTemplate.fromTemplate(ast,template,options)}analyze(template,inputSchema,identifierSchemas){if((0,_typests.isArrayInput)(template)){return(0,_utils.aggregateArrayAnalysis)(template.length,index=>this.analyze(template[index],inputSchema,identifierSchemas))}if((0,_typests.isObjectInput)(template)){return(0,_utils.aggregateObjectAnalysis)(Object.keys(template),key=>this.analyze(template[key],inputSchema,identifierSchemas))}if((0,_typests.isLiteralInput)(template)){return{valid:true,diagnostics:[],outputSchema:(0,_typests.inferPrimitiveSchema)(template)}}const ast=this.getCachedAst(template);return(0,_analyzerts.analyzeFromAst)(ast,template,inputSchema,{identifierSchemas,helpers:this.helpers})}validate(template,inputSchema,identifierSchemas){const analysis=this.analyze(template,inputSchema,identifierSchemas);return{valid:analysis.valid,diagnostics:analysis.diagnostics}}isValidSyntax(template){if((0,_typests.isArrayInput)(template)){return template.every(v=>this.isValidSyntax(v))}if((0,_typests.isObjectInput)(template)){return Object.values(template).every(v=>this.isValidSyntax(v))}if((0,_typests.isLiteralInput)(template))return true;try{this.getCachedAst(template);return true}catch{return false}}execute(template,data,options){if((0,_typests.isArrayInput)(template)){const result=[];for(const element of template){result.push(this.execute(element,data,options))}return result}if((0,_typests.isObjectInput)(template)){const result={};for(const[key,value]of Object.entries(template)){result[key]=this.execute(value,data,options)}return result}if((0,_typests.isLiteralInput)(template))return template;const ast=this.getCachedAst(template);if(options?.schema){const analysis=(0,_analyzerts.analyzeFromAst)(ast,template,options.schema,{identifierSchemas:options.identifierSchemas,helpers:this.helpers});if(!analysis.valid){throw new _errorsts.TemplateAnalysisError(analysis.diagnostics)}}return(0,_executorts.executeFromAst)(ast,template,data,{identifierData:options?.identifierData,hbs:this.hbs,compilationCache:this.compilationCache})}analyzeAndExecute(template,inputSchema,data,options){if((0,_typests.isArrayInput)(template)){return(0,_utils.aggregateArrayAnalysisAndExecution)(template.length,index=>this.analyzeAndExecute(template[index],inputSchema,data,options))}if((0,_typests.isObjectInput)(template)){return(0,_utils.aggregateObjectAnalysisAndExecution)(Object.keys(template),key=>this.analyzeAndExecute(template[key],inputSchema,data,options))}if((0,_typests.isLiteralInput)(template)){return{analysis:{valid:true,diagnostics:[],outputSchema:(0,_typests.inferPrimitiveSchema)(template)},value:template}}const ast=this.getCachedAst(template);const analysis=(0,_analyzerts.analyzeFromAst)(ast,template,inputSchema,{identifierSchemas:options?.identifierSchemas,helpers:this.helpers});if(!analysis.valid){return{analysis,value:undefined}}const value=(0,_executorts.executeFromAst)(ast,template,data,{identifierData:options?.identifierData,hbs:this.hbs,compilationCache:this.compilationCache});return{analysis,value}}registerHelper(name,definition){this.helpers.set(name,definition);this.hbs.registerHelper(name,definition.fn);this.compilationCache.clear();return this}unregisterHelper(name){this.helpers.delete(name);this.hbs.unregisterHelper(name);this.compilationCache.clear();return this}hasHelper(name){return this.helpers.has(name)}clearCaches(){this.astCache.clear();this.compilationCache.clear()}getCachedAst(template){let ast=this.astCache.get(template);if(!ast){ast=(0,_parserts.parse)(template);this.astCache.set(template,ast)}return ast}constructor(options={}){_define_property(this,"hbs",void 0);_define_property(this,"astCache",void 0);_define_property(this,"compilationCache",void 0);_define_property(this,"helpers",new Map);this.hbs=_handlebars.default.create();this.astCache=new _utils.LRUCache(options.astCacheSize??256);this.compilationCache=new _utils.LRUCache(options.compilationCacheSize??256);new _indexts.MathHelpers().register(this);new _indexts.LogicalHelpers().register(this);if(options.helpers){for(const helper of options.helpers){const{name,...definition}=helper;this.registerHelper(name,definition)}}}}
2
2
  //# sourceMappingURL=typebars.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/typebars.ts"],"sourcesContent":["import Handlebars from \"handlebars\";\nimport type { JSONSchema7 } from \"json-schema\";\nimport { analyzeFromAst } from \"./analyzer.ts\";\nimport {\n\tCompiledTemplate,\n\ttype CompiledTemplateOptions,\n} from \"./compiled-template.ts\";\nimport { TemplateAnalysisError } from \"./errors.ts\";\nimport { executeFromAst } from \"./executor.ts\";\nimport { LogicalHelpers, MathHelpers } from \"./helpers/index.ts\";\nimport { parse } from \"./parser.ts\";\nimport type {\n\tAnalysisResult,\n\tAnalyzeAndExecuteOptions,\n\tExecuteOptions,\n\tHelperDefinition,\n\tTemplateEngineOptions,\n\tTemplateInput,\n\tValidationResult,\n} from \"./types.ts\";\nimport {\n\tinferPrimitiveSchema,\n\tisLiteralInput,\n\tisObjectInput,\n} from \"./types.ts\";\nimport {\n\taggregateObjectAnalysis,\n\taggregateObjectAnalysisAndExecution,\n\tLRUCache,\n} from \"./utils\";\n\n// ─── Typebars ────────────────────────────────────────────────────────────────\n// Public entry point of the template engine. Orchestrates three phases:\n//\n// 1. **Parsing** — transforms the template string into an AST (via Handlebars)\n// 2. **Analysis** — static validation + return type inference\n// 3. **Execution** — renders the template with real data\n//\n// ─── Architecture v2 ─────────────────────────────────────────────────────────\n// - **LRU cache** for parsed ASTs and compiled Handlebars templates\n// - **Isolated Handlebars environment** per instance (custom helpers)\n// - **`compile()` pattern**: parse-once / execute-many\n// - **`validate()` method**: API shortcut without `outputSchema`\n// - **`registerHelper()`**: custom helpers with static typing\n// - **`ExecuteOptions`**: options object for `execute()`\n//\n// ─── Template Identifiers ────────────────────────────────────────────────────\n// The `{{key:N}}` syntax allows referencing variables from specific data\n// sources, identified by an integer N.\n//\n// - `identifierSchemas`: mapping `{ [id]: JSONSchema7 }` for static analysis\n// - `identifierData`: mapping `{ [id]: Record<string, unknown> }` for execution\n//\n// Usage:\n// engine.execute(\"{{meetingId:1}}\", data, { identifierData: { 1: node1Data } });\n// engine.analyze(\"{{meetingId:1}}\", schema, { 1: node1Schema });\n\n// ─── Main Class ──────────────────────────────────────────────────────────────\n\nexport class Typebars {\n\t/** Isolated Handlebars environment — each engine has its own helpers */\n\tprivate readonly hbs: typeof Handlebars;\n\n\t/** LRU cache of parsed ASTs (avoids re-parsing) */\n\tprivate readonly astCache: LRUCache<string, hbs.AST.Program>;\n\n\t/** LRU cache of compiled Handlebars templates (avoids recompilation) */\n\tprivate readonly compilationCache: LRUCache<\n\t\tstring,\n\t\tHandlebarsTemplateDelegate\n\t>;\n\n\t/** Custom helpers registered on this instance */\n\tprivate readonly helpers = new Map<string, HelperDefinition>();\n\n\tconstructor(options: TemplateEngineOptions = {}) {\n\t\tthis.hbs = Handlebars.create();\n\t\tthis.astCache = new LRUCache(options.astCacheSize ?? 256);\n\t\tthis.compilationCache = new LRUCache(options.compilationCacheSize ?? 256);\n\n\t\t// ── Built-in helpers ─────────────────────────────────────────────\n\t\tnew MathHelpers().register(this);\n\t\tnew LogicalHelpers().register(this);\n\n\t\t// ── Custom helpers via options ───────────────────────────────────\n\t\tif (options.helpers) {\n\t\t\tfor (const helper of options.helpers) {\n\t\t\t\tconst { name, ...definition } = helper;\n\t\t\t\tthis.registerHelper(name, definition);\n\t\t\t}\n\t\t}\n\t}\n\n\t// ─── Compilation ───────────────────────────────────────────────────────\n\n\t/**\n\t * Compiles a template and returns a `CompiledTemplate` ready to be\n\t * executed or analyzed without re-parsing.\n\t *\n\t * Accepts a `TemplateInput`: string, number, boolean, null, or object.\n\t * For objects, each property is compiled recursively.\n\t *\n\t * @param template - The template to compile\n\t * @returns A reusable `CompiledTemplate`\n\t */\n\tcompile(template: TemplateInput): CompiledTemplate {\n\t\tif (isObjectInput(template)) {\n\t\t\tconst children: Record<string, CompiledTemplate> = {};\n\t\t\tfor (const [key, value] of Object.entries(template)) {\n\t\t\t\tchildren[key] = this.compile(value);\n\t\t\t}\n\t\t\treturn CompiledTemplate.fromObject(children, {\n\t\t\t\thelpers: this.helpers,\n\t\t\t\thbs: this.hbs,\n\t\t\t\tcompilationCache: this.compilationCache,\n\t\t\t});\n\t\t}\n\t\tif (isLiteralInput(template)) {\n\t\t\treturn CompiledTemplate.fromLiteral(template, {\n\t\t\t\thelpers: this.helpers,\n\t\t\t\thbs: this.hbs,\n\t\t\t\tcompilationCache: this.compilationCache,\n\t\t\t});\n\t\t}\n\t\tconst ast = this.getCachedAst(template);\n\t\tconst options: CompiledTemplateOptions = {\n\t\t\thelpers: this.helpers,\n\t\t\thbs: this.hbs,\n\t\t\tcompilationCache: this.compilationCache,\n\t\t};\n\t\treturn CompiledTemplate.fromTemplate(ast, template, options);\n\t}\n\n\t// ─── Static Analysis ─────────────────────────────────────────────────────\n\n\t/**\n\t * Statically analyzes a template against a JSON Schema v7 describing\n\t * the available context.\n\t *\n\t * Accepts a `TemplateInput`: string, number, boolean, null, or object.\n\t * For objects, each property is analyzed recursively and the\n\t * `outputSchema` reflects the object structure with resolved types.\n\t *\n\t * @param template - The template to analyze\n\t * @param inputSchema - JSON Schema v7 describing the available variables\n\t * @param identifierSchemas - (optional) Schemas by identifier `{ [id]: JSONSchema7 }`\n\t */\n\tanalyze(\n\t\ttemplate: TemplateInput,\n\t\tinputSchema: JSONSchema7,\n\t\tidentifierSchemas?: Record<number, JSONSchema7>,\n\t): AnalysisResult {\n\t\tif (isObjectInput(template)) {\n\t\t\treturn aggregateObjectAnalysis(Object.keys(template), (key) =>\n\t\t\t\tthis.analyze(\n\t\t\t\t\ttemplate[key] as TemplateInput,\n\t\t\t\t\tinputSchema,\n\t\t\t\t\tidentifierSchemas,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tif (isLiteralInput(template)) {\n\t\t\treturn {\n\t\t\t\tvalid: true,\n\t\t\t\tdiagnostics: [],\n\t\t\t\toutputSchema: inferPrimitiveSchema(template),\n\t\t\t};\n\t\t}\n\t\tconst ast = this.getCachedAst(template);\n\t\treturn analyzeFromAst(ast, template, inputSchema, {\n\t\t\tidentifierSchemas,\n\t\t\thelpers: this.helpers,\n\t\t});\n\t}\n\n\t// ─── Validation ──────────────────────────────────────────────────────────\n\n\t/**\n\t * Validates a template against a schema without returning the output type.\n\t *\n\t * This is an API shortcut for `analyze()` that only returns `valid` and\n\t * `diagnostics`, without `outputSchema`. The full analysis (including type\n\t * inference) is executed internally — this method provides no performance\n\t * gain, only a simplified API.\n\t *\n\t * @param template - The template to validate\n\t * @param inputSchema - JSON Schema v7 describing the available variables\n\t * @param identifierSchemas - (optional) Schemas by identifier\n\t */\n\tvalidate(\n\t\ttemplate: TemplateInput,\n\t\tinputSchema: JSONSchema7,\n\t\tidentifierSchemas?: Record<number, JSONSchema7>,\n\t): ValidationResult {\n\t\tconst analysis = this.analyze(template, inputSchema, identifierSchemas);\n\t\treturn {\n\t\t\tvalid: analysis.valid,\n\t\t\tdiagnostics: analysis.diagnostics,\n\t\t};\n\t}\n\n\t// ─── Syntax Validation ───────────────────────────────────────────────────\n\n\t/**\n\t * Checks only that the template syntax is valid (parsing).\n\t * Does not require a schema — useful for quick feedback in an editor.\n\t *\n\t * For objects, recursively checks each property.\n\t *\n\t * @param template - The template to validate\n\t * @returns `true` if the template is syntactically correct\n\t */\n\tisValidSyntax(template: TemplateInput): boolean {\n\t\tif (isObjectInput(template)) {\n\t\t\treturn Object.values(template).every((v) => this.isValidSyntax(v));\n\t\t}\n\t\tif (isLiteralInput(template)) return true;\n\t\ttry {\n\t\t\tthis.getCachedAst(template);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// ─── Execution ───────────────────────────────────────────────────────────\n\n\t/**\n\t * Executes a template with the provided data.\n\t *\n\t * Accepts a `TemplateInput`: string, number, boolean, null, or object.\n\t * For objects, each property is executed recursively and an object with\n\t * resolved values is returned.\n\t *\n\t * If a `schema` is provided in options, static analysis is performed\n\t * before execution. A `TemplateAnalysisError` is thrown on errors.\n\t *\n\t * @param template - The template to execute\n\t * @param data - The context data for rendering\n\t * @param options - Execution options (schema, identifierData, identifierSchemas)\n\t * @returns The execution result\n\t */\n\texecute(\n\t\ttemplate: TemplateInput,\n\t\tdata: Record<string, unknown>,\n\t\toptions?: ExecuteOptions,\n\t): unknown {\n\t\t// ── Object template → recursive execution ────────────────────────────\n\t\tif (isObjectInput(template)) {\n\t\t\tconst result: Record<string, unknown> = {};\n\t\t\tfor (const [key, value] of Object.entries(template)) {\n\t\t\t\tresult[key] = this.execute(value, data, options);\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\n\t\t// ── Passthrough for literal values ────────────────────────────────────\n\t\tif (isLiteralInput(template)) return template;\n\n\t\t// ── Parse once ───────────────────────────────────────────────────────\n\t\tconst ast = this.getCachedAst(template);\n\n\t\t// ── Pre-execution static validation ──────────────────────────────────\n\t\tif (options?.schema) {\n\t\t\tconst analysis = analyzeFromAst(ast, template, options.schema, {\n\t\t\t\tidentifierSchemas: options.identifierSchemas,\n\t\t\t\thelpers: this.helpers,\n\t\t\t});\n\t\t\tif (!analysis.valid) {\n\t\t\t\tthrow new TemplateAnalysisError(analysis.diagnostics);\n\t\t\t}\n\t\t}\n\n\t\t// ── Execution ────────────────────────────────────────────────────────\n\t\treturn executeFromAst(ast, template, data, {\n\t\t\tidentifierData: options?.identifierData,\n\t\t\thbs: this.hbs,\n\t\t\tcompilationCache: this.compilationCache,\n\t\t});\n\t}\n\n\t// ─── Combined Shortcuts ──────────────────────────────────────────────────\n\n\t/**\n\t * Analyzes a template and, if valid, executes it with the provided data.\n\t * Returns both the analysis result and the executed value.\n\t *\n\t * For objects, each property is analyzed and executed recursively.\n\t * The entire object is considered invalid if at least one property is.\n\t *\n\t * @param template - The template\n\t * @param inputSchema - JSON Schema v7 describing the available variables\n\t * @param data - The context data for rendering\n\t * @param options - (optional) Options for template identifiers\n\t * @returns An object `{ analysis, value }` where `value` is `undefined`\n\t * if analysis failed.\n\t */\n\tanalyzeAndExecute(\n\t\ttemplate: TemplateInput,\n\t\tinputSchema: JSONSchema7,\n\t\tdata: Record<string, unknown>,\n\t\toptions?: AnalyzeAndExecuteOptions,\n\t): { analysis: AnalysisResult; value: unknown } {\n\t\tif (isObjectInput(template)) {\n\t\t\treturn aggregateObjectAnalysisAndExecution(Object.keys(template), (key) =>\n\t\t\t\tthis.analyzeAndExecute(\n\t\t\t\t\ttemplate[key] as TemplateInput,\n\t\t\t\t\tinputSchema,\n\t\t\t\t\tdata,\n\t\t\t\t\toptions,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\n\t\tif (isLiteralInput(template)) {\n\t\t\treturn {\n\t\t\t\tanalysis: {\n\t\t\t\t\tvalid: true,\n\t\t\t\t\tdiagnostics: [],\n\t\t\t\t\toutputSchema: inferPrimitiveSchema(template),\n\t\t\t\t},\n\t\t\t\tvalue: template,\n\t\t\t};\n\t\t}\n\n\t\tconst ast = this.getCachedAst(template);\n\t\tconst analysis = analyzeFromAst(ast, template, inputSchema, {\n\t\t\tidentifierSchemas: options?.identifierSchemas,\n\t\t\thelpers: this.helpers,\n\t\t});\n\n\t\tif (!analysis.valid) {\n\t\t\treturn { analysis, value: undefined };\n\t\t}\n\n\t\tconst value = executeFromAst(ast, template, data, {\n\t\t\tidentifierData: options?.identifierData,\n\t\t\thbs: this.hbs,\n\t\t\tcompilationCache: this.compilationCache,\n\t\t});\n\t\treturn { analysis, value };\n\t}\n\n\t// ─── Custom Helper Management ──────────────────────────────────────────\n\n\t/**\n\t * Registers a custom helper on this engine instance.\n\t *\n\t * The helper is available for both execution (via Handlebars) and\n\t * static analysis (via its declared `returnType`).\n\t *\n\t * @param name - Helper name (e.g. `\"uppercase\"`)\n\t * @param definition - Helper definition (implementation + return type)\n\t * @returns `this` to allow chaining\n\t */\n\tregisterHelper(name: string, definition: HelperDefinition): this {\n\t\tthis.helpers.set(name, definition);\n\t\tthis.hbs.registerHelper(name, definition.fn);\n\n\t\t// Invalidate the compilation cache because helpers have changed\n\t\tthis.compilationCache.clear();\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Removes a custom helper from this engine instance.\n\t *\n\t * @param name - Name of the helper to remove\n\t * @returns `this` to allow chaining\n\t */\n\tunregisterHelper(name: string): this {\n\t\tthis.helpers.delete(name);\n\t\tthis.hbs.unregisterHelper(name);\n\n\t\t// Invalidate the compilation cache\n\t\tthis.compilationCache.clear();\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Checks whether a helper is registered on this instance.\n\t *\n\t * @param name - Helper name\n\t * @returns `true` if the helper is registered\n\t */\n\thasHelper(name: string): boolean {\n\t\treturn this.helpers.has(name);\n\t}\n\n\t// ─── Cache Management ──────────────────────────────────────────────────\n\n\t/**\n\t * Clears all internal caches (AST + compilation).\n\t *\n\t * Useful after a configuration change or to free memory.\n\t */\n\tclearCaches(): void {\n\t\tthis.astCache.clear();\n\t\tthis.compilationCache.clear();\n\t}\n\n\t// ─── Internals ─────────────────────────────────────────────────────────\n\n\t/**\n\t * Retrieves the AST of a template from the cache, or parses and caches it.\n\t */\n\tprivate getCachedAst(template: string): hbs.AST.Program {\n\t\tlet ast = this.astCache.get(template);\n\t\tif (!ast) {\n\t\t\tast = parse(template);\n\t\t\tthis.astCache.set(template, ast);\n\t\t}\n\t\treturn ast;\n\t}\n}\n"],"names":["Typebars","compile","template","isObjectInput","children","key","value","Object","entries","CompiledTemplate","fromObject","helpers","hbs","compilationCache","isLiteralInput","fromLiteral","ast","getCachedAst","options","fromTemplate","analyze","inputSchema","identifierSchemas","aggregateObjectAnalysis","keys","valid","diagnostics","outputSchema","inferPrimitiveSchema","analyzeFromAst","validate","analysis","isValidSyntax","values","every","v","execute","data","result","schema","TemplateAnalysisError","executeFromAst","identifierData","analyzeAndExecute","aggregateObjectAnalysisAndExecution","undefined","registerHelper","name","definition","set","fn","clear","unregisterHelper","delete","hasHelper","has","clearCaches","astCache","get","parse","Map","Handlebars","create","LRUCache","astCacheSize","compilationCacheSize","MathHelpers","register","LogicalHelpers","helper"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/typebars.ts"],"sourcesContent":["import Handlebars from \"handlebars\";\nimport type { JSONSchema7 } from \"json-schema\";\nimport { analyzeFromAst } from \"./analyzer.ts\";\nimport {\n\tCompiledTemplate,\n\ttype CompiledTemplateOptions,\n} from \"./compiled-template.ts\";\nimport { TemplateAnalysisError } from \"./errors.ts\";\nimport { executeFromAst } from \"./executor.ts\";\nimport { LogicalHelpers, MathHelpers } from \"./helpers/index.ts\";\nimport { parse } from \"./parser.ts\";\nimport type {\n\tAnalysisResult,\n\tAnalyzeAndExecuteOptions,\n\tExecuteOptions,\n\tHelperDefinition,\n\tTemplateEngineOptions,\n\tTemplateInput,\n\tValidationResult,\n} from \"./types.ts\";\nimport {\n\tinferPrimitiveSchema,\n\tisArrayInput,\n\tisLiteralInput,\n\tisObjectInput,\n} from \"./types.ts\";\nimport {\n\taggregateArrayAnalysis,\n\taggregateArrayAnalysisAndExecution,\n\taggregateObjectAnalysis,\n\taggregateObjectAnalysisAndExecution,\n\tLRUCache,\n} from \"./utils\";\n\n// ─── Typebars ────────────────────────────────────────────────────────────────\n// Public entry point of the template engine. Orchestrates three phases:\n//\n// 1. **Parsing** — transforms the template string into an AST (via Handlebars)\n// 2. **Analysis** — static validation + return type inference\n// 3. **Execution** — renders the template with real data\n//\n// ─── Architecture v2 ─────────────────────────────────────────────────────────\n// - **LRU cache** for parsed ASTs and compiled Handlebars templates\n// - **Isolated Handlebars environment** per instance (custom helpers)\n// - **`compile()` pattern**: parse-once / execute-many\n// - **`validate()` method**: API shortcut without `outputSchema`\n// - **`registerHelper()`**: custom helpers with static typing\n// - **`ExecuteOptions`**: options object for `execute()`\n//\n// ─── Template Identifiers ────────────────────────────────────────────────────\n// The `{{key:N}}` syntax allows referencing variables from specific data\n// sources, identified by an integer N.\n//\n// - `identifierSchemas`: mapping `{ [id]: JSONSchema7 }` for static analysis\n// - `identifierData`: mapping `{ [id]: Record<string, unknown> }` for execution\n//\n// Usage:\n// engine.execute(\"{{meetingId:1}}\", data, { identifierData: { 1: node1Data } });\n// engine.analyze(\"{{meetingId:1}}\", schema, { 1: node1Schema });\n\n// ─── Main Class ──────────────────────────────────────────────────────────────\n\nexport class Typebars {\n\t/** Isolated Handlebars environment — each engine has its own helpers */\n\tprivate readonly hbs: typeof Handlebars;\n\n\t/** LRU cache of parsed ASTs (avoids re-parsing) */\n\tprivate readonly astCache: LRUCache<string, hbs.AST.Program>;\n\n\t/** LRU cache of compiled Handlebars templates (avoids recompilation) */\n\tprivate readonly compilationCache: LRUCache<\n\t\tstring,\n\t\tHandlebarsTemplateDelegate\n\t>;\n\n\t/** Custom helpers registered on this instance */\n\tprivate readonly helpers = new Map<string, HelperDefinition>();\n\n\tconstructor(options: TemplateEngineOptions = {}) {\n\t\tthis.hbs = Handlebars.create();\n\t\tthis.astCache = new LRUCache(options.astCacheSize ?? 256);\n\t\tthis.compilationCache = new LRUCache(options.compilationCacheSize ?? 256);\n\n\t\t// ── Built-in helpers ─────────────────────────────────────────────\n\t\tnew MathHelpers().register(this);\n\t\tnew LogicalHelpers().register(this);\n\n\t\t// ── Custom helpers via options ───────────────────────────────────\n\t\tif (options.helpers) {\n\t\t\tfor (const helper of options.helpers) {\n\t\t\t\tconst { name, ...definition } = helper;\n\t\t\t\tthis.registerHelper(name, definition);\n\t\t\t}\n\t\t}\n\t}\n\n\t// ─── Compilation ───────────────────────────────────────────────────────\n\n\t/**\n\t * Compiles a template and returns a `CompiledTemplate` ready to be\n\t * executed or analyzed without re-parsing.\n\t *\n\t * Accepts a `TemplateInput`: string, number, boolean, null, or object.\n\t * For objects, each property is compiled recursively.\n\t *\n\t * @param template - The template to compile\n\t * @returns A reusable `CompiledTemplate`\n\t */\n\tcompile(template: TemplateInput): CompiledTemplate {\n\t\tif (isArrayInput(template)) {\n\t\t\tconst children: CompiledTemplate[] = [];\n\t\t\tfor (const element of template) {\n\t\t\t\tchildren.push(this.compile(element));\n\t\t\t}\n\t\t\treturn CompiledTemplate.fromArray(children, {\n\t\t\t\thelpers: this.helpers,\n\t\t\t\thbs: this.hbs,\n\t\t\t\tcompilationCache: this.compilationCache,\n\t\t\t});\n\t\t}\n\t\tif (isObjectInput(template)) {\n\t\t\tconst children: Record<string, CompiledTemplate> = {};\n\t\t\tfor (const [key, value] of Object.entries(template)) {\n\t\t\t\tchildren[key] = this.compile(value);\n\t\t\t}\n\t\t\treturn CompiledTemplate.fromObject(children, {\n\t\t\t\thelpers: this.helpers,\n\t\t\t\thbs: this.hbs,\n\t\t\t\tcompilationCache: this.compilationCache,\n\t\t\t});\n\t\t}\n\t\tif (isLiteralInput(template)) {\n\t\t\treturn CompiledTemplate.fromLiteral(template, {\n\t\t\t\thelpers: this.helpers,\n\t\t\t\thbs: this.hbs,\n\t\t\t\tcompilationCache: this.compilationCache,\n\t\t\t});\n\t\t}\n\t\tconst ast = this.getCachedAst(template);\n\t\tconst options: CompiledTemplateOptions = {\n\t\t\thelpers: this.helpers,\n\t\t\thbs: this.hbs,\n\t\t\tcompilationCache: this.compilationCache,\n\t\t};\n\t\treturn CompiledTemplate.fromTemplate(ast, template, options);\n\t}\n\n\t// ─── Static Analysis ─────────────────────────────────────────────────────\n\n\t/**\n\t * Statically analyzes a template against a JSON Schema v7 describing\n\t * the available context.\n\t *\n\t * Accepts a `TemplateInput`: string, number, boolean, null, or object.\n\t * For objects, each property is analyzed recursively and the\n\t * `outputSchema` reflects the object structure with resolved types.\n\t *\n\t * @param template - The template to analyze\n\t * @param inputSchema - JSON Schema v7 describing the available variables\n\t * @param identifierSchemas - (optional) Schemas by identifier `{ [id]: JSONSchema7 }`\n\t */\n\tanalyze(\n\t\ttemplate: TemplateInput,\n\t\tinputSchema: JSONSchema7,\n\t\tidentifierSchemas?: Record<number, JSONSchema7>,\n\t): AnalysisResult {\n\t\tif (isArrayInput(template)) {\n\t\t\treturn aggregateArrayAnalysis(template.length, (index) =>\n\t\t\t\tthis.analyze(\n\t\t\t\t\ttemplate[index] as TemplateInput,\n\t\t\t\t\tinputSchema,\n\t\t\t\t\tidentifierSchemas,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tif (isObjectInput(template)) {\n\t\t\treturn aggregateObjectAnalysis(Object.keys(template), (key) =>\n\t\t\t\tthis.analyze(\n\t\t\t\t\ttemplate[key] as TemplateInput,\n\t\t\t\t\tinputSchema,\n\t\t\t\t\tidentifierSchemas,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tif (isLiteralInput(template)) {\n\t\t\treturn {\n\t\t\t\tvalid: true,\n\t\t\t\tdiagnostics: [],\n\t\t\t\toutputSchema: inferPrimitiveSchema(template),\n\t\t\t};\n\t\t}\n\t\tconst ast = this.getCachedAst(template);\n\t\treturn analyzeFromAst(ast, template, inputSchema, {\n\t\t\tidentifierSchemas,\n\t\t\thelpers: this.helpers,\n\t\t});\n\t}\n\n\t// ─── Validation ──────────────────────────────────────────────────────────\n\n\t/**\n\t * Validates a template against a schema without returning the output type.\n\t *\n\t * This is an API shortcut for `analyze()` that only returns `valid` and\n\t * `diagnostics`, without `outputSchema`. The full analysis (including type\n\t * inference) is executed internally — this method provides no performance\n\t * gain, only a simplified API.\n\t *\n\t * @param template - The template to validate\n\t * @param inputSchema - JSON Schema v7 describing the available variables\n\t * @param identifierSchemas - (optional) Schemas by identifier\n\t */\n\tvalidate(\n\t\ttemplate: TemplateInput,\n\t\tinputSchema: JSONSchema7,\n\t\tidentifierSchemas?: Record<number, JSONSchema7>,\n\t): ValidationResult {\n\t\tconst analysis = this.analyze(template, inputSchema, identifierSchemas);\n\t\treturn {\n\t\t\tvalid: analysis.valid,\n\t\t\tdiagnostics: analysis.diagnostics,\n\t\t};\n\t}\n\n\t// ─── Syntax Validation ───────────────────────────────────────────────────\n\n\t/**\n\t * Checks only that the template syntax is valid (parsing).\n\t * Does not require a schema — useful for quick feedback in an editor.\n\t *\n\t * For objects, recursively checks each property.\n\t *\n\t * @param template - The template to validate\n\t * @returns `true` if the template is syntactically correct\n\t */\n\tisValidSyntax(template: TemplateInput): boolean {\n\t\tif (isArrayInput(template)) {\n\t\t\treturn template.every((v) => this.isValidSyntax(v));\n\t\t}\n\t\tif (isObjectInput(template)) {\n\t\t\treturn Object.values(template).every((v) => this.isValidSyntax(v));\n\t\t}\n\t\tif (isLiteralInput(template)) return true;\n\t\ttry {\n\t\t\tthis.getCachedAst(template);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// ─── Execution ───────────────────────────────────────────────────────────\n\n\t/**\n\t * Executes a template with the provided data.\n\t *\n\t * Accepts a `TemplateInput`: string, number, boolean, null, or object.\n\t * For objects, each property is executed recursively and an object with\n\t * resolved values is returned.\n\t *\n\t * If a `schema` is provided in options, static analysis is performed\n\t * before execution. A `TemplateAnalysisError` is thrown on errors.\n\t *\n\t * @param template - The template to execute\n\t * @param data - The context data for rendering\n\t * @param options - Execution options (schema, identifierData, identifierSchemas)\n\t * @returns The execution result\n\t */\n\texecute(\n\t\ttemplate: TemplateInput,\n\t\tdata: Record<string, unknown>,\n\t\toptions?: ExecuteOptions,\n\t): unknown {\n\t\t// ── Array template → recursive execution ─────────────────────────────\n\t\tif (isArrayInput(template)) {\n\t\t\tconst result: unknown[] = [];\n\t\t\tfor (const element of template) {\n\t\t\t\tresult.push(this.execute(element, data, options));\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\n\t\t// ── Object template → recursive execution ────────────────────────────\n\t\tif (isObjectInput(template)) {\n\t\t\tconst result: Record<string, unknown> = {};\n\t\t\tfor (const [key, value] of Object.entries(template)) {\n\t\t\t\tresult[key] = this.execute(value, data, options);\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\n\t\t// ── Passthrough for literal values ────────────────────────────────────\n\t\tif (isLiteralInput(template)) return template;\n\n\t\t// ── Parse once ───────────────────────────────────────────────────────\n\t\tconst ast = this.getCachedAst(template);\n\n\t\t// ── Pre-execution static validation ──────────────────────────────────\n\t\tif (options?.schema) {\n\t\t\tconst analysis = analyzeFromAst(ast, template, options.schema, {\n\t\t\t\tidentifierSchemas: options.identifierSchemas,\n\t\t\t\thelpers: this.helpers,\n\t\t\t});\n\t\t\tif (!analysis.valid) {\n\t\t\t\tthrow new TemplateAnalysisError(analysis.diagnostics);\n\t\t\t}\n\t\t}\n\n\t\t// ── Execution ────────────────────────────────────────────────────────\n\t\treturn executeFromAst(ast, template, data, {\n\t\t\tidentifierData: options?.identifierData,\n\t\t\thbs: this.hbs,\n\t\t\tcompilationCache: this.compilationCache,\n\t\t});\n\t}\n\n\t// ─── Combined Shortcuts ──────────────────────────────────────────────────\n\n\t/**\n\t * Analyzes a template and, if valid, executes it with the provided data.\n\t * Returns both the analysis result and the executed value.\n\t *\n\t * For objects, each property is analyzed and executed recursively.\n\t * The entire object is considered invalid if at least one property is.\n\t *\n\t * @param template - The template\n\t * @param inputSchema - JSON Schema v7 describing the available variables\n\t * @param data - The context data for rendering\n\t * @param options - (optional) Options for template identifiers\n\t * @returns An object `{ analysis, value }` where `value` is `undefined`\n\t * if analysis failed.\n\t */\n\tanalyzeAndExecute(\n\t\ttemplate: TemplateInput,\n\t\tinputSchema: JSONSchema7,\n\t\tdata: Record<string, unknown>,\n\t\toptions?: AnalyzeAndExecuteOptions,\n\t): { analysis: AnalysisResult; value: unknown } {\n\t\tif (isArrayInput(template)) {\n\t\t\treturn aggregateArrayAnalysisAndExecution(template.length, (index) =>\n\t\t\t\tthis.analyzeAndExecute(\n\t\t\t\t\ttemplate[index] as TemplateInput,\n\t\t\t\t\tinputSchema,\n\t\t\t\t\tdata,\n\t\t\t\t\toptions,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tif (isObjectInput(template)) {\n\t\t\treturn aggregateObjectAnalysisAndExecution(Object.keys(template), (key) =>\n\t\t\t\tthis.analyzeAndExecute(\n\t\t\t\t\ttemplate[key] as TemplateInput,\n\t\t\t\t\tinputSchema,\n\t\t\t\t\tdata,\n\t\t\t\t\toptions,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\n\t\tif (isLiteralInput(template)) {\n\t\t\treturn {\n\t\t\t\tanalysis: {\n\t\t\t\t\tvalid: true,\n\t\t\t\t\tdiagnostics: [],\n\t\t\t\t\toutputSchema: inferPrimitiveSchema(template),\n\t\t\t\t},\n\t\t\t\tvalue: template,\n\t\t\t};\n\t\t}\n\n\t\tconst ast = this.getCachedAst(template);\n\t\tconst analysis = analyzeFromAst(ast, template, inputSchema, {\n\t\t\tidentifierSchemas: options?.identifierSchemas,\n\t\t\thelpers: this.helpers,\n\t\t});\n\n\t\tif (!analysis.valid) {\n\t\t\treturn { analysis, value: undefined };\n\t\t}\n\n\t\tconst value = executeFromAst(ast, template, data, {\n\t\t\tidentifierData: options?.identifierData,\n\t\t\thbs: this.hbs,\n\t\t\tcompilationCache: this.compilationCache,\n\t\t});\n\t\treturn { analysis, value };\n\t}\n\n\t// ─── Custom Helper Management ──────────────────────────────────────────\n\n\t/**\n\t * Registers a custom helper on this engine instance.\n\t *\n\t * The helper is available for both execution (via Handlebars) and\n\t * static analysis (via its declared `returnType`).\n\t *\n\t * @param name - Helper name (e.g. `\"uppercase\"`)\n\t * @param definition - Helper definition (implementation + return type)\n\t * @returns `this` to allow chaining\n\t */\n\tregisterHelper(name: string, definition: HelperDefinition): this {\n\t\tthis.helpers.set(name, definition);\n\t\tthis.hbs.registerHelper(name, definition.fn);\n\n\t\t// Invalidate the compilation cache because helpers have changed\n\t\tthis.compilationCache.clear();\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Removes a custom helper from this engine instance.\n\t *\n\t * @param name - Name of the helper to remove\n\t * @returns `this` to allow chaining\n\t */\n\tunregisterHelper(name: string): this {\n\t\tthis.helpers.delete(name);\n\t\tthis.hbs.unregisterHelper(name);\n\n\t\t// Invalidate the compilation cache\n\t\tthis.compilationCache.clear();\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Checks whether a helper is registered on this instance.\n\t *\n\t * @param name - Helper name\n\t * @returns `true` if the helper is registered\n\t */\n\thasHelper(name: string): boolean {\n\t\treturn this.helpers.has(name);\n\t}\n\n\t// ─── Cache Management ──────────────────────────────────────────────────\n\n\t/**\n\t * Clears all internal caches (AST + compilation).\n\t *\n\t * Useful after a configuration change or to free memory.\n\t */\n\tclearCaches(): void {\n\t\tthis.astCache.clear();\n\t\tthis.compilationCache.clear();\n\t}\n\n\t// ─── Internals ─────────────────────────────────────────────────────────\n\n\t/**\n\t * Retrieves the AST of a template from the cache, or parses and caches it.\n\t */\n\tprivate getCachedAst(template: string): hbs.AST.Program {\n\t\tlet ast = this.astCache.get(template);\n\t\tif (!ast) {\n\t\t\tast = parse(template);\n\t\t\tthis.astCache.set(template, ast);\n\t\t}\n\t\treturn ast;\n\t}\n}\n"],"names":["Typebars","compile","template","isArrayInput","children","element","push","CompiledTemplate","fromArray","helpers","hbs","compilationCache","isObjectInput","key","value","Object","entries","fromObject","isLiteralInput","fromLiteral","ast","getCachedAst","options","fromTemplate","analyze","inputSchema","identifierSchemas","aggregateArrayAnalysis","length","index","aggregateObjectAnalysis","keys","valid","diagnostics","outputSchema","inferPrimitiveSchema","analyzeFromAst","validate","analysis","isValidSyntax","every","v","values","execute","data","result","schema","TemplateAnalysisError","executeFromAst","identifierData","analyzeAndExecute","aggregateArrayAnalysisAndExecution","aggregateObjectAnalysisAndExecution","undefined","registerHelper","name","definition","set","fn","clear","unregisterHelper","delete","hasHelper","has","clearCaches","astCache","get","parse","Map","Handlebars","create","LRUCache","astCacheSize","compilationCacheSize","MathHelpers","register","LogicalHelpers","helper"],"mappings":"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"}
@@ -15,6 +15,16 @@ import type { FromSchema, JSONSchema } from "json-schema-to-ts";
15
15
  export interface TemplateInputObject {
16
16
  [key: string]: TemplateInput;
17
17
  }
18
+ /**
19
+ * Array where each element is a `TemplateInput` (recursive).
20
+ *
21
+ * Allows passing an array as a template:
22
+ * ```
23
+ * engine.analyze(["{{name}}", "{{age}}"], inputSchema);
24
+ * engine.execute(["{{name}}", 42], data);
25
+ * ```
26
+ */
27
+ export type TemplateInputArray = TemplateInput[];
18
28
  /**
19
29
  * Input type accepted by the template engine.
20
30
  *
@@ -22,20 +32,29 @@ export interface TemplateInputObject {
22
32
  * - `number` → numeric literal (passthrough)
23
33
  * - `boolean` → boolean literal (passthrough)
24
34
  * - `null` → null literal (passthrough)
35
+ * - `TemplateInputArray` → array where each element is a `TemplateInput`
25
36
  * - `TemplateInputObject` → object where each property is a `TemplateInput`
26
37
  */
27
- export type TemplateInput = string | number | boolean | null | TemplateInputObject;
38
+ export type TemplateInput = string | number | boolean | null | TemplateInputArray | TemplateInputObject;
28
39
  /**
29
40
  * Checks whether a value is a non-string primitive literal (number, boolean, null).
30
41
  * These values are treated as passthrough by the engine.
31
42
  *
32
- * Note: objects (`TemplateInputObject`) are NOT literals.
43
+ * Note: objects (`TemplateInputObject`) and arrays (`TemplateInputArray`) are NOT literals.
33
44
  */
34
45
  export declare function isLiteralInput(input: TemplateInput): input is number | boolean | null;
46
+ /**
47
+ * Checks whether a value is a template array (`TemplateInputArray`).
48
+ * Template arrays are processed recursively by the engine:
49
+ * each element is analyzed/executed individually and the result is an array.
50
+ */
51
+ export declare function isArrayInput(input: TemplateInput): input is TemplateInputArray;
35
52
  /**
36
53
  * Checks whether a value is a template object (`TemplateInputObject`).
37
54
  * Template objects are processed recursively by the engine:
38
55
  * each property is analyzed/executed individually.
56
+ *
57
+ * Note: arrays are excluded — use `isArrayInput()` first.
39
58
  */
40
59
  export declare function isObjectInput(input: TemplateInput): input is TemplateInputObject;
41
60
  /**
package/dist/cjs/types.js CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});function _export(target,all){for(var name in all)Object.defineProperty(target,name,{enumerable:true,get:Object.getOwnPropertyDescriptor(all,name).get})}_export(exports,{get defineHelper(){return defineHelper},get inferPrimitiveSchema(){return inferPrimitiveSchema},get isLiteralInput(){return isLiteralInput},get isObjectInput(){return isObjectInput}});function isLiteralInput(input){return input===null||typeof input!=="string"&&typeof input!=="object"}function isObjectInput(input){return input!==null&&typeof input==="object"}function inferPrimitiveSchema(value){if(value===null)return{type:"null"};if(typeof value==="boolean")return{type:"boolean"};if(typeof value==="number"){return Number.isInteger(value)?{type:"integer"}:{type:"number"}}value;return{type:"null"}}function defineHelper(config){return config}
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});function _export(target,all){for(var name in all)Object.defineProperty(target,name,{enumerable:true,get:Object.getOwnPropertyDescriptor(all,name).get})}_export(exports,{get defineHelper(){return defineHelper},get inferPrimitiveSchema(){return inferPrimitiveSchema},get isArrayInput(){return isArrayInput},get isLiteralInput(){return isLiteralInput},get isObjectInput(){return isObjectInput}});function isLiteralInput(input){return input===null||typeof input!=="string"&&typeof input!=="object"}function isArrayInput(input){return Array.isArray(input)}function isObjectInput(input){return input!==null&&typeof input==="object"&&!Array.isArray(input)}function inferPrimitiveSchema(value){if(value===null)return{type:"null"};if(typeof value==="boolean")return{type:"boolean"};if(typeof value==="number"){return Number.isInteger(value)?{type:"integer"}:{type:"number"}}value;return{type:"null"}}function defineHelper(config){return config}
2
2
  //# sourceMappingURL=types.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/types.ts"],"sourcesContent":["import type { JSONSchema7 } from \"json-schema\";\nimport type { FromSchema, JSONSchema } from \"json-schema-to-ts\";\n\n// ─── Template Input ──────────────────────────────────────────────────────────\n// The engine accepts primitive values in addition to template strings.\n// When a non-string value is passed, it is treated as a literal passthrough:\n// analysis returns the inferred type, and execution returns the value as-is.\n\n/**\n * Object where each property is a `TemplateInput` (recursive).\n *\n * Allows passing an entire structure as a template:\n * ```\n * engine.analyze({\n * userName: \"{{name}}\",\n * userAge: \"{{age}}\",\n * nested: { x: \"{{foo}}\" },\n * }, inputSchema);\n * ```\n */\nexport interface TemplateInputObject {\n\t[key: string]: TemplateInput;\n}\n\n/**\n * Input type accepted by the template engine.\n *\n * - `string` → standard Handlebars template (parsed and executed)\n * - `number` → numeric literal (passthrough)\n * - `boolean` → boolean literal (passthrough)\n * - `null` → null literal (passthrough)\n * - `TemplateInputObject` → object where each property is a `TemplateInput`\n */\nexport type TemplateInput =\n\t| string\n\t| number\n\t| boolean\n\t| null\n\t| TemplateInputObject;\n\n/**\n * Checks whether a value is a non-string primitive literal (number, boolean, null).\n * These values are treated as passthrough by the engine.\n *\n * Note: objects (`TemplateInputObject`) are NOT literals.\n */\nexport function isLiteralInput(\n\tinput: TemplateInput,\n): input is number | boolean | null {\n\treturn (\n\t\tinput === null || (typeof input !== \"string\" && typeof input !== \"object\")\n\t);\n}\n\n/**\n * Checks whether a value is a template object (`TemplateInputObject`).\n * Template objects are processed recursively by the engine:\n * each property is analyzed/executed individually.\n */\nexport function isObjectInput(\n\tinput: TemplateInput,\n): input is TemplateInputObject {\n\treturn input !== null && typeof input === \"object\";\n}\n\n/**\n * Infers the JSON Schema of a non-string primitive value.\n *\n * @param value - The primitive value (number, boolean, null)\n * @returns The corresponding JSON Schema\n *\n * @example\n * ```\n * inferPrimitiveSchema(42) // → { type: \"number\" }\n * inferPrimitiveSchema(true) // → { type: \"boolean\" }\n * inferPrimitiveSchema(null) // → { type: \"null\" }\n * ```\n */\nexport function inferPrimitiveSchema(\n\tvalue: number | boolean | null,\n): JSONSchema7 {\n\tif (value === null) return { type: \"null\" };\n\tif (typeof value === \"boolean\") return { type: \"boolean\" };\n\tif (typeof value === \"number\") {\n\t\treturn Number.isInteger(value) ? { type: \"integer\" } : { type: \"number\" };\n\t}\n\t// Exhaustiveness check — all branches are covered above.\n\t// If the type of `value` changes, TypeScript will raise an error here.\n\tvalue satisfies never;\n\treturn { type: \"null\" };\n}\n\n// ─── Diagnostic Codes ────────────────────────────────────────────────────────\n// Machine-readable codes for each error/warning type, enabling the frontend\n// to react programmatically without parsing the human-readable message.\n\nexport type DiagnosticCode =\n\t/** The referenced property does not exist in the context schema */\n\t| \"UNKNOWN_PROPERTY\"\n\t/** Type mismatch (e.g. #each on a non-array) */\n\t| \"TYPE_MISMATCH\"\n\t/** A block helper is used without a required argument */\n\t| \"MISSING_ARGUMENT\"\n\t/** Unknown block helper (neither built-in nor registered) */\n\t| \"UNKNOWN_HELPER\"\n\t/** The expression cannot be statically analyzed */\n\t| \"UNANALYZABLE\"\n\t/** The {{key:N}} syntax is used but no identifierSchemas were provided */\n\t| \"MISSING_IDENTIFIER_SCHEMAS\"\n\t/** The identifier N does not exist in the provided identifierSchemas */\n\t| \"UNKNOWN_IDENTIFIER\"\n\t/** The property does not exist in the identifier's schema */\n\t| \"IDENTIFIER_PROPERTY_NOT_FOUND\"\n\t/** Syntax error in the template */\n\t| \"PARSE_ERROR\";\n\n// ─── Diagnostic Details ──────────────────────────────────────────────────────\n// Supplementary information to understand the exact cause of the error.\n// Designed to be easily JSON-serializable and consumable by a frontend.\n\nexport interface DiagnosticDetails {\n\t/** Path of the expression that caused the error (e.g. `\"user.name.foo\"`) */\n\tpath?: string;\n\t/** Name of the helper involved (for helper-related errors) */\n\thelperName?: string;\n\t/** What was expected (e.g. `\"array\"`, `\"property to exist\"`) */\n\texpected?: string;\n\t/** What was found (e.g. `\"string\"`, `\"undefined\"`) */\n\tactual?: string;\n\t/** Available properties in the current schema (for suggestions) */\n\tavailableProperties?: string[];\n\t/** Template identifier number (for `{{key:N}}` errors) */\n\tidentifier?: number;\n}\n\n// ─── Static Analysis Result ──────────────────────────────────────────────────\n\n/** Diagnostic produced by the static analyzer */\nexport interface TemplateDiagnostic {\n\t/** \"error\" blocks execution, \"warning\" is informational */\n\tseverity: \"error\" | \"warning\";\n\n\t/** Machine-readable code identifying the error type */\n\tcode: DiagnosticCode;\n\n\t/** Human-readable message describing the problem */\n\tmessage: string;\n\n\t/** Position in the template source (if available from the AST) */\n\tloc?: {\n\t\tstart: { line: number; column: number };\n\t\tend: { line: number; column: number };\n\t};\n\n\t/** Fragment of the template source around the error */\n\tsource?: string;\n\n\t/** Structured information for debugging and frontend display */\n\tdetails?: DiagnosticDetails;\n}\n\n/** Complete result of the static analysis */\nexport interface AnalysisResult {\n\t/** true if no errors (warnings are tolerated) */\n\tvalid: boolean;\n\t/** List of diagnostics (errors + warnings) */\n\tdiagnostics: TemplateDiagnostic[];\n\t/** JSON Schema describing the template's return type */\n\toutputSchema: JSONSchema7;\n}\n\n/** Lightweight validation result (without output type inference) */\nexport interface ValidationResult {\n\t/** true if no errors (warnings are tolerated) */\n\tvalid: boolean;\n\t/** List of diagnostics (errors + warnings) */\n\tdiagnostics: TemplateDiagnostic[];\n}\n\n// ─── Public Engine Options ───────────────────────────────────────────────────\n\nexport interface TemplateEngineOptions {\n\t/**\n\t * Capacity of the parsed AST cache. Each parsed template is cached\n\t * to avoid costly re-parsing on repeated calls.\n\t * @default 256\n\t */\n\tastCacheSize?: number;\n\n\t/**\n\t * Capacity of the compiled Handlebars template cache.\n\t * @default 256\n\t */\n\tcompilationCacheSize?: number;\n\n\t/**\n\t * Custom helpers to register during engine construction.\n\t *\n\t * Each entry describes a helper with its name, implementation,\n\t * expected parameters, and return type.\n\t *\n\t * @example\n\t * ```\n\t * const engine = new Typebars({\n\t * helpers: [\n\t * {\n\t * name: \"uppercase\",\n\t * description: \"Converts a string to uppercase\",\n\t * fn: (value: string) => String(value).toUpperCase(),\n\t * params: [\n\t * { name: \"value\", type: { type: \"string\" }, description: \"The string to convert\" },\n\t * ],\n\t * returnType: { type: \"string\" },\n\t * },\n\t * ],\n\t * });\n\t * ```\n\t */\n\thelpers?: HelperConfig[];\n}\n\n// ─── Execution Options ───────────────────────────────────────────────────────\n// Optional options object for `execute()`, replacing multiple positional\n// parameters for better ergonomics.\n\nexport interface ExecuteOptions {\n\t/** JSON Schema for pre-execution static validation */\n\tschema?: JSONSchema7;\n\t/** Data by identifier `{ [id]: { key: value } }` */\n\tidentifierData?: Record<number, Record<string, unknown>>;\n\t/** Schemas by identifier (for static validation with identifiers) */\n\tidentifierSchemas?: Record<number, JSONSchema7>;\n}\n\n// ─── Combined Analyze-and-Execute Options ────────────────────────────────────\n// Optional options object for `analyzeAndExecute()`, grouping parameters\n// related to template identifiers.\n\nexport interface AnalyzeAndExecuteOptions {\n\t/** Schemas by identifier `{ [id]: JSONSchema7 }` for static analysis */\n\tidentifierSchemas?: Record<number, JSONSchema7>;\n\t/** Data by identifier `{ [id]: { key: value } }` for execution */\n\tidentifierData?: Record<number, Record<string, unknown>>;\n}\n\n// ─── Custom Helpers ──────────────────────────────────────────────────────────\n// Allows registering custom helpers with their type signature for static\n// analysis support.\n\n/** Describes a parameter expected by a helper */\nexport interface HelperParam {\n\t/** Parameter name (for documentation / introspection) */\n\tname: string;\n\n\t/**\n\t * JSON Schema describing the expected type for this parameter.\n\t * Used for documentation and static validation.\n\t */\n\ttype?: JSONSchema7;\n\n\t/** Human-readable description of the parameter */\n\tdescription?: string;\n\n\t/**\n\t * Whether the parameter is optional.\n\t * @default false\n\t */\n\toptional?: boolean;\n}\n\n/**\n * Definition of a helper registerable via `registerHelper()`.\n *\n * Contains the runtime implementation and typing metadata\n * for static analysis.\n */\nexport interface HelperDefinition {\n\t/**\n\t * Runtime implementation of the helper — will be registered with Handlebars.\n\t *\n\t * For an inline helper `{{uppercase name}}`:\n\t * `(value: string) => string`\n\t *\n\t * For a block helper `{{#repeat count}}...{{/repeat}}`:\n\t * `function(this: any, count: number, options: Handlebars.HelperOptions) { ... }`\n\t */\n\t// biome-ignore lint/suspicious/noExplicitAny: Handlebars helper signatures are inherently dynamic\n\tfn: (...args: any[]) => unknown;\n\n\t/**\n\t * Parameters expected by the helper (for documentation and analysis).\n\t *\n\t * @example\n\t * ```\n\t * params: [\n\t * { name: \"value\", type: { type: \"number\" }, description: \"The value to round\" },\n\t * { name: \"precision\", type: { type: \"number\" }, description: \"Decimal places\", optional: true },\n\t * ]\n\t * ```\n\t */\n\tparams?: HelperParam[];\n\n\t/**\n\t * JSON Schema describing the helper's return type for static analysis.\n\t * @default { type: \"string\" }\n\t */\n\treturnType?: JSONSchema7;\n\n\t/** Human-readable description of the helper */\n\tdescription?: string;\n}\n\n/**\n * Full helper configuration for registration via the `Typebars({ helpers: [...] })`\n * constructor options.\n *\n * Extends `HelperDefinition` with a required `name`.\n *\n * @example\n * ```\n * const config: HelperConfig = {\n * name: \"round\",\n * description: \"Rounds a number to a given precision\",\n * fn: (value: number, precision?: number) => { ... },\n * params: [\n * { name: \"value\", type: { type: \"number\" } },\n * { name: \"precision\", type: { type: \"number\" }, optional: true },\n * ],\n * returnType: { type: \"number\" },\n * };\n * ```\n */\nexport interface HelperConfig extends HelperDefinition {\n\t/** Name of the helper as used in templates (e.g. `\"uppercase\"`) */\n\tname: string;\n}\n\n// ─── Automatic Type Inference via json-schema-to-ts ──────────────────────────\n// Allows `defineHelper()` to infer TypeScript types for `fn` arguments\n// from the JSON Schemas declared in `params`.\n\n/**\n * Param definition used for type inference.\n * Accepts `JSONSchema` from `json-schema-to-ts` to allow `FromSchema`\n * to resolve literal types.\n */\ntype TypedHelperParam = {\n\treadonly name: string;\n\treadonly type?: JSONSchema;\n\treadonly description?: string;\n\treadonly optional?: boolean;\n};\n\n/**\n * Infers the TypeScript type of a single parameter from its JSON Schema.\n * - If `optional: true`, the resolved type is unioned with `undefined`.\n * - If `type` is not provided, the type is `unknown`.\n */\ntype InferParamType<P> = P extends {\n\treadonly type: infer S extends JSONSchema;\n\treadonly optional: true;\n}\n\t? FromSchema<S> | undefined\n\t: P extends { readonly type: infer S extends JSONSchema }\n\t\t? FromSchema<S>\n\t\t: unknown;\n\n/**\n * Maps a tuple of `TypedHelperParam` to a tuple of inferred TypeScript types,\n * usable as the `fn` signature.\n *\n * @example\n * ```\n * type Args = InferArgs<readonly [\n * { name: \"a\"; type: { type: \"string\" } },\n * { name: \"b\"; type: { type: \"number\" }; optional: true },\n * ]>;\n * // => [string, number | undefined]\n * ```\n */\ntype InferArgs<P extends readonly TypedHelperParam[]> = {\n\t[K in keyof P]: InferParamType<P[K]>;\n};\n\n/**\n * Helper configuration with generic parameter inference.\n * Used exclusively by `defineHelper()`.\n */\ninterface TypedHelperConfig<P extends readonly TypedHelperParam[]> {\n\tname: string;\n\tdescription?: string;\n\tparams: P;\n\tfn: (...args: InferArgs<P>) => unknown;\n\treturnType?: JSONSchema;\n}\n\n/**\n * Creates a `HelperConfig` with automatic type inference for `fn` arguments\n * based on the JSON Schemas declared in `params`.\n *\n * The generic parameter `const P` preserves schema literal types\n * (equivalent of `as const`), enabling `FromSchema` to resolve the\n * corresponding TypeScript types.\n *\n * @example\n * ```\n * const helper = defineHelper({\n * name: \"concat\",\n * description: \"Concatenates two strings\",\n * params: [\n * { name: \"a\", type: { type: \"string\" }, description: \"First string\" },\n * { name: \"b\", type: { type: \"string\" }, description: \"Second string\" },\n * { name: \"sep\", type: { type: \"string\" }, description: \"Separator\", optional: true },\n * ],\n * fn: (a, b, sep) => {\n * // a: string, b: string, sep: string | undefined\n * const separator = sep ?? \"\";\n * return `${a}${separator}${b}`;\n * },\n * returnType: { type: \"string\" },\n * });\n * ```\n */\nexport function defineHelper<const P extends readonly TypedHelperParam[]>(\n\tconfig: TypedHelperConfig<P>,\n): HelperConfig {\n\treturn config as unknown as HelperConfig;\n}\n"],"names":["defineHelper","inferPrimitiveSchema","isLiteralInput","isObjectInput","input","value","type","Number","isInteger","config"],"mappings":"mPAuagBA,sBAAAA,kBAzVAC,8BAAAA,0BAhCAC,wBAAAA,oBAaAC,uBAAAA,iBAbT,SAASD,eACfE,KAAoB,EAEpB,OACCA,QAAU,MAAS,OAAOA,QAAU,UAAY,OAAOA,QAAU,QAEnE,CAOO,SAASD,cACfC,KAAoB,EAEpB,OAAOA,QAAU,MAAQ,OAAOA,QAAU,QAC3C,CAeO,SAASH,qBACfI,KAA8B,EAE9B,GAAIA,QAAU,KAAM,MAAO,CAAEC,KAAM,MAAO,EAC1C,GAAI,OAAOD,QAAU,UAAW,MAAO,CAAEC,KAAM,SAAU,EACzD,GAAI,OAAOD,QAAU,SAAU,CAC9B,OAAOE,OAAOC,SAAS,CAACH,OAAS,CAAEC,KAAM,SAAU,EAAI,CAAEA,KAAM,QAAS,CACzE,CAGAD,MACA,MAAO,CAAEC,KAAM,MAAO,CACvB,CA6UO,SAASN,aACfS,MAA4B,EAE5B,OAAOA,MACR"}
1
+ {"version":3,"sources":["../../src/types.ts"],"sourcesContent":["import type { JSONSchema7 } from \"json-schema\";\nimport type { FromSchema, JSONSchema } from \"json-schema-to-ts\";\n\n// ─── Template Input ──────────────────────────────────────────────────────────\n// The engine accepts primitive values in addition to template strings.\n// When a non-string value is passed, it is treated as a literal passthrough:\n// analysis returns the inferred type, and execution returns the value as-is.\n\n/**\n * Object where each property is a `TemplateInput` (recursive).\n *\n * Allows passing an entire structure as a template:\n * ```\n * engine.analyze({\n * userName: \"{{name}}\",\n * userAge: \"{{age}}\",\n * nested: { x: \"{{foo}}\" },\n * }, inputSchema);\n * ```\n */\nexport interface TemplateInputObject {\n\t[key: string]: TemplateInput;\n}\n\n/**\n * Array where each element is a `TemplateInput` (recursive).\n *\n * Allows passing an array as a template:\n * ```\n * engine.analyze([\"{{name}}\", \"{{age}}\"], inputSchema);\n * engine.execute([\"{{name}}\", 42], data);\n * ```\n */\nexport type TemplateInputArray = TemplateInput[];\n\n/**\n * Input type accepted by the template engine.\n *\n * - `string` → standard Handlebars template (parsed and executed)\n * - `number` → numeric literal (passthrough)\n * - `boolean` → boolean literal (passthrough)\n * - `null` → null literal (passthrough)\n * - `TemplateInputArray` → array where each element is a `TemplateInput`\n * - `TemplateInputObject` → object where each property is a `TemplateInput`\n */\nexport type TemplateInput =\n\t| string\n\t| number\n\t| boolean\n\t| null\n\t| TemplateInputArray\n\t| TemplateInputObject;\n\n/**\n * Checks whether a value is a non-string primitive literal (number, boolean, null).\n * These values are treated as passthrough by the engine.\n *\n * Note: objects (`TemplateInputObject`) and arrays (`TemplateInputArray`) are NOT literals.\n */\nexport function isLiteralInput(\n\tinput: TemplateInput,\n): input is number | boolean | null {\n\treturn (\n\t\tinput === null || (typeof input !== \"string\" && typeof input !== \"object\")\n\t);\n}\n\n/**\n * Checks whether a value is a template array (`TemplateInputArray`).\n * Template arrays are processed recursively by the engine:\n * each element is analyzed/executed individually and the result is an array.\n */\nexport function isArrayInput(\n\tinput: TemplateInput,\n): input is TemplateInputArray {\n\treturn Array.isArray(input);\n}\n\n/**\n * Checks whether a value is a template object (`TemplateInputObject`).\n * Template objects are processed recursively by the engine:\n * each property is analyzed/executed individually.\n *\n * Note: arrays are excluded — use `isArrayInput()` first.\n */\nexport function isObjectInput(\n\tinput: TemplateInput,\n): input is TemplateInputObject {\n\treturn input !== null && typeof input === \"object\" && !Array.isArray(input);\n}\n\n/**\n * Infers the JSON Schema of a non-string primitive value.\n *\n * @param value - The primitive value (number, boolean, null)\n * @returns The corresponding JSON Schema\n *\n * @example\n * ```\n * inferPrimitiveSchema(42) // → { type: \"number\" }\n * inferPrimitiveSchema(true) // → { type: \"boolean\" }\n * inferPrimitiveSchema(null) // → { type: \"null\" }\n * ```\n */\nexport function inferPrimitiveSchema(\n\tvalue: number | boolean | null,\n): JSONSchema7 {\n\tif (value === null) return { type: \"null\" };\n\tif (typeof value === \"boolean\") return { type: \"boolean\" };\n\tif (typeof value === \"number\") {\n\t\treturn Number.isInteger(value) ? { type: \"integer\" } : { type: \"number\" };\n\t}\n\t// Exhaustiveness check — all branches are covered above.\n\t// If the type of `value` changes, TypeScript will raise an error here.\n\tvalue satisfies never;\n\treturn { type: \"null\" };\n}\n\n// ─── Diagnostic Codes ────────────────────────────────────────────────────────\n// Machine-readable codes for each error/warning type, enabling the frontend\n// to react programmatically without parsing the human-readable message.\n\nexport type DiagnosticCode =\n\t/** The referenced property does not exist in the context schema */\n\t| \"UNKNOWN_PROPERTY\"\n\t/** Type mismatch (e.g. #each on a non-array) */\n\t| \"TYPE_MISMATCH\"\n\t/** A block helper is used without a required argument */\n\t| \"MISSING_ARGUMENT\"\n\t/** Unknown block helper (neither built-in nor registered) */\n\t| \"UNKNOWN_HELPER\"\n\t/** The expression cannot be statically analyzed */\n\t| \"UNANALYZABLE\"\n\t/** The {{key:N}} syntax is used but no identifierSchemas were provided */\n\t| \"MISSING_IDENTIFIER_SCHEMAS\"\n\t/** The identifier N does not exist in the provided identifierSchemas */\n\t| \"UNKNOWN_IDENTIFIER\"\n\t/** The property does not exist in the identifier's schema */\n\t| \"IDENTIFIER_PROPERTY_NOT_FOUND\"\n\t/** Syntax error in the template */\n\t| \"PARSE_ERROR\";\n\n// ─── Diagnostic Details ──────────────────────────────────────────────────────\n// Supplementary information to understand the exact cause of the error.\n// Designed to be easily JSON-serializable and consumable by a frontend.\n\nexport interface DiagnosticDetails {\n\t/** Path of the expression that caused the error (e.g. `\"user.name.foo\"`) */\n\tpath?: string;\n\t/** Name of the helper involved (for helper-related errors) */\n\thelperName?: string;\n\t/** What was expected (e.g. `\"array\"`, `\"property to exist\"`) */\n\texpected?: string;\n\t/** What was found (e.g. `\"string\"`, `\"undefined\"`) */\n\tactual?: string;\n\t/** Available properties in the current schema (for suggestions) */\n\tavailableProperties?: string[];\n\t/** Template identifier number (for `{{key:N}}` errors) */\n\tidentifier?: number;\n}\n\n// ─── Static Analysis Result ──────────────────────────────────────────────────\n\n/** Diagnostic produced by the static analyzer */\nexport interface TemplateDiagnostic {\n\t/** \"error\" blocks execution, \"warning\" is informational */\n\tseverity: \"error\" | \"warning\";\n\n\t/** Machine-readable code identifying the error type */\n\tcode: DiagnosticCode;\n\n\t/** Human-readable message describing the problem */\n\tmessage: string;\n\n\t/** Position in the template source (if available from the AST) */\n\tloc?: {\n\t\tstart: { line: number; column: number };\n\t\tend: { line: number; column: number };\n\t};\n\n\t/** Fragment of the template source around the error */\n\tsource?: string;\n\n\t/** Structured information for debugging and frontend display */\n\tdetails?: DiagnosticDetails;\n}\n\n/** Complete result of the static analysis */\nexport interface AnalysisResult {\n\t/** true if no errors (warnings are tolerated) */\n\tvalid: boolean;\n\t/** List of diagnostics (errors + warnings) */\n\tdiagnostics: TemplateDiagnostic[];\n\t/** JSON Schema describing the template's return type */\n\toutputSchema: JSONSchema7;\n}\n\n/** Lightweight validation result (without output type inference) */\nexport interface ValidationResult {\n\t/** true if no errors (warnings are tolerated) */\n\tvalid: boolean;\n\t/** List of diagnostics (errors + warnings) */\n\tdiagnostics: TemplateDiagnostic[];\n}\n\n// ─── Public Engine Options ───────────────────────────────────────────────────\n\nexport interface TemplateEngineOptions {\n\t/**\n\t * Capacity of the parsed AST cache. Each parsed template is cached\n\t * to avoid costly re-parsing on repeated calls.\n\t * @default 256\n\t */\n\tastCacheSize?: number;\n\n\t/**\n\t * Capacity of the compiled Handlebars template cache.\n\t * @default 256\n\t */\n\tcompilationCacheSize?: number;\n\n\t/**\n\t * Custom helpers to register during engine construction.\n\t *\n\t * Each entry describes a helper with its name, implementation,\n\t * expected parameters, and return type.\n\t *\n\t * @example\n\t * ```\n\t * const engine = new Typebars({\n\t * helpers: [\n\t * {\n\t * name: \"uppercase\",\n\t * description: \"Converts a string to uppercase\",\n\t * fn: (value: string) => String(value).toUpperCase(),\n\t * params: [\n\t * { name: \"value\", type: { type: \"string\" }, description: \"The string to convert\" },\n\t * ],\n\t * returnType: { type: \"string\" },\n\t * },\n\t * ],\n\t * });\n\t * ```\n\t */\n\thelpers?: HelperConfig[];\n}\n\n// ─── Execution Options ───────────────────────────────────────────────────────\n// Optional options object for `execute()`, replacing multiple positional\n// parameters for better ergonomics.\n\nexport interface ExecuteOptions {\n\t/** JSON Schema for pre-execution static validation */\n\tschema?: JSONSchema7;\n\t/** Data by identifier `{ [id]: { key: value } }` */\n\tidentifierData?: Record<number, Record<string, unknown>>;\n\t/** Schemas by identifier (for static validation with identifiers) */\n\tidentifierSchemas?: Record<number, JSONSchema7>;\n}\n\n// ─── Combined Analyze-and-Execute Options ────────────────────────────────────\n// Optional options object for `analyzeAndExecute()`, grouping parameters\n// related to template identifiers.\n\nexport interface AnalyzeAndExecuteOptions {\n\t/** Schemas by identifier `{ [id]: JSONSchema7 }` for static analysis */\n\tidentifierSchemas?: Record<number, JSONSchema7>;\n\t/** Data by identifier `{ [id]: { key: value } }` for execution */\n\tidentifierData?: Record<number, Record<string, unknown>>;\n}\n\n// ─── Custom Helpers ──────────────────────────────────────────────────────────\n// Allows registering custom helpers with their type signature for static\n// analysis support.\n\n/** Describes a parameter expected by a helper */\nexport interface HelperParam {\n\t/** Parameter name (for documentation / introspection) */\n\tname: string;\n\n\t/**\n\t * JSON Schema describing the expected type for this parameter.\n\t * Used for documentation and static validation.\n\t */\n\ttype?: JSONSchema7;\n\n\t/** Human-readable description of the parameter */\n\tdescription?: string;\n\n\t/**\n\t * Whether the parameter is optional.\n\t * @default false\n\t */\n\toptional?: boolean;\n}\n\n/**\n * Definition of a helper registerable via `registerHelper()`.\n *\n * Contains the runtime implementation and typing metadata\n * for static analysis.\n */\nexport interface HelperDefinition {\n\t/**\n\t * Runtime implementation of the helper — will be registered with Handlebars.\n\t *\n\t * For an inline helper `{{uppercase name}}`:\n\t * `(value: string) => string`\n\t *\n\t * For a block helper `{{#repeat count}}...{{/repeat}}`:\n\t * `function(this: any, count: number, options: Handlebars.HelperOptions) { ... }`\n\t */\n\t// biome-ignore lint/suspicious/noExplicitAny: Handlebars helper signatures are inherently dynamic\n\tfn: (...args: any[]) => unknown;\n\n\t/**\n\t * Parameters expected by the helper (for documentation and analysis).\n\t *\n\t * @example\n\t * ```\n\t * params: [\n\t * { name: \"value\", type: { type: \"number\" }, description: \"The value to round\" },\n\t * { name: \"precision\", type: { type: \"number\" }, description: \"Decimal places\", optional: true },\n\t * ]\n\t * ```\n\t */\n\tparams?: HelperParam[];\n\n\t/**\n\t * JSON Schema describing the helper's return type for static analysis.\n\t * @default { type: \"string\" }\n\t */\n\treturnType?: JSONSchema7;\n\n\t/** Human-readable description of the helper */\n\tdescription?: string;\n}\n\n/**\n * Full helper configuration for registration via the `Typebars({ helpers: [...] })`\n * constructor options.\n *\n * Extends `HelperDefinition` with a required `name`.\n *\n * @example\n * ```\n * const config: HelperConfig = {\n * name: \"round\",\n * description: \"Rounds a number to a given precision\",\n * fn: (value: number, precision?: number) => { ... },\n * params: [\n * { name: \"value\", type: { type: \"number\" } },\n * { name: \"precision\", type: { type: \"number\" }, optional: true },\n * ],\n * returnType: { type: \"number\" },\n * };\n * ```\n */\nexport interface HelperConfig extends HelperDefinition {\n\t/** Name of the helper as used in templates (e.g. `\"uppercase\"`) */\n\tname: string;\n}\n\n// ─── Automatic Type Inference via json-schema-to-ts ──────────────────────────\n// Allows `defineHelper()` to infer TypeScript types for `fn` arguments\n// from the JSON Schemas declared in `params`.\n\n/**\n * Param definition used for type inference.\n * Accepts `JSONSchema` from `json-schema-to-ts` to allow `FromSchema`\n * to resolve literal types.\n */\ntype TypedHelperParam = {\n\treadonly name: string;\n\treadonly type?: JSONSchema;\n\treadonly description?: string;\n\treadonly optional?: boolean;\n};\n\n/**\n * Infers the TypeScript type of a single parameter from its JSON Schema.\n * - If `optional: true`, the resolved type is unioned with `undefined`.\n * - If `type` is not provided, the type is `unknown`.\n */\ntype InferParamType<P> = P extends {\n\treadonly type: infer S extends JSONSchema;\n\treadonly optional: true;\n}\n\t? FromSchema<S> | undefined\n\t: P extends { readonly type: infer S extends JSONSchema }\n\t\t? FromSchema<S>\n\t\t: unknown;\n\n/**\n * Maps a tuple of `TypedHelperParam` to a tuple of inferred TypeScript types,\n * usable as the `fn` signature.\n *\n * @example\n * ```\n * type Args = InferArgs<readonly [\n * { name: \"a\"; type: { type: \"string\" } },\n * { name: \"b\"; type: { type: \"number\" }; optional: true },\n * ]>;\n * // => [string, number | undefined]\n * ```\n */\ntype InferArgs<P extends readonly TypedHelperParam[]> = {\n\t[K in keyof P]: InferParamType<P[K]>;\n};\n\n/**\n * Helper configuration with generic parameter inference.\n * Used exclusively by `defineHelper()`.\n */\ninterface TypedHelperConfig<P extends readonly TypedHelperParam[]> {\n\tname: string;\n\tdescription?: string;\n\tparams: P;\n\tfn: (...args: InferArgs<P>) => unknown;\n\treturnType?: JSONSchema;\n}\n\n/**\n * Creates a `HelperConfig` with automatic type inference for `fn` arguments\n * based on the JSON Schemas declared in `params`.\n *\n * The generic parameter `const P` preserves schema literal types\n * (equivalent of `as const`), enabling `FromSchema` to resolve the\n * corresponding TypeScript types.\n *\n * @example\n * ```\n * const helper = defineHelper({\n * name: \"concat\",\n * description: \"Concatenates two strings\",\n * params: [\n * { name: \"a\", type: { type: \"string\" }, description: \"First string\" },\n * { name: \"b\", type: { type: \"string\" }, description: \"Second string\" },\n * { name: \"sep\", type: { type: \"string\" }, description: \"Separator\", optional: true },\n * ],\n * fn: (a, b, sep) => {\n * // a: string, b: string, sep: string | undefined\n * const separator = sep ?? \"\";\n * return `${a}${separator}${b}`;\n * },\n * returnType: { type: \"string\" },\n * });\n * ```\n */\nexport function defineHelper<const P extends readonly TypedHelperParam[]>(\n\tconfig: TypedHelperConfig<P>,\n): HelperConfig {\n\treturn config as unknown as HelperConfig;\n}\n"],"names":["defineHelper","inferPrimitiveSchema","isArrayInput","isLiteralInput","isObjectInput","input","Array","isArray","value","type","Number","isInteger","config"],"mappings":"mPAicgBA,sBAAAA,kBAzVAC,8BAAAA,0BAhCAC,sBAAAA,kBAbAC,wBAAAA,oBA0BAC,uBAAAA,iBA1BT,SAASD,eACfE,KAAoB,EAEpB,OACCA,QAAU,MAAS,OAAOA,QAAU,UAAY,OAAOA,QAAU,QAEnE,CAOO,SAASH,aACfG,KAAoB,EAEpB,OAAOC,MAAMC,OAAO,CAACF,MACtB,CASO,SAASD,cACfC,KAAoB,EAEpB,OAAOA,QAAU,MAAQ,OAAOA,QAAU,UAAY,CAACC,MAAMC,OAAO,CAACF,MACtE,CAeO,SAASJ,qBACfO,KAA8B,EAE9B,GAAIA,QAAU,KAAM,MAAO,CAAEC,KAAM,MAAO,EAC1C,GAAI,OAAOD,QAAU,UAAW,MAAO,CAAEC,KAAM,SAAU,EACzD,GAAI,OAAOD,QAAU,SAAU,CAC9B,OAAOE,OAAOC,SAAS,CAACH,OAAS,CAAEC,KAAM,SAAU,EAAI,CAAEA,KAAM,QAAS,CACzE,CAGAD,MACA,MAAO,CAAEC,KAAM,MAAO,CACvB,CA6UO,SAAST,aACfY,MAA4B,EAE5B,OAAOA,MACR"}
@@ -111,3 +111,36 @@ export declare function aggregateObjectAnalysisAndExecution(keys: string[], proc
111
111
  analysis: AnalysisResult;
112
112
  value: unknown;
113
113
  };
114
+ /**
115
+ * Aggregates analysis results from a set of array elements into a single
116
+ * `AnalysisResult` with an array-typed `outputSchema`.
117
+ *
118
+ * @param length - The number of elements in the array
119
+ * @param analyzeEntry - Callback that analyzes an element by its index
120
+ * @returns An aggregated `AnalysisResult`
121
+ *
122
+ * @example
123
+ * ```
124
+ * aggregateArrayAnalysis(
125
+ * template.length,
126
+ * (index) => analyze(template[index], inputSchema),
127
+ * );
128
+ * ```
129
+ */
130
+ export declare function aggregateArrayAnalysis(length: number, analyzeEntry: (index: number) => AnalysisResult): AnalysisResult;
131
+ /**
132
+ * Aggregates both analysis **and** execution results from a set of array
133
+ * elements. Returns the aggregated `AnalysisResult` and the array of
134
+ * executed values (or `undefined` if at least one element is invalid).
135
+ *
136
+ * @param length - The number of elements
137
+ * @param processEntry - Callback that analyzes and executes an element by index
138
+ * @returns Aggregated `{ analysis, value }`
139
+ */
140
+ export declare function aggregateArrayAnalysisAndExecution(length: number, processEntry: (index: number) => {
141
+ analysis: AnalysisResult;
142
+ value: unknown;
143
+ }): {
144
+ analysis: AnalysisResult;
145
+ value: unknown;
146
+ };
package/dist/cjs/utils.js CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});function _export(target,all){for(var name in all)Object.defineProperty(target,name,{enumerable:true,get:Object.getOwnPropertyDescriptor(all,name).get})}_export(exports,{get LRUCache(){return LRUCache},get aggregateObjectAnalysis(){return aggregateObjectAnalysis},get aggregateObjectAnalysisAndExecution(){return aggregateObjectAnalysisAndExecution},get deepEqual(){return deepEqual},get extractSourceSnippet(){return extractSourceSnippet},get getSchemaPropertyNames(){return getSchemaPropertyNames}});function _define_property(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true})}else{obj[key]=value}return obj}function deepEqual(a,b){if(a===b)return true;if(a===null||b===null)return false;if(typeof a!==typeof b)return false;if(Array.isArray(a)){if(!Array.isArray(b))return false;if(a.length!==b.length)return false;for(let i=0;i<a.length;i++){if(!deepEqual(a[i],b[i]))return false}return true}if(typeof a==="object"){const objA=a;const objB=b;const keysA=Object.keys(objA);const keysB=Object.keys(objB);if(keysA.length!==keysB.length)return false;for(const key of keysA){if(!(key in objB)||!deepEqual(objA[key],objB[key]))return false}return true}return false}class LRUCache{get(key){if(!this.cache.has(key))return undefined;const value=this.cache.get(key);this.cache.delete(key);this.cache.set(key,value);return value}set(key,value){if(this.cache.has(key)){this.cache.delete(key)}else if(this.cache.size>=this.capacity){const oldestKey=this.cache.keys().next().value;if(oldestKey!==undefined){this.cache.delete(oldestKey)}}this.cache.set(key,value)}has(key){return this.cache.has(key)}delete(key){return this.cache.delete(key)}clear(){this.cache.clear()}get size(){return this.cache.size}constructor(capacity){_define_property(this,"capacity",void 0);_define_property(this,"cache",void 0);this.capacity=capacity;this.cache=new Map;if(capacity<1){throw new Error("LRUCache capacity must be at least 1")}}}function extractSourceSnippet(template,loc){const lines=template.split("\n");const startLine=loc.start.line-1;const endLine=loc.end.line-1;if(startLine<0||startLine>=lines.length)return"";if(startLine===endLine){const line=lines[startLine]??"";return line.trim()}const clampedEnd=Math.min(endLine,lines.length-1);return lines.slice(startLine,clampedEnd+1).map(l=>l.trimEnd()).join("\n").trim()}function getSchemaPropertyNames(schema){const names=new Set;if(schema.properties){for(const key of Object.keys(schema.properties)){names.add(key)}}for(const combinator of["allOf","anyOf","oneOf"]){const branches=schema[combinator];if(branches){for(const branch of branches){if(typeof branch==="boolean")continue;if(branch.properties){for(const key of Object.keys(branch.properties)){names.add(key)}}}}}return Array.from(names).sort()}function aggregateObjectAnalysis(keys,analyzeEntry){const allDiagnostics=[];const properties={};let allValid=true;for(const key of keys){const child=analyzeEntry(key);if(!child.valid)allValid=false;allDiagnostics.push(...child.diagnostics);properties[key]=child.outputSchema}return{valid:allValid,diagnostics:allDiagnostics,outputSchema:{type:"object",properties,required:keys}}}function aggregateObjectAnalysisAndExecution(keys,processEntry){const allDiagnostics=[];const properties={};const resultValues={};let allValid=true;for(const key of keys){const child=processEntry(key);if(!child.analysis.valid)allValid=false;allDiagnostics.push(...child.analysis.diagnostics);properties[key]=child.analysis.outputSchema;resultValues[key]=child.value}const analysis={valid:allValid,diagnostics:allDiagnostics,outputSchema:{type:"object",properties,required:keys}};return{analysis,value:allValid?resultValues:undefined}}
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});function _export(target,all){for(var name in all)Object.defineProperty(target,name,{enumerable:true,get:Object.getOwnPropertyDescriptor(all,name).get})}_export(exports,{get LRUCache(){return LRUCache},get aggregateArrayAnalysis(){return aggregateArrayAnalysis},get aggregateArrayAnalysisAndExecution(){return aggregateArrayAnalysisAndExecution},get aggregateObjectAnalysis(){return aggregateObjectAnalysis},get aggregateObjectAnalysisAndExecution(){return aggregateObjectAnalysisAndExecution},get deepEqual(){return deepEqual},get extractSourceSnippet(){return extractSourceSnippet},get getSchemaPropertyNames(){return getSchemaPropertyNames}});function _define_property(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true})}else{obj[key]=value}return obj}function deepEqual(a,b){if(a===b)return true;if(a===null||b===null)return false;if(typeof a!==typeof b)return false;if(Array.isArray(a)){if(!Array.isArray(b))return false;if(a.length!==b.length)return false;for(let i=0;i<a.length;i++){if(!deepEqual(a[i],b[i]))return false}return true}if(typeof a==="object"){const objA=a;const objB=b;const keysA=Object.keys(objA);const keysB=Object.keys(objB);if(keysA.length!==keysB.length)return false;for(const key of keysA){if(!(key in objB)||!deepEqual(objA[key],objB[key]))return false}return true}return false}class LRUCache{get(key){if(!this.cache.has(key))return undefined;const value=this.cache.get(key);this.cache.delete(key);this.cache.set(key,value);return value}set(key,value){if(this.cache.has(key)){this.cache.delete(key)}else if(this.cache.size>=this.capacity){const oldestKey=this.cache.keys().next().value;if(oldestKey!==undefined){this.cache.delete(oldestKey)}}this.cache.set(key,value)}has(key){return this.cache.has(key)}delete(key){return this.cache.delete(key)}clear(){this.cache.clear()}get size(){return this.cache.size}constructor(capacity){_define_property(this,"capacity",void 0);_define_property(this,"cache",void 0);this.capacity=capacity;this.cache=new Map;if(capacity<1){throw new Error("LRUCache capacity must be at least 1")}}}function extractSourceSnippet(template,loc){const lines=template.split("\n");const startLine=loc.start.line-1;const endLine=loc.end.line-1;if(startLine<0||startLine>=lines.length)return"";if(startLine===endLine){const line=lines[startLine]??"";return line.trim()}const clampedEnd=Math.min(endLine,lines.length-1);return lines.slice(startLine,clampedEnd+1).map(l=>l.trimEnd()).join("\n").trim()}function getSchemaPropertyNames(schema){const names=new Set;if(schema.properties){for(const key of Object.keys(schema.properties)){names.add(key)}}for(const combinator of["allOf","anyOf","oneOf"]){const branches=schema[combinator];if(branches){for(const branch of branches){if(typeof branch==="boolean")continue;if(branch.properties){for(const key of Object.keys(branch.properties)){names.add(key)}}}}}return Array.from(names).sort()}function aggregateObjectAnalysis(keys,analyzeEntry){const allDiagnostics=[];const properties={};let allValid=true;for(const key of keys){const child=analyzeEntry(key);if(!child.valid)allValid=false;allDiagnostics.push(...child.diagnostics);properties[key]=child.outputSchema}return{valid:allValid,diagnostics:allDiagnostics,outputSchema:{type:"object",properties,required:keys}}}function aggregateObjectAnalysisAndExecution(keys,processEntry){const allDiagnostics=[];const properties={};const resultValues={};let allValid=true;for(const key of keys){const child=processEntry(key);if(!child.analysis.valid)allValid=false;allDiagnostics.push(...child.analysis.diagnostics);properties[key]=child.analysis.outputSchema;resultValues[key]=child.value}const analysis={valid:allValid,diagnostics:allDiagnostics,outputSchema:{type:"object",properties,required:keys}};return{analysis,value:allValid?resultValues:undefined}}function computeArrayItemsSchema(elementSchemas){if(elementSchemas.length===0)return{};const unique=[];for(const schema of elementSchemas){if(!unique.some(u=>deepEqual(u,schema))){unique.push(schema)}}if(unique.length===1){return unique[0]}return{oneOf:unique}}function aggregateArrayAnalysis(length,analyzeEntry){const allDiagnostics=[];const elementSchemas=[];let allValid=true;for(let i=0;i<length;i++){const child=analyzeEntry(i);if(!child.valid)allValid=false;allDiagnostics.push(...child.diagnostics);elementSchemas.push(child.outputSchema)}return{valid:allValid,diagnostics:allDiagnostics,outputSchema:{type:"array",items:computeArrayItemsSchema(elementSchemas)}}}function aggregateArrayAnalysisAndExecution(length,processEntry){const allDiagnostics=[];const elementSchemas=[];const resultValues=[];let allValid=true;for(let i=0;i<length;i++){const child=processEntry(i);if(!child.analysis.valid)allValid=false;allDiagnostics.push(...child.analysis.diagnostics);elementSchemas.push(child.analysis.outputSchema);resultValues.push(child.value)}const analysis={valid:allValid,diagnostics:allDiagnostics,outputSchema:{type:"array",items:computeArrayItemsSchema(elementSchemas)}};return{analysis,value:allValid?resultValues:undefined}}
2
2
  //# sourceMappingURL=utils.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils.ts"],"sourcesContent":["import type { JSONSchema7 } from \"json-schema\";\nimport type { AnalysisResult, TemplateDiagnostic } from \"./types.ts\";\n\n// ─── Utilities ───────────────────────────────────────────────────────────────\n// Shared utility functions and classes used across the different modules\n// of the template engine.\n\n// ─── Deep Equality ───────────────────────────────────────────────────────────\n// Deep structural comparison for JSON-compatible values.\n// More robust than `JSON.stringify` because it is independent of key order\n// and does not allocate intermediate strings.\n\n/**\n * Recursively compares two JSON-compatible values.\n *\n * @param a - First value\n * @param b - Second value\n * @returns `true` if the two values are structurally identical\n *\n * @example\n * ```\n * deepEqual({ a: 1, b: 2 }, { b: 2, a: 1 }) // → true\n * deepEqual([1, 2], [1, 2]) // → true\n * deepEqual({ a: 1 }, { a: 2 }) // → false\n * ```\n */\nexport function deepEqual(a: unknown, b: unknown): boolean {\n\t// Strict identity (covers primitives, same ref; NaN !== NaN is intentional)\n\tif (a === b) return true;\n\n\t// null is typeof \"object\" in JS — handle it separately\n\tif (a === null || b === null) return false;\n\tif (typeof a !== typeof b) return false;\n\n\t// ── Arrays ───────────────────────────────────────────────────────────────\n\tif (Array.isArray(a)) {\n\t\tif (!Array.isArray(b)) return false;\n\t\tif (a.length !== b.length) return false;\n\t\tfor (let i = 0; i < a.length; i++) {\n\t\t\tif (!deepEqual(a[i], b[i])) return false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t// ── Objects ──────────────────────────────────────────────────────────────\n\tif (typeof a === \"object\") {\n\t\tconst objA = a as Record<string, unknown>;\n\t\tconst objB = b as Record<string, unknown>;\n\t\tconst keysA = Object.keys(objA);\n\t\tconst keysB = Object.keys(objB);\n\n\t\tif (keysA.length !== keysB.length) return false;\n\n\t\tfor (const key of keysA) {\n\t\t\tif (!(key in objB) || !deepEqual(objA[key], objB[key])) return false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t// Different primitives (already covered by a === b at the top)\n\treturn false;\n}\n\n// ─── LRU Cache ───────────────────────────────────────────────────────────────\n// Fixed-capacity cache with Least Recently Used (LRU) eviction.\n// Leverages `Map` insertion order to track access: the oldest entry\n// is always in the first position.\n\n/**\n * Simple fixed-capacity LRU cache.\n *\n * @example\n * ```\n * const cache = new LRUCache<string, number>(2);\n * cache.set(\"a\", 1);\n * cache.set(\"b\", 2);\n * cache.get(\"a\"); // → 1 (marks \"a\" as recently used)\n * cache.set(\"c\", 3); // evicts \"b\" (least recently used)\n * cache.get(\"b\"); // → undefined\n * ```\n */\nexport class LRUCache<K, V> {\n\tprivate readonly cache = new Map<K, V>();\n\n\tconstructor(private readonly capacity: number) {\n\t\tif (capacity < 1) {\n\t\t\tthrow new Error(\"LRUCache capacity must be at least 1\");\n\t\t}\n\t}\n\n\t/**\n\t * Retrieves a value from the cache. Returns `undefined` if absent.\n\t * Marks the entry as recently used.\n\t */\n\tget(key: K): V | undefined {\n\t\tif (!this.cache.has(key)) return undefined;\n\n\t\t// Move to the end of the Map (= most recently used)\n\t\tconst value = this.cache.get(key) as V;\n\t\tthis.cache.delete(key);\n\t\tthis.cache.set(key, value);\n\t\treturn value;\n\t}\n\n\t/**\n\t * Inserts or updates a value in the cache.\n\t * If the cache is full, evicts the least recently used entry.\n\t */\n\tset(key: K, value: V): void {\n\t\tif (this.cache.has(key)) {\n\t\t\tthis.cache.delete(key);\n\t\t} else if (this.cache.size >= this.capacity) {\n\t\t\t// Evict the first entry (the oldest one)\n\t\t\tconst oldestKey = this.cache.keys().next().value;\n\t\t\tif (oldestKey !== undefined) {\n\t\t\t\tthis.cache.delete(oldestKey);\n\t\t\t}\n\t\t}\n\t\tthis.cache.set(key, value);\n\t}\n\n\t/**\n\t * Checks whether a key exists in the cache (without affecting LRU order).\n\t */\n\thas(key: K): boolean {\n\t\treturn this.cache.has(key);\n\t}\n\n\t/**\n\t * Removes an entry from the cache.\n\t * @returns `true` if the entry existed and was removed\n\t */\n\tdelete(key: K): boolean {\n\t\treturn this.cache.delete(key);\n\t}\n\n\t/** Clears the entire cache. */\n\tclear(): void {\n\t\tthis.cache.clear();\n\t}\n\n\t/** Number of entries currently in the cache. */\n\tget size(): number {\n\t\treturn this.cache.size;\n\t}\n}\n\n// ─── Source Snippet Extraction ────────────────────────────────────────────────\n// Used to enrich diagnostics with the template fragment that caused the error.\n\n/**\n * Extracts a template fragment around a given position.\n *\n * @param template - The full template source\n * @param loc - The position (line/column, 1-based) of the error\n * @returns The corresponding code fragment (trimmed)\n */\nexport function extractSourceSnippet(\n\ttemplate: string,\n\tloc: {\n\t\tstart: { line: number; column: number };\n\t\tend: { line: number; column: number };\n\t},\n): string {\n\tconst lines = template.split(\"\\n\");\n\tconst startLine = loc.start.line - 1; // 0-based\n\tconst endLine = loc.end.line - 1;\n\n\tif (startLine < 0 || startLine >= lines.length) return \"\";\n\n\tif (startLine === endLine) {\n\t\t// Same line — extract the portion between start.column and end.column\n\t\tconst line = lines[startLine] ?? \"\";\n\t\treturn line.trim();\n\t}\n\n\t// Multi-line — return the affected lines\n\tconst clampedEnd = Math.min(endLine, lines.length - 1);\n\treturn lines\n\t\t.slice(startLine, clampedEnd + 1)\n\t\t.map((l) => l.trimEnd())\n\t\t.join(\"\\n\")\n\t\t.trim();\n}\n\n// ─── Schema Properties ──────────────────────────────────────────────────────\n// Utility for listing available properties in a schema, used to enrich\n// error messages with suggestions.\n\n/**\n * Lists the declared property names in a JSON Schema.\n * Returns an empty array if the schema has no `properties`.\n */\nexport function getSchemaPropertyNames(schema: JSONSchema7): string[] {\n\tconst names = new Set<string>();\n\n\t// Direct properties\n\tif (schema.properties) {\n\t\tfor (const key of Object.keys(schema.properties)) {\n\t\t\tnames.add(key);\n\t\t}\n\t}\n\n\t// Properties within combinators\n\tfor (const combinator of [\"allOf\", \"anyOf\", \"oneOf\"] as const) {\n\t\tconst branches = schema[combinator];\n\t\tif (branches) {\n\t\t\tfor (const branch of branches) {\n\t\t\t\tif (typeof branch === \"boolean\") continue;\n\t\t\t\tif (branch.properties) {\n\t\t\t\t\tfor (const key of Object.keys(branch.properties)) {\n\t\t\t\t\t\tnames.add(key);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn Array.from(names).sort();\n}\n\n// ─── Object Analysis Aggregation ─────────────────────────────────────────────\n// Factorizes the common recursion pattern over template objects:\n// iterate the keys, analyze each entry via a callback, accumulate\n// diagnostics, and build the output object schema.\n//\n// Used by:\n// - `analyzer.ts` (analyzeObjectTemplate)\n// - `Typebars.analyzeObject()` (typebars.ts)\n// - `CompiledTemplate.analyze()` in object mode (compiled-template.ts)\n\n/**\n * Aggregates analysis results from a set of named entries into a single\n * `AnalysisResult` with an object-typed `outputSchema`.\n *\n * @param keys - The keys of the object to analyze\n * @param analyzeEntry - Callback that analyzes an entry by its key\n * @returns An aggregated `AnalysisResult`\n *\n * @example\n * ```\n * aggregateObjectAnalysis(\n * Object.keys(template),\n * (key) => analyze(template[key], inputSchema),\n * );\n * ```\n */\nexport function aggregateObjectAnalysis(\n\tkeys: string[],\n\tanalyzeEntry: (key: string) => AnalysisResult,\n): AnalysisResult {\n\tconst allDiagnostics: TemplateDiagnostic[] = [];\n\tconst properties: Record<string, JSONSchema7> = {};\n\tlet allValid = true;\n\n\tfor (const key of keys) {\n\t\tconst child = analyzeEntry(key);\n\t\tif (!child.valid) allValid = false;\n\t\tallDiagnostics.push(...child.diagnostics);\n\t\tproperties[key] = child.outputSchema;\n\t}\n\n\treturn {\n\t\tvalid: allValid,\n\t\tdiagnostics: allDiagnostics,\n\t\toutputSchema: {\n\t\t\ttype: \"object\",\n\t\t\tproperties,\n\t\t\trequired: keys,\n\t\t},\n\t};\n}\n\n/**\n * Aggregates both analysis **and** execution results from a set of named\n * entries. Returns the aggregated `AnalysisResult` and the object of\n * executed values (or `undefined` if at least one entry is invalid).\n *\n * @param keys - The keys of the object\n * @param processEntry - Callback that analyzes and executes an entry by its key\n * @returns Aggregated `{ analysis, value }`\n */\nexport function aggregateObjectAnalysisAndExecution(\n\tkeys: string[],\n\tprocessEntry: (key: string) => { analysis: AnalysisResult; value: unknown },\n): { analysis: AnalysisResult; value: unknown } {\n\tconst allDiagnostics: TemplateDiagnostic[] = [];\n\tconst properties: Record<string, JSONSchema7> = {};\n\tconst resultValues: Record<string, unknown> = {};\n\tlet allValid = true;\n\n\tfor (const key of keys) {\n\t\tconst child = processEntry(key);\n\t\tif (!child.analysis.valid) allValid = false;\n\t\tallDiagnostics.push(...child.analysis.diagnostics);\n\t\tproperties[key] = child.analysis.outputSchema;\n\t\tresultValues[key] = child.value;\n\t}\n\n\tconst analysis: AnalysisResult = {\n\t\tvalid: allValid,\n\t\tdiagnostics: allDiagnostics,\n\t\toutputSchema: {\n\t\t\ttype: \"object\",\n\t\t\tproperties,\n\t\t\trequired: keys,\n\t\t},\n\t};\n\n\treturn {\n\t\tanalysis,\n\t\tvalue: allValid ? resultValues : undefined,\n\t};\n}\n"],"names":["LRUCache","aggregateObjectAnalysis","aggregateObjectAnalysisAndExecution","deepEqual","extractSourceSnippet","getSchemaPropertyNames","a","b","Array","isArray","length","i","objA","objB","keysA","Object","keys","keysB","key","get","cache","has","undefined","value","delete","set","size","capacity","oldestKey","next","clear","Map","Error","template","loc","lines","split","startLine","start","line","endLine","end","trim","clampedEnd","Math","min","slice","map","l","trimEnd","join","schema","names","Set","properties","add","combinator","branches","branch","from","sort","analyzeEntry","allDiagnostics","allValid","child","valid","push","diagnostics","outputSchema","type","required","processEntry","resultValues","analysis"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/utils.ts"],"sourcesContent":["import type { JSONSchema7 } from \"json-schema\";\nimport type { AnalysisResult, TemplateDiagnostic } from \"./types.ts\";\n\n// ─── Utilities ───────────────────────────────────────────────────────────────\n// Shared utility functions and classes used across the different modules\n// of the template engine.\n\n// ─── Deep Equality ───────────────────────────────────────────────────────────\n// Deep structural comparison for JSON-compatible values.\n// More robust than `JSON.stringify` because it is independent of key order\n// and does not allocate intermediate strings.\n\n/**\n * Recursively compares two JSON-compatible values.\n *\n * @param a - First value\n * @param b - Second value\n * @returns `true` if the two values are structurally identical\n *\n * @example\n * ```\n * deepEqual({ a: 1, b: 2 }, { b: 2, a: 1 }) // → true\n * deepEqual([1, 2], [1, 2]) // → true\n * deepEqual({ a: 1 }, { a: 2 }) // → false\n * ```\n */\nexport function deepEqual(a: unknown, b: unknown): boolean {\n\t// Strict identity (covers primitives, same ref; NaN !== NaN is intentional)\n\tif (a === b) return true;\n\n\t// null is typeof \"object\" in JS — handle it separately\n\tif (a === null || b === null) return false;\n\tif (typeof a !== typeof b) return false;\n\n\t// ── Arrays ───────────────────────────────────────────────────────────────\n\tif (Array.isArray(a)) {\n\t\tif (!Array.isArray(b)) return false;\n\t\tif (a.length !== b.length) return false;\n\t\tfor (let i = 0; i < a.length; i++) {\n\t\t\tif (!deepEqual(a[i], b[i])) return false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t// ── Objects ──────────────────────────────────────────────────────────────\n\tif (typeof a === \"object\") {\n\t\tconst objA = a as Record<string, unknown>;\n\t\tconst objB = b as Record<string, unknown>;\n\t\tconst keysA = Object.keys(objA);\n\t\tconst keysB = Object.keys(objB);\n\n\t\tif (keysA.length !== keysB.length) return false;\n\n\t\tfor (const key of keysA) {\n\t\t\tif (!(key in objB) || !deepEqual(objA[key], objB[key])) return false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t// Different primitives (already covered by a === b at the top)\n\treturn false;\n}\n\n// ─── LRU Cache ───────────────────────────────────────────────────────────────\n// Fixed-capacity cache with Least Recently Used (LRU) eviction.\n// Leverages `Map` insertion order to track access: the oldest entry\n// is always in the first position.\n\n/**\n * Simple fixed-capacity LRU cache.\n *\n * @example\n * ```\n * const cache = new LRUCache<string, number>(2);\n * cache.set(\"a\", 1);\n * cache.set(\"b\", 2);\n * cache.get(\"a\"); // → 1 (marks \"a\" as recently used)\n * cache.set(\"c\", 3); // evicts \"b\" (least recently used)\n * cache.get(\"b\"); // → undefined\n * ```\n */\nexport class LRUCache<K, V> {\n\tprivate readonly cache = new Map<K, V>();\n\n\tconstructor(private readonly capacity: number) {\n\t\tif (capacity < 1) {\n\t\t\tthrow new Error(\"LRUCache capacity must be at least 1\");\n\t\t}\n\t}\n\n\t/**\n\t * Retrieves a value from the cache. Returns `undefined` if absent.\n\t * Marks the entry as recently used.\n\t */\n\tget(key: K): V | undefined {\n\t\tif (!this.cache.has(key)) return undefined;\n\n\t\t// Move to the end of the Map (= most recently used)\n\t\tconst value = this.cache.get(key) as V;\n\t\tthis.cache.delete(key);\n\t\tthis.cache.set(key, value);\n\t\treturn value;\n\t}\n\n\t/**\n\t * Inserts or updates a value in the cache.\n\t * If the cache is full, evicts the least recently used entry.\n\t */\n\tset(key: K, value: V): void {\n\t\tif (this.cache.has(key)) {\n\t\t\tthis.cache.delete(key);\n\t\t} else if (this.cache.size >= this.capacity) {\n\t\t\t// Evict the first entry (the oldest one)\n\t\t\tconst oldestKey = this.cache.keys().next().value;\n\t\t\tif (oldestKey !== undefined) {\n\t\t\t\tthis.cache.delete(oldestKey);\n\t\t\t}\n\t\t}\n\t\tthis.cache.set(key, value);\n\t}\n\n\t/**\n\t * Checks whether a key exists in the cache (without affecting LRU order).\n\t */\n\thas(key: K): boolean {\n\t\treturn this.cache.has(key);\n\t}\n\n\t/**\n\t * Removes an entry from the cache.\n\t * @returns `true` if the entry existed and was removed\n\t */\n\tdelete(key: K): boolean {\n\t\treturn this.cache.delete(key);\n\t}\n\n\t/** Clears the entire cache. */\n\tclear(): void {\n\t\tthis.cache.clear();\n\t}\n\n\t/** Number of entries currently in the cache. */\n\tget size(): number {\n\t\treturn this.cache.size;\n\t}\n}\n\n// ─── Source Snippet Extraction ────────────────────────────────────────────────\n// Used to enrich diagnostics with the template fragment that caused the error.\n\n/**\n * Extracts a template fragment around a given position.\n *\n * @param template - The full template source\n * @param loc - The position (line/column, 1-based) of the error\n * @returns The corresponding code fragment (trimmed)\n */\nexport function extractSourceSnippet(\n\ttemplate: string,\n\tloc: {\n\t\tstart: { line: number; column: number };\n\t\tend: { line: number; column: number };\n\t},\n): string {\n\tconst lines = template.split(\"\\n\");\n\tconst startLine = loc.start.line - 1; // 0-based\n\tconst endLine = loc.end.line - 1;\n\n\tif (startLine < 0 || startLine >= lines.length) return \"\";\n\n\tif (startLine === endLine) {\n\t\t// Same line — extract the portion between start.column and end.column\n\t\tconst line = lines[startLine] ?? \"\";\n\t\treturn line.trim();\n\t}\n\n\t// Multi-line — return the affected lines\n\tconst clampedEnd = Math.min(endLine, lines.length - 1);\n\treturn lines\n\t\t.slice(startLine, clampedEnd + 1)\n\t\t.map((l) => l.trimEnd())\n\t\t.join(\"\\n\")\n\t\t.trim();\n}\n\n// ─── Schema Properties ──────────────────────────────────────────────────────\n// Utility for listing available properties in a schema, used to enrich\n// error messages with suggestions.\n\n/**\n * Lists the declared property names in a JSON Schema.\n * Returns an empty array if the schema has no `properties`.\n */\nexport function getSchemaPropertyNames(schema: JSONSchema7): string[] {\n\tconst names = new Set<string>();\n\n\t// Direct properties\n\tif (schema.properties) {\n\t\tfor (const key of Object.keys(schema.properties)) {\n\t\t\tnames.add(key);\n\t\t}\n\t}\n\n\t// Properties within combinators\n\tfor (const combinator of [\"allOf\", \"anyOf\", \"oneOf\"] as const) {\n\t\tconst branches = schema[combinator];\n\t\tif (branches) {\n\t\t\tfor (const branch of branches) {\n\t\t\t\tif (typeof branch === \"boolean\") continue;\n\t\t\t\tif (branch.properties) {\n\t\t\t\t\tfor (const key of Object.keys(branch.properties)) {\n\t\t\t\t\t\tnames.add(key);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn Array.from(names).sort();\n}\n\n// ─── Object Analysis Aggregation ─────────────────────────────────────────────\n// Factorizes the common recursion pattern over template objects:\n// iterate the keys, analyze each entry via a callback, accumulate\n// diagnostics, and build the output object schema.\n//\n// Used by:\n// - `analyzer.ts` (analyzeObjectTemplate)\n// - `Typebars.analyzeObject()` (typebars.ts)\n// - `CompiledTemplate.analyze()` in object mode (compiled-template.ts)\n\n/**\n * Aggregates analysis results from a set of named entries into a single\n * `AnalysisResult` with an object-typed `outputSchema`.\n *\n * @param keys - The keys of the object to analyze\n * @param analyzeEntry - Callback that analyzes an entry by its key\n * @returns An aggregated `AnalysisResult`\n *\n * @example\n * ```\n * aggregateObjectAnalysis(\n * Object.keys(template),\n * (key) => analyze(template[key], inputSchema),\n * );\n * ```\n */\nexport function aggregateObjectAnalysis(\n\tkeys: string[],\n\tanalyzeEntry: (key: string) => AnalysisResult,\n): AnalysisResult {\n\tconst allDiagnostics: TemplateDiagnostic[] = [];\n\tconst properties: Record<string, JSONSchema7> = {};\n\tlet allValid = true;\n\n\tfor (const key of keys) {\n\t\tconst child = analyzeEntry(key);\n\t\tif (!child.valid) allValid = false;\n\t\tallDiagnostics.push(...child.diagnostics);\n\t\tproperties[key] = child.outputSchema;\n\t}\n\n\treturn {\n\t\tvalid: allValid,\n\t\tdiagnostics: allDiagnostics,\n\t\toutputSchema: {\n\t\t\ttype: \"object\",\n\t\t\tproperties,\n\t\t\trequired: keys,\n\t\t},\n\t};\n}\n\n/**\n * Aggregates both analysis **and** execution results from a set of named\n * entries. Returns the aggregated `AnalysisResult` and the object of\n * executed values (or `undefined` if at least one entry is invalid).\n *\n * @param keys - The keys of the object\n * @param processEntry - Callback that analyzes and executes an entry by its key\n * @returns Aggregated `{ analysis, value }`\n */\nexport function aggregateObjectAnalysisAndExecution(\n\tkeys: string[],\n\tprocessEntry: (key: string) => { analysis: AnalysisResult; value: unknown },\n): { analysis: AnalysisResult; value: unknown } {\n\tconst allDiagnostics: TemplateDiagnostic[] = [];\n\tconst properties: Record<string, JSONSchema7> = {};\n\tconst resultValues: Record<string, unknown> = {};\n\tlet allValid = true;\n\n\tfor (const key of keys) {\n\t\tconst child = processEntry(key);\n\t\tif (!child.analysis.valid) allValid = false;\n\t\tallDiagnostics.push(...child.analysis.diagnostics);\n\t\tproperties[key] = child.analysis.outputSchema;\n\t\tresultValues[key] = child.value;\n\t}\n\n\tconst analysis: AnalysisResult = {\n\t\tvalid: allValid,\n\t\tdiagnostics: allDiagnostics,\n\t\toutputSchema: {\n\t\t\ttype: \"object\",\n\t\t\tproperties,\n\t\t\trequired: keys,\n\t\t},\n\t};\n\n\treturn {\n\t\tanalysis,\n\t\tvalue: allValid ? resultValues : undefined,\n\t};\n}\n\n// ─── Array Analysis Aggregation ──────────────────────────────────────────────\n// Factorizes the common recursion pattern over template arrays:\n// iterate the elements, analyze each entry via a callback, accumulate\n// diagnostics, and build the output array schema with a proper `items`.\n//\n// Used by:\n// - `analyzer.ts` (analyzeArrayTemplate)\n// - `Typebars.analyze()` (typebars.ts)\n// - `CompiledTemplate.analyze()` in array mode (compiled-template.ts)\n\n/**\n * Computes the `items` schema for an array from the output schemas of its\n * elements:\n * - If all elements share the same schema → that schema\n * - If there are multiple distinct schemas → `{ oneOf: [...] }`\n * - If the array is empty → `{}` (any type)\n */\nfunction computeArrayItemsSchema(elementSchemas: JSONSchema7[]): JSONSchema7 {\n\tif (elementSchemas.length === 0) return {};\n\n\t// Deduplicate schemas using deep equality\n\tconst unique: JSONSchema7[] = [];\n\tfor (const schema of elementSchemas) {\n\t\tif (!unique.some((u) => deepEqual(u, schema))) {\n\t\t\tunique.push(schema);\n\t\t}\n\t}\n\n\tif (unique.length === 1) {\n\t\treturn unique[0] as JSONSchema7;\n\t}\n\n\treturn { oneOf: unique };\n}\n\n/**\n * Aggregates analysis results from a set of array elements into a single\n * `AnalysisResult` with an array-typed `outputSchema`.\n *\n * @param length - The number of elements in the array\n * @param analyzeEntry - Callback that analyzes an element by its index\n * @returns An aggregated `AnalysisResult`\n *\n * @example\n * ```\n * aggregateArrayAnalysis(\n * template.length,\n * (index) => analyze(template[index], inputSchema),\n * );\n * ```\n */\nexport function aggregateArrayAnalysis(\n\tlength: number,\n\tanalyzeEntry: (index: number) => AnalysisResult,\n): AnalysisResult {\n\tconst allDiagnostics: TemplateDiagnostic[] = [];\n\tconst elementSchemas: JSONSchema7[] = [];\n\tlet allValid = true;\n\n\tfor (let i = 0; i < length; i++) {\n\t\tconst child = analyzeEntry(i);\n\t\tif (!child.valid) allValid = false;\n\t\tallDiagnostics.push(...child.diagnostics);\n\t\telementSchemas.push(child.outputSchema);\n\t}\n\n\treturn {\n\t\tvalid: allValid,\n\t\tdiagnostics: allDiagnostics,\n\t\toutputSchema: {\n\t\t\ttype: \"array\",\n\t\t\titems: computeArrayItemsSchema(elementSchemas),\n\t\t},\n\t};\n}\n\n/**\n * Aggregates both analysis **and** execution results from a set of array\n * elements. Returns the aggregated `AnalysisResult` and the array of\n * executed values (or `undefined` if at least one element is invalid).\n *\n * @param length - The number of elements\n * @param processEntry - Callback that analyzes and executes an element by index\n * @returns Aggregated `{ analysis, value }`\n */\nexport function aggregateArrayAnalysisAndExecution(\n\tlength: number,\n\tprocessEntry: (index: number) => { analysis: AnalysisResult; value: unknown },\n): { analysis: AnalysisResult; value: unknown } {\n\tconst allDiagnostics: TemplateDiagnostic[] = [];\n\tconst elementSchemas: JSONSchema7[] = [];\n\tconst resultValues: unknown[] = [];\n\tlet allValid = true;\n\n\tfor (let i = 0; i < length; i++) {\n\t\tconst child = processEntry(i);\n\t\tif (!child.analysis.valid) allValid = false;\n\t\tallDiagnostics.push(...child.analysis.diagnostics);\n\t\telementSchemas.push(child.analysis.outputSchema);\n\t\tresultValues.push(child.value);\n\t}\n\n\tconst analysis: AnalysisResult = {\n\t\tvalid: allValid,\n\t\tdiagnostics: allDiagnostics,\n\t\toutputSchema: {\n\t\t\ttype: \"array\",\n\t\t\titems: computeArrayItemsSchema(elementSchemas),\n\t\t},\n\t};\n\n\treturn {\n\t\tanalysis,\n\t\tvalue: allValid ? resultValues : undefined,\n\t};\n}\n"],"names":["LRUCache","aggregateArrayAnalysis","aggregateArrayAnalysisAndExecution","aggregateObjectAnalysis","aggregateObjectAnalysisAndExecution","deepEqual","extractSourceSnippet","getSchemaPropertyNames","a","b","Array","isArray","length","i","objA","objB","keysA","Object","keys","keysB","key","get","cache","has","undefined","value","delete","set","size","capacity","oldestKey","next","clear","Map","Error","template","loc","lines","split","startLine","start","line","endLine","end","trim","clampedEnd","Math","min","slice","map","l","trimEnd","join","schema","names","Set","properties","add","combinator","branches","branch","from","sort","analyzeEntry","allDiagnostics","allValid","child","valid","push","diagnostics","outputSchema","type","required","processEntry","resultValues","analysis","computeArrayItemsSchema","elementSchemas","unique","some","u","oneOf","items"],"mappings":"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"}