typebars 1.0.19 → 1.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/analyzer.js +1 -1
- package/dist/cjs/analyzer.js.map +1 -1
- package/dist/cjs/executor.js +1 -1
- package/dist/cjs/executor.js.map +1 -1
- package/dist/cjs/schema-resolver.d.ts +36 -3
- package/dist/cjs/schema-resolver.js +1 -1
- package/dist/cjs/schema-resolver.js.map +1 -1
- package/dist/cjs/types.d.ts +3 -1
- package/dist/cjs/types.js.map +1 -1
- package/dist/cjs/utils.js +1 -1
- package/dist/cjs/utils.js.map +1 -1
- package/dist/esm/analyzer.js +1 -1
- package/dist/esm/analyzer.js.map +1 -1
- package/dist/esm/executor.js +1 -1
- package/dist/esm/executor.js.map +1 -1
- package/dist/esm/schema-resolver.d.ts +36 -3
- package/dist/esm/schema-resolver.js +1 -1
- package/dist/esm/schema-resolver.js.map +1 -1
- package/dist/esm/types.d.ts +3 -1
- package/dist/esm/types.js.map +1 -1
- package/dist/esm/utils.js +1 -1
- package/dist/esm/utils.js.map +1 -1
- package/package.json +1 -1
package/dist/esm/analyzer.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/analyzer.ts"],"sourcesContent":["import type { JSONSchema7 } from \"json-schema\";\nimport { dispatchAnalyze } from \"./dispatch.ts\";\nimport {\n\tcreateMissingArgumentMessage,\n\tcreatePropertyNotFoundMessage,\n\tcreateRootPathTraversalMessage,\n\tcreateTypeMismatchMessage,\n\tcreateUnanalyzableMessage,\n\tcreateUnknownHelperMessage,\n} from \"./errors\";\nimport { MapHelpers } from \"./helpers/map-helpers.ts\";\nimport {\n\tdetectLiteralType,\n\textractExpressionIdentifier,\n\textractPathSegments,\n\tgetEffectiveBody,\n\tgetEffectivelySingleBlock,\n\tgetEffectivelySingleExpression,\n\tisRootPathTraversal,\n\tisRootSegments,\n\tisThisExpression,\n\tparse,\n} from \"./parser\";\nimport {\n\tassertNoConditionalSchema,\n\tresolveArrayItems,\n\tresolveSchemaPath,\n\tsimplifySchema,\n} from \"./schema-resolver\";\nimport type {\n\tAnalysisResult,\n\tDiagnosticCode,\n\tDiagnosticDetails,\n\tHelperDefinition,\n\tTemplateDiagnostic,\n\tTemplateInput,\n} from \"./types.ts\";\nimport {\n\tdeepEqual,\n\textractSourceSnippet,\n\tgetSchemaPropertyNames,\n} from \"./utils\";\n\n// ─── Static Analyzer ─────────────────────────────────────────────────────────\n// Static analysis of a Handlebars template against a JSON Schema v7\n// describing the available context.\n//\n// Merged architecture (v2):\n// A single AST traversal performs both **validation** and **return type\n// inference** simultaneously. This eliminates duplication between the former\n// `validate*` and `infer*` functions and improves performance by avoiding\n// a double traversal.\n//\n// Context:\n// The analysis context uses a **save/restore** pattern instead of creating\n// new objects on each recursion (`{ ...ctx, current: X }`). This reduces\n// GC pressure for deeply nested templates.\n//\n// ─── Template Identifiers ────────────────────────────────────────────────────\n// The `{{key:N}}` syntax allows referencing a variable from a specific\n// schema, identified by an integer N. The optional `identifierSchemas`\n// parameter provides a mapping `{ [id]: JSONSchema7 }`.\n//\n// Resolution rules:\n// - `{{meetingId}}` → validated against `inputSchema` (standard behavior)\n// - `{{meetingId:1}}` → validated against `identifierSchemas[1]`\n// - `{{meetingId:1}}` without `identifierSchemas[1]` → error\n\n// ─── Internal Types ──────────────────────────────────────────────────────────\n\n/** Context passed recursively during AST traversal */\ninterface AnalysisContext {\n\t/** Root schema (for resolving $refs) */\n\troot: JSONSchema7;\n\t/** Current context schema (changes with #each, #with) — mutated via save/restore */\n\tcurrent: JSONSchema7;\n\t/** Diagnostics accumulator */\n\tdiagnostics: TemplateDiagnostic[];\n\t/** Full template source (for extracting error snippets) */\n\ttemplate: string;\n\t/** Schemas by template identifier (for the {{key:N}} syntax) */\n\tidentifierSchemas?: Record<number, JSONSchema7>;\n\t/** Registered custom helpers (for static analysis) */\n\thelpers?: Map<string, HelperDefinition>;\n\t/**\n\t * Explicit coercion schema provided by the caller.\n\t * When set, static literal values like `\"123\"` will respect the type\n\t * declared in this schema instead of being auto-detected by\n\t * `detectLiteralType`. Unlike the previous `expectedOutputType`,\n\t * this is NEVER derived from the inputSchema — it must be explicitly\n\t * provided via the `coerceSchema` option.\n\t */\n\tcoerceSchema?: JSONSchema7;\n}\n\n// ─── Public API ──────────────────────────────────────────────────────────────\n\n/** Options for the standalone `analyze()` function */\nexport interface AnalyzeOptions {\n\t/** Schemas by template identifier (for the `{{key:N}}` syntax) */\n\tidentifierSchemas?: Record<number, JSONSchema7>;\n\t/**\n\t * Explicit coercion schema. When provided, static literal values\n\t * will respect the types declared in this schema instead of being\n\t * auto-detected by `detectLiteralType`.\n\t *\n\t * This schema is independent from the `inputSchema` (which describes\n\t * available variables) — it only controls the output type inference\n\t * for static content.\n\t */\n\tcoerceSchema?: JSONSchema7;\n\t/**\n\t * When `true`, properties whose values contain Handlebars expressions\n\t * (i.e. any `{{…}}` syntax) are excluded from the output schema.\n\t *\n\t * Only the properties with static values (literals, plain strings\n\t * without expressions) are retained. This is useful when you want\n\t * the output schema to describe only the known, compile-time-constant\n\t * portion of the template.\n\t *\n\t * This option only has an effect on **object** and **array** templates.\n\t * A root-level string template with expressions is analyzed normally\n\t * (there is no parent property to exclude it from).\n\t *\n\t * @default false\n\t */\n\texcludeTemplateExpression?: boolean;\n}\n\n// ─── Coerce Text Value ──────────────────────────────────────────────────────\n\n/**\n * Parses a raw text string into the appropriate JS primitive according to the\n * target JSON Schema type. Used when `coerceSchema` overrides the default\n * `detectLiteralType` inference for static literal values.\n */\nfunction coerceTextValue(\n\ttext: string,\n\ttargetType: \"string\" | \"number\" | \"integer\" | \"boolean\" | \"null\",\n): string | number | boolean | null {\n\tswitch (targetType) {\n\t\tcase \"number\":\n\t\tcase \"integer\":\n\t\t\treturn Number(text);\n\t\tcase \"boolean\":\n\t\t\treturn text === \"true\";\n\t\tcase \"null\":\n\t\t\treturn null;\n\t\tdefault:\n\t\t\treturn text;\n\t}\n}\n\n/**\n * Statically analyzes a template against a JSON Schema v7 describing the\n * available context.\n *\n * Backward-compatible version — parses the template internally.\n * Uses `dispatchAnalyze` for the recursive array/object/literal dispatching,\n * delegating only the string (template) case to `analyzeFromAst`.\n *\n * @param template - The template string (e.g. `\"Hello {{user.name}}\"`)\n * @param inputSchema - JSON Schema v7 describing the available variables\n * @param options - (optional) Analysis options (identifierSchemas, coerceSchema)\n * @returns An `AnalysisResult` containing validity, diagnostics, and the\n * inferred output schema.\n */\nexport function analyze(\n\ttemplate: TemplateInput,\n\tinputSchema: JSONSchema7 = {},\n\toptions?: AnalyzeOptions,\n): AnalysisResult {\n\treturn dispatchAnalyze(\n\t\ttemplate,\n\t\toptions,\n\t\t// String handler — parse and analyze the AST\n\t\t(tpl, coerceSchema) => {\n\t\t\tconst ast = parse(tpl);\n\t\t\treturn analyzeFromAst(ast, tpl, inputSchema, {\n\t\t\t\tidentifierSchemas: options?.identifierSchemas,\n\t\t\t\tcoerceSchema,\n\t\t\t});\n\t\t},\n\t\t// Recursive handler — re-enter analyze() for child elements\n\t\t(child, childOptions) => analyze(child, inputSchema, childOptions),\n\t);\n}\n\n/**\n * Statically analyzes a template from an already-parsed AST.\n *\n * This is the internal function used by `Typebars.compile()` and\n * `CompiledTemplate.analyze()` to avoid costly re-parsing.\n *\n * @param ast - The already-parsed Handlebars AST\n * @param template - The template source (for error snippets)\n * @param inputSchema - JSON Schema v7 describing the available variables\n * @param options - Additional options\n * @returns An `AnalysisResult`\n */\nexport function analyzeFromAst(\n\tast: hbs.AST.Program,\n\ttemplate: string,\n\tinputSchema: JSONSchema7 = {},\n\toptions?: {\n\t\tidentifierSchemas?: Record<number, JSONSchema7>;\n\t\thelpers?: Map<string, HelperDefinition>;\n\t\t/**\n\t\t * Explicit coercion schema. When set, static literal values will\n\t\t * respect the types declared in this schema instead of auto-detecting.\n\t\t * Unlike `expectedOutputType`, this is NEVER derived from inputSchema.\n\t\t */\n\t\tcoerceSchema?: JSONSchema7;\n\t},\n): AnalysisResult {\n\t// ── Reject unsupported schema features before analysis ────────────\n\t// Conditional schemas (if/then/else) are non-resolvable without runtime\n\t// data. Fail fast with a clear error rather than producing silently\n\t// incorrect results.\n\tassertNoConditionalSchema(inputSchema);\n\n\tif (options?.identifierSchemas) {\n\t\tfor (const [id, idSchema] of Object.entries(options.identifierSchemas)) {\n\t\t\tassertNoConditionalSchema(idSchema, `/identifierSchemas/${id}`);\n\t\t}\n\t}\n\n\tconst ctx: AnalysisContext = {\n\t\troot: inputSchema,\n\t\tcurrent: inputSchema,\n\t\tdiagnostics: [],\n\t\ttemplate,\n\t\tidentifierSchemas: options?.identifierSchemas,\n\t\thelpers: options?.helpers,\n\t\tcoerceSchema: options?.coerceSchema,\n\t};\n\n\t// Single pass: type inference + validation in one traversal.\n\tconst outputSchema = inferProgramType(ast, ctx);\n\n\tconst hasErrors = ctx.diagnostics.some((d) => d.severity === \"error\");\n\n\treturn {\n\t\tvalid: !hasErrors,\n\t\tdiagnostics: ctx.diagnostics,\n\t\toutputSchema: simplifySchema(outputSchema),\n\t};\n}\n\n// ─── Unified AST Traversal ───────────────────────────────────────────────────\n// A single set of functions handles both validation (emitting diagnostics)\n// and type inference (returning a JSONSchema7).\n//\n// Main functions:\n// - `inferProgramType` — entry point for a Program (template body or block)\n// - `processStatement` — dispatches a statement (validation side-effects)\n// - `processMustache` — handles a MustacheStatement (expression or inline helper)\n// - `inferBlockType` — handles a BlockStatement (if, each, with, custom…)\n\n/**\n * Dispatches the processing of an individual statement.\n *\n * Called by `inferProgramType` in the \"mixed template\" case to validate\n * each statement while ignoring the returned type (the result is always\n * `string` for a mixed template).\n *\n * @returns The inferred schema for this statement, or `undefined` for\n * statements with no semantics (ContentStatement, CommentStatement).\n */\nfunction processStatement(\n\tstmt: hbs.AST.Statement,\n\tctx: AnalysisContext,\n): JSONSchema7 | undefined {\n\tswitch (stmt.type) {\n\t\tcase \"ContentStatement\":\n\t\tcase \"CommentStatement\":\n\t\t\t// Static text or comment — nothing to validate, no type to infer\n\t\t\treturn undefined;\n\n\t\tcase \"MustacheStatement\":\n\t\t\treturn processMustache(stmt as hbs.AST.MustacheStatement, ctx);\n\n\t\tcase \"BlockStatement\":\n\t\t\treturn inferBlockType(stmt as hbs.AST.BlockStatement, ctx);\n\n\t\tdefault:\n\t\t\t// Unrecognized AST node — emit a warning rather than an error\n\t\t\t// to avoid blocking on future Handlebars extensions.\n\t\t\taddDiagnostic(\n\t\t\t\tctx,\n\t\t\t\t\"UNANALYZABLE\",\n\t\t\t\t\"warning\",\n\t\t\t\t`Unsupported AST node type: \"${stmt.type}\"`,\n\t\t\t\tstmt,\n\t\t\t);\n\t\t\treturn undefined;\n\t}\n}\n\n/**\n * Processes a MustacheStatement `{{expression}}` or `{{helper arg}}`.\n *\n * Distinguishes two cases:\n * 1. **Simple expression** (`{{name}}`, `{{user.age}}`) — resolution in the schema\n * 2. **Inline helper** (`{{uppercase name}}`) — params > 0 or hash present\n *\n * @returns The inferred schema for this expression\n */\nfunction processMustache(\n\tstmt: hbs.AST.MustacheStatement,\n\tctx: AnalysisContext,\n): JSONSchema7 {\n\t// Sub-expressions (nested helpers) are not supported for static\n\t// analysis — emit a warning.\n\tif (stmt.path.type === \"SubExpression\") {\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"UNANALYZABLE\",\n\t\t\t\"warning\",\n\t\t\t\"Sub-expressions are not statically analyzable\",\n\t\t\tstmt,\n\t\t);\n\t\treturn {};\n\t}\n\n\t// ── Inline helper detection ──────────────────────────────────────────────\n\t// If the MustacheStatement has parameters or a hash, it's a helper call\n\t// (e.g. `{{uppercase name}}`), not a simple expression.\n\tif (stmt.params.length > 0 || stmt.hash) {\n\t\tconst helperName = getExpressionName(stmt.path);\n\n\t\t// ── Special-case: map helper ─────────────────────────────────────\n\t\t// The `map` helper requires deep static analysis that the generic\n\t\t// helper path cannot perform: it must resolve the first argument as\n\t\t// an array-of-objects schema, then resolve the second argument (a\n\t\t// property name) within the item schema to infer the output type\n\t\t// `{ type: \"array\", items: <property schema> }`.\n\t\tif (helperName === MapHelpers.MAP_HELPER_NAME) {\n\t\t\treturn processMapHelper(stmt, ctx);\n\t\t}\n\n\t\t// Check if the helper is registered\n\t\tconst helper = ctx.helpers?.get(helperName);\n\t\tif (helper) {\n\t\t\tconst helperParams = helper.params;\n\n\t\t\t// ── Check the number of required parameters ──────────────\n\t\t\tif (helperParams) {\n\t\t\t\tconst requiredCount = helperParams.filter((p) => !p.optional).length;\n\t\t\t\tif (stmt.params.length < requiredCount) {\n\t\t\t\t\taddDiagnostic(\n\t\t\t\t\t\tctx,\n\t\t\t\t\t\t\"MISSING_ARGUMENT\",\n\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t`Helper \"${helperName}\" expects at least ${requiredCount} argument(s), but got ${stmt.params.length}`,\n\t\t\t\t\t\tstmt,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\thelperName,\n\t\t\t\t\t\t\texpected: `${requiredCount} argument(s)`,\n\t\t\t\t\t\t\tactual: `${stmt.params.length} argument(s)`,\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// ── Validate each parameter (existence + type) ───────────────\n\t\t\tfor (let i = 0; i < stmt.params.length; i++) {\n\t\t\t\tconst resolvedSchema = resolveExpressionWithDiagnostics(\n\t\t\t\t\tstmt.params[i] as hbs.AST.Expression,\n\t\t\t\t\tctx,\n\t\t\t\t\tstmt,\n\t\t\t\t);\n\n\t\t\t\t// Check type compatibility if the helper declares the\n\t\t\t\t// expected type for this parameter\n\t\t\t\tconst helperParam = helperParams?.[i];\n\t\t\t\tif (resolvedSchema && helperParam?.type) {\n\t\t\t\t\tconst expectedType = helperParam.type;\n\t\t\t\t\tif (!isParamTypeCompatible(resolvedSchema, expectedType)) {\n\t\t\t\t\t\tconst paramName = helperParam.name;\n\t\t\t\t\t\taddDiagnostic(\n\t\t\t\t\t\t\tctx,\n\t\t\t\t\t\t\t\"TYPE_MISMATCH\",\n\t\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t\t`Helper \"${helperName}\" parameter \"${paramName}\" expects ${schemaTypeLabel(expectedType)}, but got ${schemaTypeLabel(resolvedSchema)}`,\n\t\t\t\t\t\t\tstmt,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\thelperName,\n\t\t\t\t\t\t\t\texpected: schemaTypeLabel(expectedType),\n\t\t\t\t\t\t\t\tactual: schemaTypeLabel(resolvedSchema),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn helper.returnType ?? { type: \"string\" };\n\t\t}\n\n\t\t// Unknown inline helper — warning\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"UNKNOWN_HELPER\",\n\t\t\t\"warning\",\n\t\t\t`Unknown inline helper \"${helperName}\" — cannot analyze statically`,\n\t\t\tstmt,\n\t\t\t{ helperName },\n\t\t);\n\t\treturn { type: \"string\" };\n\t}\n\n\t// ── Simple expression ────────────────────────────────────────────────────\n\treturn resolveExpressionWithDiagnostics(stmt.path, ctx, stmt) ?? {};\n}\n\n// ─── map helper — special-case analysis ──────────────────────────────────────\n// Validates the arguments and infers the precise return type:\n// {{ map <arrayPath> <propertyName> }}\n// → { type: \"array\", items: <schema of the property in the item> }\n//\n// Validation rules:\n// 1. Exactly 2 arguments are required\n// 2. The first argument must resolve to an array schema\n// 3. The array items must be an object schema\n// 4. The second argument must be a string literal (property name)\n// 5. The property must exist in the item schema\n\nfunction processMapHelper(\n\tstmt: hbs.AST.MustacheStatement,\n\tctx: AnalysisContext,\n): JSONSchema7 {\n\tconst helperName = MapHelpers.MAP_HELPER_NAME;\n\n\t// ── 1. Check argument count ──────────────────────────────────────────\n\tif (stmt.params.length < 2) {\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"MISSING_ARGUMENT\",\n\t\t\t\"error\",\n\t\t\t`Helper \"${helperName}\" expects at least 2 argument(s), but got ${stmt.params.length}`,\n\t\t\tstmt,\n\t\t\t{\n\t\t\t\thelperName,\n\t\t\t\texpected: \"2 argument(s)\",\n\t\t\t\tactual: `${stmt.params.length} argument(s)`,\n\t\t\t},\n\t\t);\n\t\treturn { type: \"array\" };\n\t}\n\n\t// ── 2. Resolve the first argument (collection path) ──────────────────\n\tconst collectionExpr = stmt.params[0] as hbs.AST.Expression;\n\tconst collectionSchema = resolveExpressionWithDiagnostics(\n\t\tcollectionExpr,\n\t\tctx,\n\t\tstmt,\n\t);\n\n\tif (!collectionSchema) {\n\t\t// Path resolution failed — diagnostic already emitted\n\t\treturn { type: \"array\" };\n\t}\n\n\t// ── 3. Validate that the collection is an array ──────────────────────\n\tconst itemSchema = resolveArrayItems(collectionSchema, ctx.root);\n\tif (!itemSchema) {\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"TYPE_MISMATCH\",\n\t\t\t\"error\",\n\t\t\t`Helper \"${helperName}\" parameter \"collection\" expects an array, but got ${schemaTypeLabel(collectionSchema)}`,\n\t\t\tstmt,\n\t\t\t{\n\t\t\t\thelperName,\n\t\t\t\texpected: \"array\",\n\t\t\t\tactual: schemaTypeLabel(collectionSchema),\n\t\t\t},\n\t\t);\n\t\treturn { type: \"array\" };\n\t}\n\n\t// ── 4. Validate that the items are objects ───────────────────────────\n\t// If the items are arrays (e.g. from a nested map), flatten one level\n\t// to match the runtime `flat(1)` behavior and use the inner items instead.\n\tlet effectiveItemSchema = itemSchema;\n\tconst itemType = effectiveItemSchema.type;\n\tif (\n\t\titemType === \"array\" ||\n\t\t(Array.isArray(itemType) && itemType.includes(\"array\"))\n\t) {\n\t\tconst innerItems = resolveArrayItems(effectiveItemSchema, ctx.root);\n\t\tif (innerItems) {\n\t\t\teffectiveItemSchema = innerItems;\n\t\t}\n\t}\n\n\tconst effectiveItemType = effectiveItemSchema.type;\n\tconst isObject =\n\t\teffectiveItemType === \"object\" ||\n\t\t(Array.isArray(effectiveItemType) &&\n\t\t\teffectiveItemType.includes(\"object\")) ||\n\t\t// If no type but has properties, treat as object\n\t\t(!effectiveItemType && effectiveItemSchema.properties !== undefined);\n\n\tif (!isObject && effectiveItemType !== undefined) {\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"TYPE_MISMATCH\",\n\t\t\t\"error\",\n\t\t\t`Helper \"${helperName}\" expects an array of objects, but the array items have type \"${schemaTypeLabel(effectiveItemSchema)}\"`,\n\t\t\tstmt,\n\t\t\t{\n\t\t\t\thelperName,\n\t\t\t\texpected: \"object\",\n\t\t\t\tactual: schemaTypeLabel(effectiveItemSchema),\n\t\t\t},\n\t\t);\n\t\treturn { type: \"array\" };\n\t}\n\n\t// ── 5. Validate the second argument (property name) ──────────────────\n\tconst propertyExpr = stmt.params[1] as hbs.AST.Expression;\n\n\t// The property name MUST be a StringLiteral (quoted string like `\"name\"`).\n\t// A bare identifier like `name` is parsed by Handlebars as a PathExpression,\n\t// which would be resolved as a data path at runtime — yielding `undefined`\n\t// when the identifier doesn't exist in the top-level context. This is a\n\t// common mistake, so we provide a clear error message guiding the user.\n\tlet propertyName: string | undefined;\n\n\tif (propertyExpr.type === \"PathExpression\") {\n\t\tconst bare = (propertyExpr as hbs.AST.PathExpression).original;\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"TYPE_MISMATCH\",\n\t\t\t\"error\",\n\t\t\t`Helper \"${helperName}\" parameter \"property\" must be a quoted string. ` +\n\t\t\t\t`Use {{ ${helperName} … \"${bare}\" }} instead of {{ ${helperName} … ${bare} }}`,\n\t\t\tstmt,\n\t\t\t{\n\t\t\t\thelperName,\n\t\t\t\texpected: 'StringLiteral (e.g. \"property\")',\n\t\t\t\tactual: `PathExpression (${bare})`,\n\t\t\t},\n\t\t);\n\t\treturn { type: \"array\" };\n\t}\n\n\tif (propertyExpr.type === \"StringLiteral\") {\n\t\tpropertyName = (propertyExpr as hbs.AST.StringLiteral).value;\n\t}\n\n\tif (!propertyName) {\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"TYPE_MISMATCH\",\n\t\t\t\"error\",\n\t\t\t`Helper \"${helperName}\" parameter \"property\" expects a quoted string literal, but got ${propertyExpr.type}`,\n\t\t\tstmt,\n\t\t\t{\n\t\t\t\thelperName,\n\t\t\t\texpected: 'StringLiteral (e.g. \"property\")',\n\t\t\t\tactual: propertyExpr.type,\n\t\t\t},\n\t\t);\n\t\treturn { type: \"array\" };\n\t}\n\n\t// ── 6. Resolve the property within the item schema ───────────────────\n\tconst propertySchema = resolveSchemaPath(effectiveItemSchema, [propertyName]);\n\tif (!propertySchema) {\n\t\tconst availableProperties = getSchemaPropertyNames(effectiveItemSchema);\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"UNKNOWN_PROPERTY\",\n\t\t\t\"error\",\n\t\t\tcreatePropertyNotFoundMessage(propertyName, availableProperties),\n\t\t\tstmt,\n\t\t\t{\n\t\t\t\tpath: propertyName,\n\t\t\t\tavailableProperties,\n\t\t\t},\n\t\t);\n\t\treturn { type: \"array\" };\n\t}\n\n\t// ── 7. Return the inferred output schema ─────────────────────────────\n\treturn { type: \"array\", items: propertySchema };\n}\n\n/**\n * Checks whether a resolved type is compatible with the type expected\n * by a helper parameter.\n *\n * Compatibility rules:\n * - If either schema has no `type`, validation is not possible → compatible\n * - `integer` is compatible with `number` (integer ⊂ number)\n * - For multiple types (e.g. `[\"string\", \"number\"]`), at least one resolved\n * type must match one expected type\n */\nfunction isParamTypeCompatible(\n\tresolved: JSONSchema7,\n\texpected: JSONSchema7,\n): boolean {\n\t// If either has no type info, we cannot validate\n\tif (!expected.type || !resolved.type) return true;\n\n\tconst expectedTypes = Array.isArray(expected.type)\n\t\t? expected.type\n\t\t: [expected.type];\n\tconst resolvedTypes = Array.isArray(resolved.type)\n\t\t? resolved.type\n\t\t: [resolved.type];\n\n\t// At least one resolved type must be compatible with one expected type\n\treturn resolvedTypes.some((rt) =>\n\t\texpectedTypes.some(\n\t\t\t(et) =>\n\t\t\t\trt === et ||\n\t\t\t\t// integer is a subtype of number\n\t\t\t\t(et === \"number\" && rt === \"integer\") ||\n\t\t\t\t(et === \"integer\" && rt === \"number\"),\n\t\t),\n\t);\n}\n\n/**\n * Infers the output type of a `Program` (template body or block body).\n *\n * Handles 4 cases, from most specific to most general:\n *\n * 1. **Single expression** `{{expr}}` → type of the expression\n * 2. **Single block** `{{#if}}…{{/if}}` → type of the block\n * 3. **Pure text content** → literal detection (number, boolean, null)\n * 4. **Mixed template** → always `string` (concatenation)\n *\n * Validation is performed alongside inference: each expression and block\n * is validated during processing.\n */\nfunction inferProgramType(\n\tprogram: hbs.AST.Program,\n\tctx: AnalysisContext,\n): JSONSchema7 {\n\tconst effective = getEffectiveBody(program);\n\n\t// No significant statements → empty string\n\tif (effective.length === 0) {\n\t\treturn { type: \"string\" };\n\t}\n\n\t// ── Case 1: single expression {{expr}} ─────────────────────────────────\n\tconst singleExpr = getEffectivelySingleExpression(program);\n\tif (singleExpr) {\n\t\treturn processMustache(singleExpr, ctx);\n\t}\n\n\t// ── Case 2: single block {{#if}}, {{#each}}, {{#with}}, … ──────────────\n\tconst singleBlock = getEffectivelySingleBlock(program);\n\tif (singleBlock) {\n\t\treturn inferBlockType(singleBlock, ctx);\n\t}\n\n\t// ── Case 3: only ContentStatements (no expressions) ────────────────────\n\t// If the concatenated (trimmed) text is a typed literal (number, boolean,\n\t// null), we infer the corresponding type.\n\tconst allContent = effective.every((s) => s.type === \"ContentStatement\");\n\tif (allContent) {\n\t\tconst text = effective\n\t\t\t.map((s) => (s as hbs.AST.ContentStatement).value)\n\t\t\t.join(\"\")\n\t\t\t.trim();\n\n\t\tif (text === \"\") return { type: \"string\" };\n\n\t\t// If an explicit coerceSchema was provided and declares a specific\n\t\t// primitive type, respect it instead of auto-detecting. For example,\n\t\t// \"123\" with coerceSchema `{ type: \"string\" }` should stay \"string\".\n\t\t// This only applies when coerceSchema is explicitly set — the\n\t\t// inputSchema is NEVER used for coercion.\n\t\t//\n\t\t// Only the **type** is extracted from coerceSchema. Value-level\n\t\t// constraints (enum, const, format, pattern, minLength, …) are NOT\n\t\t// propagated because they describe what the *consumer* accepts, not\n\t\t// what the literal *produces*. The actual literal value is set as\n\t\t// `const` so downstream compatibility checkers can detect mismatches.\n\t\tconst coercedType = ctx.coerceSchema?.type;\n\t\tif (\n\t\t\ttypeof coercedType === \"string\" &&\n\t\t\t(coercedType === \"string\" ||\n\t\t\t\tcoercedType === \"number\" ||\n\t\t\t\tcoercedType === \"integer\" ||\n\t\t\t\tcoercedType === \"boolean\" ||\n\t\t\t\tcoercedType === \"null\")\n\t\t) {\n\t\t\tconst coercedValue = coerceTextValue(text, coercedType);\n\t\t\treturn { type: coercedType, const: coercedValue } as JSONSchema7;\n\t\t}\n\n\t\tconst literalType = detectLiteralType(text);\n\t\tif (literalType) return { type: literalType };\n\t}\n\n\t// ── Case 4: multiple blocks only (no significant text between them) ────\n\t// When the effective body consists entirely of BlockStatements, gather\n\t// each block's inferred type and combine them via oneOf. This handles\n\t// templates like:\n\t// {{#if showName}}{{name}}{{/if}}\n\t// {{#if showAge}}{{age}}{{/if}}\n\t// where the output could be string OR number depending on which branch\n\t// is active.\n\tconst allBlocks = effective.every((s) => s.type === \"BlockStatement\");\n\tif (allBlocks) {\n\t\tconst types: JSONSchema7[] = [];\n\t\tfor (const stmt of effective) {\n\t\t\tconst t = inferBlockType(stmt as hbs.AST.BlockStatement, ctx);\n\t\t\tif (t) types.push(t);\n\t\t}\n\t\tif (types.length === 1) return types[0] as JSONSchema7;\n\t\tif (types.length > 1) return simplifySchema({ oneOf: types });\n\t\treturn { type: \"string\" };\n\t}\n\n\t// ── Case 5: mixed template (text + expressions, blocks…) ───────────────\n\t// Traverse all statements for validation (side-effects: diagnostics).\n\t// The result is always string (concatenation).\n\tfor (const stmt of program.body) {\n\t\tprocessStatement(stmt, ctx);\n\t}\n\treturn { type: \"string\" };\n}\n\n/**\n * Infers the output type of a BlockStatement and validates its content.\n *\n * Supports built-in helpers (`if`, `unless`, `each`, `with`) and custom\n * helpers registered via `Typebars.registerHelper()`.\n *\n * Uses the **save/restore** pattern for context: instead of creating a new\n * object `{ ...ctx, current: X }` on each recursion, we save `ctx.current`,\n * mutate it, process the body, then restore. This reduces GC pressure for\n * deeply nested templates.\n */\nfunction inferBlockType(\n\tstmt: hbs.AST.BlockStatement,\n\tctx: AnalysisContext,\n): JSONSchema7 {\n\tconst helperName = getBlockHelperName(stmt);\n\n\tswitch (helperName) {\n\t\t// ── if / unless ──────────────────────────────────────────────────────\n\t\t// Validate the condition argument, then infer types from both branches.\n\t\tcase \"if\":\n\t\tcase \"unless\": {\n\t\t\tconst arg = getBlockArgument(stmt);\n\t\t\tif (arg) {\n\t\t\t\tresolveExpressionWithDiagnostics(arg, ctx, stmt);\n\t\t\t} else {\n\t\t\t\taddDiagnostic(\n\t\t\t\t\tctx,\n\t\t\t\t\t\"MISSING_ARGUMENT\",\n\t\t\t\t\t\"error\",\n\t\t\t\t\tcreateMissingArgumentMessage(helperName),\n\t\t\t\t\tstmt,\n\t\t\t\t\t{ helperName },\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Infer the type of the \"then\" branch\n\t\t\tconst thenType = inferProgramType(stmt.program, ctx);\n\n\t\t\tif (stmt.inverse) {\n\t\t\t\tconst elseType = inferProgramType(stmt.inverse, ctx);\n\t\t\t\t// If both branches have the same type → single type\n\t\t\t\tif (deepEqual(thenType, elseType)) return thenType;\n\t\t\t\t// Otherwise → union of both types\n\t\t\t\treturn simplifySchema({ oneOf: [thenType, elseType] });\n\t\t\t}\n\n\t\t\t// No else branch → the result is the type of the then branch\n\t\t\t// (conceptually optional, but Handlebars returns \"\" for falsy)\n\t\t\treturn thenType;\n\t\t}\n\n\t\t// ── each ─────────────────────────────────────────────────────────────\n\t\t// Resolve the collection schema, then validate the body with the item\n\t\t// schema as the new context.\n\t\tcase \"each\": {\n\t\t\tconst arg = getBlockArgument(stmt);\n\t\t\tif (!arg) {\n\t\t\t\taddDiagnostic(\n\t\t\t\t\tctx,\n\t\t\t\t\t\"MISSING_ARGUMENT\",\n\t\t\t\t\t\"error\",\n\t\t\t\t\tcreateMissingArgumentMessage(\"each\"),\n\t\t\t\t\tstmt,\n\t\t\t\t\t{ helperName: \"each\" },\n\t\t\t\t);\n\t\t\t\t// Validate the body with an empty context (best-effort)\n\t\t\t\tconst saved = ctx.current;\n\t\t\t\tctx.current = {};\n\t\t\t\tinferProgramType(stmt.program, ctx);\n\t\t\t\tctx.current = saved;\n\t\t\t\tif (stmt.inverse) inferProgramType(stmt.inverse, ctx);\n\t\t\t\treturn { type: \"string\" };\n\t\t\t}\n\n\t\t\tconst collectionSchema = resolveExpressionWithDiagnostics(arg, ctx, stmt);\n\t\t\tif (!collectionSchema) {\n\t\t\t\t// The path could not be resolved — diagnostic already emitted.\n\t\t\t\tconst saved = ctx.current;\n\t\t\t\tctx.current = {};\n\t\t\t\tinferProgramType(stmt.program, ctx);\n\t\t\t\tctx.current = saved;\n\t\t\t\tif (stmt.inverse) inferProgramType(stmt.inverse, ctx);\n\t\t\t\treturn { type: \"string\" };\n\t\t\t}\n\n\t\t\t// Resolve the schema of the array elements\n\t\t\tconst itemSchema = resolveArrayItems(collectionSchema, ctx.root);\n\t\t\tif (!itemSchema) {\n\t\t\t\taddDiagnostic(\n\t\t\t\t\tctx,\n\t\t\t\t\t\"TYPE_MISMATCH\",\n\t\t\t\t\t\"error\",\n\t\t\t\t\tcreateTypeMismatchMessage(\n\t\t\t\t\t\t\"each\",\n\t\t\t\t\t\t\"an array\",\n\t\t\t\t\t\tschemaTypeLabel(collectionSchema),\n\t\t\t\t\t),\n\t\t\t\t\tstmt,\n\t\t\t\t\t{\n\t\t\t\t\t\thelperName: \"each\",\n\t\t\t\t\t\texpected: \"array\",\n\t\t\t\t\t\tactual: schemaTypeLabel(collectionSchema),\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t\t// Validate the body with an empty context (best-effort)\n\t\t\t\tconst saved = ctx.current;\n\t\t\t\tctx.current = {};\n\t\t\t\tinferProgramType(stmt.program, ctx);\n\t\t\t\tctx.current = saved;\n\t\t\t\tif (stmt.inverse) inferProgramType(stmt.inverse, ctx);\n\t\t\t\treturn { type: \"string\" };\n\t\t\t}\n\n\t\t\t// Validate the body with the item schema as the new context\n\t\t\tconst saved = ctx.current;\n\t\t\tctx.current = itemSchema;\n\t\t\tinferProgramType(stmt.program, ctx);\n\t\t\tctx.current = saved;\n\n\t\t\t// The inverse branch ({{else}}) keeps the parent context\n\t\t\tif (stmt.inverse) inferProgramType(stmt.inverse, ctx);\n\n\t\t\t// An each concatenates renders → always string\n\t\t\treturn { type: \"string\" };\n\t\t}\n\n\t\t// ── with ─────────────────────────────────────────────────────────────\n\t\t// Resolve the inner schema, then validate the body with it as the\n\t\t// new context.\n\t\tcase \"with\": {\n\t\t\tconst arg = getBlockArgument(stmt);\n\t\t\tif (!arg) {\n\t\t\t\taddDiagnostic(\n\t\t\t\t\tctx,\n\t\t\t\t\t\"MISSING_ARGUMENT\",\n\t\t\t\t\t\"error\",\n\t\t\t\t\tcreateMissingArgumentMessage(\"with\"),\n\t\t\t\t\tstmt,\n\t\t\t\t\t{ helperName: \"with\" },\n\t\t\t\t);\n\t\t\t\t// Validate the body with an empty context\n\t\t\t\tconst saved = ctx.current;\n\t\t\t\tctx.current = {};\n\t\t\t\tconst result = inferProgramType(stmt.program, ctx);\n\t\t\t\tctx.current = saved;\n\t\t\t\tif (stmt.inverse) inferProgramType(stmt.inverse, ctx);\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tconst innerSchema = resolveExpressionWithDiagnostics(arg, ctx, stmt);\n\n\t\t\tconst saved = ctx.current;\n\t\t\tctx.current = innerSchema ?? {};\n\t\t\tconst result = inferProgramType(stmt.program, ctx);\n\t\t\tctx.current = saved;\n\n\t\t\t// The inverse branch keeps the parent context\n\t\t\tif (stmt.inverse) inferProgramType(stmt.inverse, ctx);\n\n\t\t\treturn result;\n\t\t}\n\n\t\t// ── Custom or unknown helper ─────────────────────────────────────────\n\t\tdefault: {\n\t\t\tconst helper = ctx.helpers?.get(helperName);\n\t\t\tif (helper) {\n\t\t\t\t// Registered custom helper — validate parameters\n\t\t\t\tfor (const param of stmt.params) {\n\t\t\t\t\tresolveExpressionWithDiagnostics(\n\t\t\t\t\t\tparam as hbs.AST.Expression,\n\t\t\t\t\t\tctx,\n\t\t\t\t\t\tstmt,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\t// Validate the body with the current context\n\t\t\t\tinferProgramType(stmt.program, ctx);\n\t\t\t\tif (stmt.inverse) inferProgramType(stmt.inverse, ctx);\n\t\t\t\treturn helper.returnType ?? { type: \"string\" };\n\t\t\t}\n\n\t\t\t// Unknown helper — warning\n\t\t\taddDiagnostic(\n\t\t\t\tctx,\n\t\t\t\t\"UNKNOWN_HELPER\",\n\t\t\t\t\"warning\",\n\t\t\t\tcreateUnknownHelperMessage(helperName),\n\t\t\t\tstmt,\n\t\t\t\t{ helperName },\n\t\t\t);\n\t\t\t// Still validate the body with the current context (best-effort)\n\t\t\tinferProgramType(stmt.program, ctx);\n\t\t\tif (stmt.inverse) inferProgramType(stmt.inverse, ctx);\n\t\t\treturn { type: \"string\" };\n\t\t}\n\t}\n}\n\n// ─── Expression Resolution ───────────────────────────────────────────────────\n\n/**\n * Resolves an AST expression to a sub-schema, emitting a diagnostic\n * if the path cannot be resolved.\n *\n * Handles the `{{key:N}}` syntax:\n * - If the expression has an identifier N → resolution in `identifierSchemas[N]`\n * - If identifier N has no associated schema → error\n * - If no identifier → resolution in `ctx.current` (standard behavior)\n *\n * @returns The resolved sub-schema, or `undefined` if the path is invalid.\n */\nfunction resolveExpressionWithDiagnostics(\n\texpr: hbs.AST.Expression,\n\tctx: AnalysisContext,\n\t/** Parent AST node (for diagnostic location) */\n\tparentNode?: hbs.AST.Node,\n): JSONSchema7 | undefined {\n\t// Handle `this` / `.` → return the current context\n\tif (isThisExpression(expr)) {\n\t\treturn ctx.current;\n\t}\n\n\t// ── SubExpression (nested helper call, e.g. `(lt account.balance 500)`) ──\n\tif (expr.type === \"SubExpression\") {\n\t\treturn resolveSubExpression(expr as hbs.AST.SubExpression, ctx, parentNode);\n\t}\n\n\tconst segments = extractPathSegments(expr);\n\tif (segments.length === 0) {\n\t\t// Expression that is not a PathExpression (e.g. literal)\n\t\tif (expr.type === \"StringLiteral\") return { type: \"string\" };\n\t\tif (expr.type === \"NumberLiteral\") return { type: \"number\" };\n\t\tif (expr.type === \"BooleanLiteral\") return { type: \"boolean\" };\n\t\tif (expr.type === \"NullLiteral\") return { type: \"null\" };\n\t\tif (expr.type === \"UndefinedLiteral\") return {};\n\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"UNANALYZABLE\",\n\t\t\t\"warning\",\n\t\t\tcreateUnanalyzableMessage(expr.type),\n\t\t\tparentNode ?? expr,\n\t\t);\n\t\treturn undefined;\n\t}\n\n\t// ── Identifier extraction ──────────────────────────────────────────────\n\t// Extract the `:N` suffix BEFORE checking for `$root` so that both\n\t// `{{$root}}` and `{{$root:2}}` are handled uniformly.\n\tconst { cleanSegments, identifier } = extractExpressionIdentifier(segments);\n\n\t// ── $root token ──────────────────────────────────────────────────────\n\t// Path traversal ($root.name, $root.address.city) is always forbidden,\n\t// regardless of whether an identifier is present.\n\tif (isRootPathTraversal(cleanSegments)) {\n\t\tconst fullPath = cleanSegments.join(\".\");\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"ROOT_PATH_TRAVERSAL\",\n\t\t\t\"error\",\n\t\t\tcreateRootPathTraversalMessage(fullPath),\n\t\t\tparentNode ?? expr,\n\t\t\t{ path: fullPath },\n\t\t);\n\t\treturn undefined;\n\t}\n\n\t// `{{$root}}` → return the entire current context schema\n\t// `{{$root:N}}` → return the entire schema for identifier N\n\tif (isRootSegments(cleanSegments)) {\n\t\tif (identifier !== null) {\n\t\t\treturn resolveRootWithIdentifier(identifier, ctx, parentNode ?? expr);\n\t\t}\n\t\treturn ctx.current;\n\t}\n\n\tif (identifier !== null) {\n\t\t// The expression uses the {{key:N}} syntax — resolve from\n\t\t// the schema of identifier N.\n\t\treturn resolveWithIdentifier(\n\t\t\tcleanSegments,\n\t\t\tidentifier,\n\t\t\tctx,\n\t\t\tparentNode ?? expr,\n\t\t);\n\t}\n\n\t// ── Standard resolution (no identifier) ────────────────────────────────\n\tconst resolved = resolveSchemaPath(ctx.current, cleanSegments);\n\tif (resolved === undefined) {\n\t\tconst fullPath = cleanSegments.join(\".\");\n\t\tconst availableProperties = getSchemaPropertyNames(ctx.current);\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"UNKNOWN_PROPERTY\",\n\t\t\t\"error\",\n\t\t\tcreatePropertyNotFoundMessage(fullPath, availableProperties),\n\t\t\tparentNode ?? expr,\n\t\t\t{ path: fullPath, availableProperties },\n\t\t);\n\t\treturn undefined;\n\t}\n\n\treturn resolved;\n}\n\n/**\n * Resolves `{{$root:N}}` — returns the **entire** schema for identifier N.\n *\n * This is the identifier-aware counterpart of returning `ctx.current` for\n * a plain `{{$root}}`. Instead of navigating into properties, it returns\n * the identifier's root schema directly.\n *\n * Emits an error diagnostic if:\n * - No `identifierSchemas` were provided\n * - Identifier N has no associated schema\n */\nfunction resolveRootWithIdentifier(\n\tidentifier: number,\n\tctx: AnalysisContext,\n\tnode: hbs.AST.Node,\n): JSONSchema7 | undefined {\n\t// No identifierSchemas provided at all\n\tif (!ctx.identifierSchemas) {\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"MISSING_IDENTIFIER_SCHEMAS\",\n\t\t\t\"error\",\n\t\t\t`Property \"$root:${identifier}\" uses an identifier but no identifier schemas were provided`,\n\t\t\tnode,\n\t\t\t{ path: `$root:${identifier}`, identifier },\n\t\t);\n\t\treturn undefined;\n\t}\n\n\t// The identifier does not exist in the provided schemas\n\tconst idSchema = ctx.identifierSchemas[identifier];\n\tif (!idSchema) {\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"UNKNOWN_IDENTIFIER\",\n\t\t\t\"error\",\n\t\t\t`Property \"$root:${identifier}\" references identifier ${identifier} but no schema exists for this identifier`,\n\t\t\tnode,\n\t\t\t{ path: `$root:${identifier}`, identifier },\n\t\t);\n\t\treturn undefined;\n\t}\n\n\t// Return the entire schema for identifier N\n\treturn idSchema;\n}\n\n/**\n * Resolves an expression with identifier `{{key:N}}` by looking up the\n * schema associated with identifier N.\n *\n * Emits an error diagnostic if:\n * - No `identifierSchemas` were provided\n * - Identifier N has no associated schema\n * - The property does not exist in the identifier's schema\n */\nfunction resolveWithIdentifier(\n\tcleanSegments: string[],\n\tidentifier: number,\n\tctx: AnalysisContext,\n\tnode: hbs.AST.Node,\n): JSONSchema7 | undefined {\n\tconst fullPath = cleanSegments.join(\".\");\n\n\t// No identifierSchemas provided at all\n\tif (!ctx.identifierSchemas) {\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"MISSING_IDENTIFIER_SCHEMAS\",\n\t\t\t\"error\",\n\t\t\t`Property \"${fullPath}:${identifier}\" uses an identifier but no identifier schemas were provided`,\n\t\t\tnode,\n\t\t\t{ path: `${fullPath}:${identifier}`, identifier },\n\t\t);\n\t\treturn undefined;\n\t}\n\n\t// The identifier does not exist in the provided schemas\n\tconst idSchema = ctx.identifierSchemas[identifier];\n\tif (!idSchema) {\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"UNKNOWN_IDENTIFIER\",\n\t\t\t\"error\",\n\t\t\t`Property \"${fullPath}:${identifier}\" references identifier ${identifier} but no schema exists for this identifier`,\n\t\t\tnode,\n\t\t\t{ path: `${fullPath}:${identifier}`, identifier },\n\t\t);\n\t\treturn undefined;\n\t}\n\n\t// Resolve the path within the identifier's schema\n\tconst resolved = resolveSchemaPath(idSchema, cleanSegments);\n\tif (resolved === undefined) {\n\t\tconst availableProperties = getSchemaPropertyNames(idSchema);\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"IDENTIFIER_PROPERTY_NOT_FOUND\",\n\t\t\t\"error\",\n\t\t\t`Property \"${fullPath}\" does not exist in the schema for identifier ${identifier}`,\n\t\t\tnode,\n\t\t\t{\n\t\t\t\tpath: fullPath,\n\t\t\t\tidentifier,\n\t\t\t\tavailableProperties,\n\t\t\t},\n\t\t);\n\t\treturn undefined;\n\t}\n\n\treturn resolved;\n}\n\n// ─── Utilities ───────────────────────────────────────────────────────────────\n\n/**\n * Extracts the first argument of a BlockStatement.\n *\n * In the Handlebars AST, for `{{#if active}}`:\n * - `stmt.path` → PathExpression(\"if\") ← the helper name\n * - `stmt.params[0]` → PathExpression(\"active\") ← the actual argument\n *\n * @returns The argument expression, or `undefined` if the block has no argument.\n */\n// ─── SubExpression Resolution ────────────────────────────────────────────────\n\n/**\n * Resolves a SubExpression (nested helper call) such as `(lt account.balance 500)`.\n *\n * This mirrors the helper-call logic in `processMustache` but applies to\n * expressions used as arguments (e.g. inside `{{#if (lt a b)}}`).\n *\n * Steps:\n * 1. Extract the helper name from the SubExpression's path.\n * 2. Look up the helper in `ctx.helpers`.\n * 3. Validate argument count and types.\n * 4. Return the helper's declared `returnType` (defaults to `{ type: \"string\" }`).\n */\nfunction resolveSubExpression(\n\texpr: hbs.AST.SubExpression,\n\tctx: AnalysisContext,\n\tparentNode?: hbs.AST.Node,\n): JSONSchema7 | undefined {\n\tconst helperName = getExpressionName(expr.path);\n\n\t// ── Special-case: map helper ─────────────────────────────────────\n\t// The `map` helper requires deep static analysis to infer the\n\t// precise return type `{ type: \"array\", items: <property schema> }`.\n\t// The generic path would only return `{ type: \"array\" }` (the static\n\t// returnType), losing the item schema needed by nested map calls.\n\tif (helperName === MapHelpers.MAP_HELPER_NAME) {\n\t\treturn processMapSubExpression(expr, ctx, parentNode);\n\t}\n\n\tconst helper = ctx.helpers?.get(helperName);\n\tif (!helper) {\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"UNKNOWN_HELPER\",\n\t\t\t\"warning\",\n\t\t\t`Unknown sub-expression helper \"${helperName}\" — cannot analyze statically`,\n\t\t\tparentNode ?? expr,\n\t\t\t{ helperName },\n\t\t);\n\t\treturn { type: \"string\" };\n\t}\n\n\tconst helperParams = helper.params;\n\n\t// ── Check the number of required parameters ──────────────────────\n\tif (helperParams) {\n\t\tconst requiredCount = helperParams.filter((p) => !p.optional).length;\n\t\tif (expr.params.length < requiredCount) {\n\t\t\taddDiagnostic(\n\t\t\t\tctx,\n\t\t\t\t\"MISSING_ARGUMENT\",\n\t\t\t\t\"error\",\n\t\t\t\t`Helper \"${helperName}\" expects at least ${requiredCount} argument(s), but got ${expr.params.length}`,\n\t\t\t\tparentNode ?? expr,\n\t\t\t\t{\n\t\t\t\t\thelperName,\n\t\t\t\t\texpected: `${requiredCount} argument(s)`,\n\t\t\t\t\tactual: `${expr.params.length} argument(s)`,\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t}\n\n\t// ── Validate each parameter (existence + type) ───────────────────\n\tfor (let i = 0; i < expr.params.length; i++) {\n\t\tconst resolvedSchema = resolveExpressionWithDiagnostics(\n\t\t\texpr.params[i] as hbs.AST.Expression,\n\t\t\tctx,\n\t\t\tparentNode ?? expr,\n\t\t);\n\n\t\tconst helperParam = helperParams?.[i];\n\t\tif (resolvedSchema && helperParam?.type) {\n\t\t\tconst expectedType = helperParam.type;\n\t\t\tif (!isParamTypeCompatible(resolvedSchema, expectedType)) {\n\t\t\t\tconst paramName = helperParam.name;\n\t\t\t\taddDiagnostic(\n\t\t\t\t\tctx,\n\t\t\t\t\t\"TYPE_MISMATCH\",\n\t\t\t\t\t\"error\",\n\t\t\t\t\t`Helper \"${helperName}\" parameter \"${paramName}\" expects ${schemaTypeLabel(expectedType)}, but got ${schemaTypeLabel(resolvedSchema)}`,\n\t\t\t\t\tparentNode ?? expr,\n\t\t\t\t\t{\n\t\t\t\t\t\thelperName,\n\t\t\t\t\t\texpected: schemaTypeLabel(expectedType),\n\t\t\t\t\t\tactual: schemaTypeLabel(resolvedSchema),\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn helper.returnType ?? { type: \"string\" };\n}\n\n// ─── map helper — sub-expression analysis ────────────────────────────────────\n// Mirrors processMapHelper but for SubExpression nodes (e.g.\n// `(map users 'cartItems')` used as an argument to another helper).\n// This enables nested map: `{{ map (map users 'cartItems') 'productId' }}`\n\nfunction processMapSubExpression(\n\texpr: hbs.AST.SubExpression,\n\tctx: AnalysisContext,\n\tparentNode?: hbs.AST.Node,\n): JSONSchema7 {\n\tconst helperName = MapHelpers.MAP_HELPER_NAME;\n\tconst node = parentNode ?? expr;\n\n\t// ── 1. Check argument count ──────────────────────────────────────────\n\tif (expr.params.length < 2) {\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"MISSING_ARGUMENT\",\n\t\t\t\"error\",\n\t\t\t`Helper \"${helperName}\" expects at least 2 argument(s), but got ${expr.params.length}`,\n\t\t\tnode,\n\t\t\t{\n\t\t\t\thelperName,\n\t\t\t\texpected: \"2 argument(s)\",\n\t\t\t\tactual: `${expr.params.length} argument(s)`,\n\t\t\t},\n\t\t);\n\t\treturn { type: \"array\" };\n\t}\n\n\t// ── 2. Resolve the first argument (collection path) ──────────────────\n\tconst collectionExpr = expr.params[0] as hbs.AST.Expression;\n\tconst collectionSchema = resolveExpressionWithDiagnostics(\n\t\tcollectionExpr,\n\t\tctx,\n\t\tnode,\n\t);\n\n\tif (!collectionSchema) {\n\t\treturn { type: \"array\" };\n\t}\n\n\t// ── 3. Validate that the collection is an array ──────────────────────\n\tconst itemSchema = resolveArrayItems(collectionSchema, ctx.root);\n\tif (!itemSchema) {\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"TYPE_MISMATCH\",\n\t\t\t\"error\",\n\t\t\t`Helper \"${helperName}\" parameter \"collection\" expects an array, but got ${schemaTypeLabel(collectionSchema)}`,\n\t\t\tnode,\n\t\t\t{\n\t\t\t\thelperName,\n\t\t\t\texpected: \"array\",\n\t\t\t\tactual: schemaTypeLabel(collectionSchema),\n\t\t\t},\n\t\t);\n\t\treturn { type: \"array\" };\n\t}\n\n\t// ── 4. Validate that the items are objects ───────────────────────────\n\t// If the items are arrays (e.g. from a nested map), flatten one level\n\t// to match the runtime `flat(1)` behavior and use the inner items instead.\n\tlet effectiveItemSchema = itemSchema;\n\tconst itemType = effectiveItemSchema.type;\n\tif (\n\t\titemType === \"array\" ||\n\t\t(Array.isArray(itemType) && itemType.includes(\"array\"))\n\t) {\n\t\tconst innerItems = resolveArrayItems(effectiveItemSchema, ctx.root);\n\t\tif (innerItems) {\n\t\t\teffectiveItemSchema = innerItems;\n\t\t}\n\t}\n\n\tconst effectiveItemType = effectiveItemSchema.type;\n\tconst isObject =\n\t\teffectiveItemType === \"object\" ||\n\t\t(Array.isArray(effectiveItemType) &&\n\t\t\teffectiveItemType.includes(\"object\")) ||\n\t\t(!effectiveItemType && effectiveItemSchema.properties !== undefined);\n\n\tif (!isObject && effectiveItemType !== undefined) {\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"TYPE_MISMATCH\",\n\t\t\t\"error\",\n\t\t\t`Helper \"${helperName}\" expects an array of objects, but the array items have type \"${schemaTypeLabel(effectiveItemSchema)}\"`,\n\t\t\tnode,\n\t\t\t{\n\t\t\t\thelperName,\n\t\t\t\texpected: \"object\",\n\t\t\t\tactual: schemaTypeLabel(effectiveItemSchema),\n\t\t\t},\n\t\t);\n\t\treturn { type: \"array\" };\n\t}\n\n\t// ── 5. Validate the second argument (property name) ──────────────────\n\tconst propertyExpr = expr.params[1] as hbs.AST.Expression;\n\tlet propertyName: string | undefined;\n\n\tif (propertyExpr.type === \"PathExpression\") {\n\t\tconst bare = (propertyExpr as hbs.AST.PathExpression).original;\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"TYPE_MISMATCH\",\n\t\t\t\"error\",\n\t\t\t`Helper \"${helperName}\" parameter \"property\" must be a quoted string. ` +\n\t\t\t\t`Use (${helperName} … \"${bare}\") instead of (${helperName} … ${bare})`,\n\t\t\tnode,\n\t\t\t{\n\t\t\t\thelperName,\n\t\t\t\texpected: 'StringLiteral (e.g. \"property\")',\n\t\t\t\tactual: `PathExpression (${bare})`,\n\t\t\t},\n\t\t);\n\t\treturn { type: \"array\" };\n\t}\n\n\tif (propertyExpr.type === \"StringLiteral\") {\n\t\tpropertyName = (propertyExpr as hbs.AST.StringLiteral).value;\n\t}\n\n\tif (!propertyName) {\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"TYPE_MISMATCH\",\n\t\t\t\"error\",\n\t\t\t`Helper \"${helperName}\" parameter \"property\" expects a quoted string literal, but got ${propertyExpr.type}`,\n\t\t\tnode,\n\t\t\t{\n\t\t\t\thelperName,\n\t\t\t\texpected: 'StringLiteral (e.g. \"property\")',\n\t\t\t\tactual: propertyExpr.type,\n\t\t\t},\n\t\t);\n\t\treturn { type: \"array\" };\n\t}\n\n\t// ── 6. Resolve the property within the item schema ───────────────────\n\tconst propertySchema = resolveSchemaPath(effectiveItemSchema, [propertyName]);\n\tif (!propertySchema) {\n\t\tconst availableProperties = getSchemaPropertyNames(effectiveItemSchema);\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"UNKNOWN_PROPERTY\",\n\t\t\t\"error\",\n\t\t\tcreatePropertyNotFoundMessage(propertyName, availableProperties),\n\t\t\tnode,\n\t\t\t{\n\t\t\t\tpath: propertyName,\n\t\t\t\tavailableProperties,\n\t\t\t},\n\t\t);\n\t\treturn { type: \"array\" };\n\t}\n\n\t// ── 7. Return the inferred output schema ─────────────────────────────\n\treturn { type: \"array\", items: propertySchema };\n}\n\nfunction getBlockArgument(\n\tstmt: hbs.AST.BlockStatement,\n): hbs.AST.Expression | undefined {\n\treturn stmt.params[0] as hbs.AST.Expression | undefined;\n}\n\n/**\n * Retrieves the helper name from a BlockStatement (e.g. \"if\", \"each\", \"with\").\n */\nfunction getBlockHelperName(stmt: hbs.AST.BlockStatement): string {\n\tif (stmt.path.type === \"PathExpression\") {\n\t\treturn (stmt.path as hbs.AST.PathExpression).original;\n\t}\n\treturn \"\";\n}\n\n/**\n * Retrieves the name of an expression (first segment of the PathExpression).\n * Used to identify inline helpers.\n */\nfunction getExpressionName(expr: hbs.AST.Expression): string {\n\tif (expr.type === \"PathExpression\") {\n\t\treturn (expr as hbs.AST.PathExpression).original;\n\t}\n\treturn \"\";\n}\n\n/**\n * Adds an enriched diagnostic to the analysis context.\n *\n * Each diagnostic includes:\n * - A machine-readable `code` for the frontend\n * - A human-readable `message` describing the problem\n * - A `source` snippet from the template (if the position is available)\n * - Structured `details` for debugging\n */\nfunction addDiagnostic(\n\tctx: AnalysisContext,\n\tcode: DiagnosticCode,\n\tseverity: \"error\" | \"warning\",\n\tmessage: string,\n\tnode?: hbs.AST.Node,\n\tdetails?: DiagnosticDetails,\n): void {\n\tconst diagnostic: TemplateDiagnostic = { severity, code, message };\n\n\t// Extract the position and source snippet if available\n\tif (node && \"loc\" in node && node.loc) {\n\t\tdiagnostic.loc = {\n\t\t\tstart: { line: node.loc.start.line, column: node.loc.start.column },\n\t\t\tend: { line: node.loc.end.line, column: node.loc.end.column },\n\t\t};\n\t\t// Extract the template fragment around the error\n\t\tdiagnostic.source = extractSourceSnippet(ctx.template, diagnostic.loc);\n\t}\n\n\tif (details) {\n\t\tdiagnostic.details = details;\n\t}\n\n\tctx.diagnostics.push(diagnostic);\n}\n\n/**\n * Returns a human-readable label for a schema's type (for error messages).\n */\nfunction schemaTypeLabel(schema: JSONSchema7): string {\n\tif (schema.type) {\n\t\treturn Array.isArray(schema.type) ? schema.type.join(\" | \") : schema.type;\n\t}\n\tif (schema.oneOf) return \"oneOf(...)\";\n\tif (schema.anyOf) return \"anyOf(...)\";\n\tif (schema.allOf) return \"allOf(...)\";\n\tif (schema.enum) return \"enum\";\n\treturn \"unknown\";\n}\n\n// ─── Export for Internal Use ─────────────────────────────────────────────────\n// `inferBlockType` is exported to allow targeted unit tests\n// on block type inference.\nexport { inferBlockType };\n"],"names":["dispatchAnalyze","createMissingArgumentMessage","createPropertyNotFoundMessage","createRootPathTraversalMessage","createTypeMismatchMessage","createUnanalyzableMessage","createUnknownHelperMessage","MapHelpers","detectLiteralType","extractExpressionIdentifier","extractPathSegments","getEffectiveBody","getEffectivelySingleBlock","getEffectivelySingleExpression","isRootPathTraversal","isRootSegments","isThisExpression","parse","assertNoConditionalSchema","resolveArrayItems","resolveSchemaPath","simplifySchema","deepEqual","extractSourceSnippet","getSchemaPropertyNames","coerceTextValue","text","targetType","Number","analyze","template","inputSchema","options","tpl","coerceSchema","ast","analyzeFromAst","identifierSchemas","child","childOptions","id","idSchema","Object","entries","ctx","root","current","diagnostics","helpers","outputSchema","inferProgramType","hasErrors","some","d","severity","valid","processStatement","stmt","type","undefined","processMustache","inferBlockType","addDiagnostic","path","params","length","hash","helperName","getExpressionName","MAP_HELPER_NAME","processMapHelper","helper","get","helperParams","requiredCount","filter","p","optional","expected","actual","i","resolvedSchema","resolveExpressionWithDiagnostics","helperParam","expectedType","isParamTypeCompatible","paramName","name","schemaTypeLabel","returnType","collectionExpr","collectionSchema","itemSchema","effectiveItemSchema","itemType","Array","isArray","includes","innerItems","effectiveItemType","isObject","properties","propertyExpr","propertyName","bare","original","value","propertySchema","availableProperties","items","resolved","expectedTypes","resolvedTypes","rt","et","program","effective","singleExpr","singleBlock","allContent","every","s","map","join","trim","coercedType","coercedValue","const","literalType","allBlocks","types","t","push","oneOf","body","getBlockHelperName","arg","getBlockArgument","thenType","inverse","elseType","saved","result","innerSchema","param","expr","parentNode","resolveSubExpression","segments","cleanSegments","identifier","fullPath","resolveRootWithIdentifier","resolveWithIdentifier","node","processMapSubExpression","code","message","details","diagnostic","loc","start","line","column","end","source","schema","anyOf","allOf","enum"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/analyzer.ts"],"sourcesContent":["import type { JSONSchema7 } from \"json-schema\";\nimport { dispatchAnalyze } from \"./dispatch.ts\";\nimport {\n\tcreateMissingArgumentMessage,\n\tcreatePropertyNotFoundMessage,\n\tcreateRootPathTraversalMessage,\n\tcreateTypeMismatchMessage,\n\tcreateUnanalyzableMessage,\n\tcreateUnknownHelperMessage,\n} from \"./errors\";\nimport { MapHelpers } from \"./helpers/map-helpers.ts\";\nimport {\n\tdetectLiteralType,\n\textractExpressionIdentifier,\n\textractPathSegments,\n\tgetEffectiveBody,\n\tgetEffectivelySingleBlock,\n\tgetEffectivelySingleExpression,\n\tisRootPathTraversal,\n\tisRootSegments,\n\tisThisExpression,\n\tparse,\n} from \"./parser\";\nimport {\n\tfindConditionalSchemaLocations,\n\tresolveArrayItems,\n\tresolveSchemaPath,\n\tsimplifySchema,\n} from \"./schema-resolver\";\nimport type {\n\tAnalysisResult,\n\tDiagnosticCode,\n\tDiagnosticDetails,\n\tHelperDefinition,\n\tTemplateDiagnostic,\n\tTemplateInput,\n} from \"./types.ts\";\nimport {\n\tdeepEqual,\n\textractSourceSnippet,\n\tgetSchemaPropertyNames,\n} from \"./utils\";\n\n// ─── Static Analyzer ─────────────────────────────────────────────────────────\n// Static analysis of a Handlebars template against a JSON Schema v7\n// describing the available context.\n//\n// Merged architecture (v2):\n// A single AST traversal performs both **validation** and **return type\n// inference** simultaneously. This eliminates duplication between the former\n// `validate*` and `infer*` functions and improves performance by avoiding\n// a double traversal.\n//\n// Context:\n// The analysis context uses a **save/restore** pattern instead of creating\n// new objects on each recursion (`{ ...ctx, current: X }`). This reduces\n// GC pressure for deeply nested templates.\n//\n// ─── Template Identifiers ────────────────────────────────────────────────────\n// The `{{key:N}}` syntax allows referencing a variable from a specific\n// schema, identified by an integer N. The optional `identifierSchemas`\n// parameter provides a mapping `{ [id]: JSONSchema7 }`.\n//\n// Resolution rules:\n// - `{{meetingId}}` → validated against `inputSchema` (standard behavior)\n// - `{{meetingId:1}}` → validated against `identifierSchemas[1]`\n// - `{{meetingId:1}}` without `identifierSchemas[1]` → error\n\n// ─── Internal Types ──────────────────────────────────────────────────────────\n\n/** Context passed recursively during AST traversal */\ninterface AnalysisContext {\n\t/** Root schema (for resolving $refs) */\n\troot: JSONSchema7;\n\t/** Current context schema (changes with #each, #with) — mutated via save/restore */\n\tcurrent: JSONSchema7;\n\t/** Diagnostics accumulator */\n\tdiagnostics: TemplateDiagnostic[];\n\t/** Full template source (for extracting error snippets) */\n\ttemplate: string;\n\t/** Schemas by template identifier (for the {{key:N}} syntax) */\n\tidentifierSchemas?: Record<number, JSONSchema7>;\n\t/** Registered custom helpers (for static analysis) */\n\thelpers?: Map<string, HelperDefinition>;\n\t/**\n\t * Explicit coercion schema provided by the caller.\n\t * When set, static literal values like `\"123\"` will respect the type\n\t * declared in this schema instead of being auto-detected by\n\t * `detectLiteralType`. Unlike the previous `expectedOutputType`,\n\t * this is NEVER derived from the inputSchema — it must be explicitly\n\t * provided via the `coerceSchema` option.\n\t */\n\tcoerceSchema?: JSONSchema7;\n}\n\n// ─── Public API ──────────────────────────────────────────────────────────────\n\n/** Options for the standalone `analyze()` function */\nexport interface AnalyzeOptions {\n\t/** Schemas by template identifier (for the `{{key:N}}` syntax) */\n\tidentifierSchemas?: Record<number, JSONSchema7>;\n\t/**\n\t * Explicit coercion schema. When provided, static literal values\n\t * will respect the types declared in this schema instead of being\n\t * auto-detected by `detectLiteralType`.\n\t *\n\t * This schema is independent from the `inputSchema` (which describes\n\t * available variables) — it only controls the output type inference\n\t * for static content.\n\t */\n\tcoerceSchema?: JSONSchema7;\n\t/**\n\t * When `true`, properties whose values contain Handlebars expressions\n\t * (i.e. any `{{…}}` syntax) are excluded from the output schema.\n\t *\n\t * Only the properties with static values (literals, plain strings\n\t * without expressions) are retained. This is useful when you want\n\t * the output schema to describe only the known, compile-time-constant\n\t * portion of the template.\n\t *\n\t * This option only has an effect on **object** and **array** templates.\n\t * A root-level string template with expressions is analyzed normally\n\t * (there is no parent property to exclude it from).\n\t *\n\t * @default false\n\t */\n\texcludeTemplateExpression?: boolean;\n}\n\n// ─── Coerce Text Value ──────────────────────────────────────────────────────\n\n/**\n * Parses a raw text string into the appropriate JS primitive according to the\n * target JSON Schema type. Used when `coerceSchema` overrides the default\n * `detectLiteralType` inference for static literal values.\n */\nfunction coerceTextValue(\n\ttext: string,\n\ttargetType: \"string\" | \"number\" | \"integer\" | \"boolean\" | \"null\",\n): string | number | boolean | null | undefined {\n\tswitch (targetType) {\n\t\tcase \"number\":\n\t\tcase \"integer\": {\n\t\t\tif (text === \"\") return undefined;\n\t\t\tconst num = Number(text);\n\t\t\tif (Number.isNaN(num)) return undefined;\n\t\t\tif (targetType === \"integer\" && !Number.isInteger(num)) return undefined;\n\t\t\treturn num;\n\t\t}\n\t\tcase \"boolean\": {\n\t\t\tconst lower = text.toLowerCase();\n\t\t\tif (lower === \"true\") return true;\n\t\t\tif (lower === \"false\") return false;\n\t\t\treturn undefined;\n\t\t}\n\t\tcase \"null\":\n\t\t\treturn null;\n\t\tdefault:\n\t\t\treturn text;\n\t}\n}\n\n/**\n * Statically analyzes a template against a JSON Schema v7 describing the\n * available context.\n *\n * Backward-compatible version — parses the template internally.\n * Uses `dispatchAnalyze` for the recursive array/object/literal dispatching,\n * delegating only the string (template) case to `analyzeFromAst`.\n *\n * @param template - The template string (e.g. `\"Hello {{user.name}}\"`)\n * @param inputSchema - JSON Schema v7 describing the available variables\n * @param options - (optional) Analysis options (identifierSchemas, coerceSchema)\n * @returns An `AnalysisResult` containing validity, diagnostics, and the\n * inferred output schema.\n */\nexport function analyze(\n\ttemplate: TemplateInput,\n\tinputSchema: JSONSchema7 = {},\n\toptions?: AnalyzeOptions,\n): AnalysisResult {\n\treturn dispatchAnalyze(\n\t\ttemplate,\n\t\toptions,\n\t\t// String handler — parse and analyze the AST\n\t\t(tpl, coerceSchema) => {\n\t\t\tconst ast = parse(tpl);\n\t\t\treturn analyzeFromAst(ast, tpl, inputSchema, {\n\t\t\t\tidentifierSchemas: options?.identifierSchemas,\n\t\t\t\tcoerceSchema,\n\t\t\t});\n\t\t},\n\t\t// Recursive handler — re-enter analyze() for child elements\n\t\t(child, childOptions) => analyze(child, inputSchema, childOptions),\n\t);\n}\n\n/**\n * Statically analyzes a template from an already-parsed AST.\n *\n * This is the internal function used by `Typebars.compile()` and\n * `CompiledTemplate.analyze()` to avoid costly re-parsing.\n *\n * @param ast - The already-parsed Handlebars AST\n * @param template - The template source (for error snippets)\n * @param inputSchema - JSON Schema v7 describing the available variables\n * @param options - Additional options\n * @returns An `AnalysisResult`\n */\nexport function analyzeFromAst(\n\tast: hbs.AST.Program,\n\ttemplate: string,\n\tinputSchema: JSONSchema7 = {},\n\toptions?: {\n\t\tidentifierSchemas?: Record<number, JSONSchema7>;\n\t\thelpers?: Map<string, HelperDefinition>;\n\t\t/**\n\t\t * Explicit coercion schema. When set, static literal values will\n\t\t * respect the types declared in this schema instead of auto-detecting.\n\t\t * Unlike `expectedOutputType`, this is NEVER derived from inputSchema.\n\t\t */\n\t\tcoerceSchema?: JSONSchema7;\n\t},\n): AnalysisResult {\n\t// ── Initialize the diagnostic context FIRST ────────────────────────\n\tconst ctx: AnalysisContext = {\n\t\troot: inputSchema,\n\t\tcurrent: inputSchema,\n\t\tdiagnostics: [],\n\t\ttemplate,\n\t\tidentifierSchemas: options?.identifierSchemas,\n\t\thelpers: options?.helpers,\n\t\tcoerceSchema: options?.coerceSchema,\n\t};\n\n\t// ── Detect unsupported schema features as diagnostics ──────────────\n\t// Conditional schemas (if/then/else) are non-resolvable without runtime\n\t// data. Instead of throwing, we collect structured diagnostics so the\n\t// caller receives a standard AnalysisResult.\n\tconst conditionalLocations = findConditionalSchemaLocations(inputSchema);\n\tfor (const loc of conditionalLocations) {\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"UNSUPPORTED_SCHEMA\",\n\t\t\t\"error\",\n\t\t\t`Unsupported JSON Schema feature: \"${loc.keyword}\" at \"${loc.schemaPath}\". ` +\n\t\t\t\t\"Conditional schemas (if/then/else) cannot be resolved during static analysis \" +\n\t\t\t\t\"because they depend on runtime data. Consider using oneOf/anyOf combinators instead.\",\n\t\t\tundefined,\n\t\t\t{ path: loc.schemaPath },\n\t\t);\n\t}\n\n\tif (options?.identifierSchemas) {\n\t\tfor (const [id, idSchema] of Object.entries(options.identifierSchemas)) {\n\t\t\tconst idLocations = findConditionalSchemaLocations(\n\t\t\t\tidSchema,\n\t\t\t\t`/identifierSchemas/${id}`,\n\t\t\t);\n\t\t\tfor (const loc of idLocations) {\n\t\t\t\taddDiagnostic(\n\t\t\t\t\tctx,\n\t\t\t\t\t\"UNSUPPORTED_SCHEMA\",\n\t\t\t\t\t\"error\",\n\t\t\t\t\t`Unsupported JSON Schema feature: \"${loc.keyword}\" at \"${loc.schemaPath}\". ` +\n\t\t\t\t\t\t\"Conditional schemas (if/then/else) cannot be resolved during static analysis \" +\n\t\t\t\t\t\t\"because they depend on runtime data. Consider using oneOf/anyOf combinators instead.\",\n\t\t\t\t\tundefined,\n\t\t\t\t\t{ path: loc.schemaPath },\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t// If unsupported schemas were found, return early with valid: false\n\tif (ctx.diagnostics.length > 0) {\n\t\treturn {\n\t\t\tvalid: false,\n\t\t\tdiagnostics: ctx.diagnostics,\n\t\t\toutputSchema: {},\n\t\t};\n\t}\n\n\t// Single pass: type inference + validation in one traversal.\n\tconst outputSchema = inferProgramType(ast, ctx);\n\n\tconst hasErrors = ctx.diagnostics.some((d) => d.severity === \"error\");\n\n\treturn {\n\t\tvalid: !hasErrors,\n\t\tdiagnostics: ctx.diagnostics,\n\t\toutputSchema: simplifySchema(outputSchema),\n\t};\n}\n\n// ─── Unified AST Traversal ───────────────────────────────────────────────────\n// A single set of functions handles both validation (emitting diagnostics)\n// and type inference (returning a JSONSchema7).\n//\n// Main functions:\n// - `inferProgramType` — entry point for a Program (template body or block)\n// - `processStatement` — dispatches a statement (validation side-effects)\n// - `processMustache` — handles a MustacheStatement (expression or inline helper)\n// - `inferBlockType` — handles a BlockStatement (if, each, with, custom…)\n\n/**\n * Dispatches the processing of an individual statement.\n *\n * Called by `inferProgramType` in the \"mixed template\" case to validate\n * each statement while ignoring the returned type (the result is always\n * `string` for a mixed template).\n *\n * @returns The inferred schema for this statement, or `undefined` for\n * statements with no semantics (ContentStatement, CommentStatement).\n */\nfunction processStatement(\n\tstmt: hbs.AST.Statement,\n\tctx: AnalysisContext,\n): JSONSchema7 | undefined {\n\tswitch (stmt.type) {\n\t\tcase \"ContentStatement\":\n\t\tcase \"CommentStatement\":\n\t\t\t// Static text or comment — nothing to validate, no type to infer\n\t\t\treturn undefined;\n\n\t\tcase \"MustacheStatement\":\n\t\t\treturn processMustache(stmt as hbs.AST.MustacheStatement, ctx);\n\n\t\tcase \"BlockStatement\":\n\t\t\treturn inferBlockType(stmt as hbs.AST.BlockStatement, ctx);\n\n\t\tdefault:\n\t\t\t// Unrecognized AST node — emit a warning rather than an error\n\t\t\t// to avoid blocking on future Handlebars extensions.\n\t\t\taddDiagnostic(\n\t\t\t\tctx,\n\t\t\t\t\"UNANALYZABLE\",\n\t\t\t\t\"warning\",\n\t\t\t\t`Unsupported AST node type: \"${stmt.type}\"`,\n\t\t\t\tstmt,\n\t\t\t);\n\t\t\treturn undefined;\n\t}\n}\n\n/**\n * Processes a MustacheStatement `{{expression}}` or `{{helper arg}}`.\n *\n * Distinguishes two cases:\n * 1. **Simple expression** (`{{name}}`, `{{user.age}}`) — resolution in the schema\n * 2. **Inline helper** (`{{uppercase name}}`) — params > 0 or hash present\n *\n * @returns The inferred schema for this expression\n */\nfunction processMustache(\n\tstmt: hbs.AST.MustacheStatement,\n\tctx: AnalysisContext,\n): JSONSchema7 {\n\t// Sub-expressions (nested helpers) are not supported for static\n\t// analysis — emit a warning.\n\tif (stmt.path.type === \"SubExpression\") {\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"UNANALYZABLE\",\n\t\t\t\"warning\",\n\t\t\t\"Sub-expressions are not statically analyzable\",\n\t\t\tstmt,\n\t\t);\n\t\treturn {};\n\t}\n\n\t// ── Inline helper detection ──────────────────────────────────────────────\n\t// If the MustacheStatement has parameters or a hash, it's a helper call\n\t// (e.g. `{{uppercase name}}`), not a simple expression.\n\tif (stmt.params.length > 0 || stmt.hash) {\n\t\tconst helperName = getExpressionName(stmt.path);\n\n\t\t// ── Special-case: map helper ─────────────────────────────────────\n\t\t// The `map` helper requires deep static analysis that the generic\n\t\t// helper path cannot perform: it must resolve the first argument as\n\t\t// an array-of-objects schema, then resolve the second argument (a\n\t\t// property name) within the item schema to infer the output type\n\t\t// `{ type: \"array\", items: <property schema> }`.\n\t\tif (helperName === MapHelpers.MAP_HELPER_NAME) {\n\t\t\treturn processMapHelper(stmt, ctx);\n\t\t}\n\n\t\t// Check if the helper is registered\n\t\tconst helper = ctx.helpers?.get(helperName);\n\t\tif (helper) {\n\t\t\tconst helperParams = helper.params;\n\n\t\t\t// ── Check the number of required parameters ──────────────\n\t\t\tif (helperParams) {\n\t\t\t\tconst requiredCount = helperParams.filter((p) => !p.optional).length;\n\t\t\t\tif (stmt.params.length < requiredCount) {\n\t\t\t\t\taddDiagnostic(\n\t\t\t\t\t\tctx,\n\t\t\t\t\t\t\"MISSING_ARGUMENT\",\n\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t`Helper \"${helperName}\" expects at least ${requiredCount} argument(s), but got ${stmt.params.length}`,\n\t\t\t\t\t\tstmt,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\thelperName,\n\t\t\t\t\t\t\texpected: `${requiredCount} argument(s)`,\n\t\t\t\t\t\t\tactual: `${stmt.params.length} argument(s)`,\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// ── Validate each parameter (existence + type) ───────────────\n\t\t\tfor (let i = 0; i < stmt.params.length; i++) {\n\t\t\t\tconst resolvedSchema = resolveExpressionWithDiagnostics(\n\t\t\t\t\tstmt.params[i] as hbs.AST.Expression,\n\t\t\t\t\tctx,\n\t\t\t\t\tstmt,\n\t\t\t\t);\n\n\t\t\t\t// Check type compatibility if the helper declares the\n\t\t\t\t// expected type for this parameter\n\t\t\t\tconst helperParam = helperParams?.[i];\n\t\t\t\tif (resolvedSchema && helperParam?.type) {\n\t\t\t\t\tconst expectedType = helperParam.type;\n\t\t\t\t\tif (!isParamTypeCompatible(resolvedSchema, expectedType)) {\n\t\t\t\t\t\tconst paramName = helperParam.name;\n\t\t\t\t\t\taddDiagnostic(\n\t\t\t\t\t\t\tctx,\n\t\t\t\t\t\t\t\"TYPE_MISMATCH\",\n\t\t\t\t\t\t\t\"error\",\n\t\t\t\t\t\t\t`Helper \"${helperName}\" parameter \"${paramName}\" expects ${schemaTypeLabel(expectedType)}, but got ${schemaTypeLabel(resolvedSchema)}`,\n\t\t\t\t\t\t\tstmt,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\thelperName,\n\t\t\t\t\t\t\t\texpected: schemaTypeLabel(expectedType),\n\t\t\t\t\t\t\t\tactual: schemaTypeLabel(resolvedSchema),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn helper.returnType ?? { type: \"string\" };\n\t\t}\n\n\t\t// Unknown inline helper — warning\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"UNKNOWN_HELPER\",\n\t\t\t\"warning\",\n\t\t\t`Unknown inline helper \"${helperName}\" — cannot analyze statically`,\n\t\t\tstmt,\n\t\t\t{ helperName },\n\t\t);\n\t\treturn { type: \"string\" };\n\t}\n\n\t// ── Simple expression ────────────────────────────────────────────────────\n\treturn resolveExpressionWithDiagnostics(stmt.path, ctx, stmt) ?? {};\n}\n\n// ─── map helper — special-case analysis ──────────────────────────────────────\n// Validates the arguments and infers the precise return type:\n// {{ map <arrayPath> <propertyName> }}\n// → { type: \"array\", items: <schema of the property in the item> }\n//\n// Validation rules:\n// 1. Exactly 2 arguments are required\n// 2. The first argument must resolve to an array schema\n// 3. The array items must be an object schema\n// 4. The second argument must be a string literal (property name)\n// 5. The property must exist in the item schema\n\nfunction processMapHelper(\n\tstmt: hbs.AST.MustacheStatement,\n\tctx: AnalysisContext,\n): JSONSchema7 {\n\tconst helperName = MapHelpers.MAP_HELPER_NAME;\n\n\t// ── 1. Check argument count ──────────────────────────────────────────\n\tif (stmt.params.length < 2) {\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"MISSING_ARGUMENT\",\n\t\t\t\"error\",\n\t\t\t`Helper \"${helperName}\" expects at least 2 argument(s), but got ${stmt.params.length}`,\n\t\t\tstmt,\n\t\t\t{\n\t\t\t\thelperName,\n\t\t\t\texpected: \"2 argument(s)\",\n\t\t\t\tactual: `${stmt.params.length} argument(s)`,\n\t\t\t},\n\t\t);\n\t\treturn { type: \"array\" };\n\t}\n\n\t// ── 2. Resolve the first argument (collection path) ──────────────────\n\tconst collectionExpr = stmt.params[0] as hbs.AST.Expression;\n\tconst collectionSchema = resolveExpressionWithDiagnostics(\n\t\tcollectionExpr,\n\t\tctx,\n\t\tstmt,\n\t);\n\n\tif (!collectionSchema) {\n\t\t// Path resolution failed — diagnostic already emitted\n\t\treturn { type: \"array\" };\n\t}\n\n\t// ── 3. Validate that the collection is an array ──────────────────────\n\tconst itemSchema = resolveArrayItems(collectionSchema, ctx.root);\n\tif (!itemSchema) {\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"TYPE_MISMATCH\",\n\t\t\t\"error\",\n\t\t\t`Helper \"${helperName}\" parameter \"collection\" expects an array, but got ${schemaTypeLabel(collectionSchema)}`,\n\t\t\tstmt,\n\t\t\t{\n\t\t\t\thelperName,\n\t\t\t\texpected: \"array\",\n\t\t\t\tactual: schemaTypeLabel(collectionSchema),\n\t\t\t},\n\t\t);\n\t\treturn { type: \"array\" };\n\t}\n\n\t// ── 4. Validate that the items are objects ───────────────────────────\n\t// If the items are arrays (e.g. from a nested map), flatten one level\n\t// to match the runtime `flat(1)` behavior and use the inner items instead.\n\tlet effectiveItemSchema = itemSchema;\n\tconst itemType = effectiveItemSchema.type;\n\tif (\n\t\titemType === \"array\" ||\n\t\t(Array.isArray(itemType) && itemType.includes(\"array\"))\n\t) {\n\t\tconst innerItems = resolveArrayItems(effectiveItemSchema, ctx.root);\n\t\tif (innerItems) {\n\t\t\teffectiveItemSchema = innerItems;\n\t\t}\n\t}\n\n\tconst effectiveItemType = effectiveItemSchema.type;\n\tconst isObject =\n\t\teffectiveItemType === \"object\" ||\n\t\t(Array.isArray(effectiveItemType) &&\n\t\t\teffectiveItemType.includes(\"object\")) ||\n\t\t// If no type but has properties, treat as object\n\t\t(!effectiveItemType && effectiveItemSchema.properties !== undefined);\n\n\tif (!isObject && effectiveItemType !== undefined) {\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"TYPE_MISMATCH\",\n\t\t\t\"error\",\n\t\t\t`Helper \"${helperName}\" expects an array of objects, but the array items have type \"${schemaTypeLabel(effectiveItemSchema)}\"`,\n\t\t\tstmt,\n\t\t\t{\n\t\t\t\thelperName,\n\t\t\t\texpected: \"object\",\n\t\t\t\tactual: schemaTypeLabel(effectiveItemSchema),\n\t\t\t},\n\t\t);\n\t\treturn { type: \"array\" };\n\t}\n\n\t// ── 5. Validate the second argument (property name) ──────────────────\n\tconst propertyExpr = stmt.params[1] as hbs.AST.Expression;\n\n\t// The property name MUST be a StringLiteral (quoted string like `\"name\"`).\n\t// A bare identifier like `name` is parsed by Handlebars as a PathExpression,\n\t// which would be resolved as a data path at runtime — yielding `undefined`\n\t// when the identifier doesn't exist in the top-level context. This is a\n\t// common mistake, so we provide a clear error message guiding the user.\n\tlet propertyName: string | undefined;\n\n\tif (propertyExpr.type === \"PathExpression\") {\n\t\tconst bare = (propertyExpr as hbs.AST.PathExpression).original;\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"TYPE_MISMATCH\",\n\t\t\t\"error\",\n\t\t\t`Helper \"${helperName}\" parameter \"property\" must be a quoted string. ` +\n\t\t\t\t`Use {{ ${helperName} … \"${bare}\" }} instead of {{ ${helperName} … ${bare} }}`,\n\t\t\tstmt,\n\t\t\t{\n\t\t\t\thelperName,\n\t\t\t\texpected: 'StringLiteral (e.g. \"property\")',\n\t\t\t\tactual: `PathExpression (${bare})`,\n\t\t\t},\n\t\t);\n\t\treturn { type: \"array\" };\n\t}\n\n\tif (propertyExpr.type === \"StringLiteral\") {\n\t\tpropertyName = (propertyExpr as hbs.AST.StringLiteral).value;\n\t}\n\n\tif (!propertyName) {\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"TYPE_MISMATCH\",\n\t\t\t\"error\",\n\t\t\t`Helper \"${helperName}\" parameter \"property\" expects a quoted string literal, but got ${propertyExpr.type}`,\n\t\t\tstmt,\n\t\t\t{\n\t\t\t\thelperName,\n\t\t\t\texpected: 'StringLiteral (e.g. \"property\")',\n\t\t\t\tactual: propertyExpr.type,\n\t\t\t},\n\t\t);\n\t\treturn { type: \"array\" };\n\t}\n\n\t// ── 6. Resolve the property within the item schema ───────────────────\n\tconst propertySchema = resolveSchemaPath(effectiveItemSchema, [propertyName]);\n\tif (!propertySchema) {\n\t\tconst availableProperties = getSchemaPropertyNames(effectiveItemSchema);\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"UNKNOWN_PROPERTY\",\n\t\t\t\"error\",\n\t\t\tcreatePropertyNotFoundMessage(propertyName, availableProperties),\n\t\t\tstmt,\n\t\t\t{\n\t\t\t\tpath: propertyName,\n\t\t\t\tavailableProperties,\n\t\t\t},\n\t\t);\n\t\treturn { type: \"array\" };\n\t}\n\n\t// ── 7. Return the inferred output schema ─────────────────────────────\n\treturn { type: \"array\", items: propertySchema };\n}\n\n/**\n * Checks whether a resolved type is compatible with the type expected\n * by a helper parameter.\n *\n * Compatibility rules:\n * - If either schema has no `type`, validation is not possible → compatible\n * - `integer` is compatible with `number` (integer ⊂ number)\n * - For multiple types (e.g. `[\"string\", \"number\"]`), at least one resolved\n * type must match one expected type\n */\nfunction isParamTypeCompatible(\n\tresolved: JSONSchema7,\n\texpected: JSONSchema7,\n): boolean {\n\t// If either has no type info, we cannot validate\n\tif (!expected.type || !resolved.type) return true;\n\n\tconst expectedTypes = Array.isArray(expected.type)\n\t\t? expected.type\n\t\t: [expected.type];\n\tconst resolvedTypes = Array.isArray(resolved.type)\n\t\t? resolved.type\n\t\t: [resolved.type];\n\n\t// At least one resolved type must be compatible with one expected type\n\treturn resolvedTypes.some((rt) =>\n\t\texpectedTypes.some(\n\t\t\t(et) =>\n\t\t\t\trt === et ||\n\t\t\t\t// integer is a subtype of number\n\t\t\t\t(et === \"number\" && rt === \"integer\") ||\n\t\t\t\t(et === \"integer\" && rt === \"number\"),\n\t\t),\n\t);\n}\n\n/**\n * Infers the output type of a `Program` (template body or block body).\n *\n * Handles 4 cases, from most specific to most general:\n *\n * 1. **Single expression** `{{expr}}` → type of the expression\n * 2. **Single block** `{{#if}}…{{/if}}` → type of the block\n * 3. **Pure text content** → literal detection (number, boolean, null)\n * 4. **Mixed template** → always `string` (concatenation)\n *\n * Validation is performed alongside inference: each expression and block\n * is validated during processing.\n */\nfunction inferProgramType(\n\tprogram: hbs.AST.Program,\n\tctx: AnalysisContext,\n): JSONSchema7 {\n\tconst effective = getEffectiveBody(program);\n\n\t// No significant statements → empty string\n\tif (effective.length === 0) {\n\t\treturn { type: \"string\" };\n\t}\n\n\t// ── Case 1: single expression {{expr}} ─────────────────────────────────\n\tconst singleExpr = getEffectivelySingleExpression(program);\n\tif (singleExpr) {\n\t\treturn processMustache(singleExpr, ctx);\n\t}\n\n\t// ── Case 2: single block {{#if}}, {{#each}}, {{#with}}, … ──────────────\n\tconst singleBlock = getEffectivelySingleBlock(program);\n\tif (singleBlock) {\n\t\treturn inferBlockType(singleBlock, ctx);\n\t}\n\n\t// ── Case 3: only ContentStatements (no expressions) ────────────────────\n\t// If the concatenated (trimmed) text is a typed literal (number, boolean,\n\t// null), we infer the corresponding type.\n\tconst allContent = effective.every((s) => s.type === \"ContentStatement\");\n\tif (allContent) {\n\t\tconst text = effective\n\t\t\t.map((s) => (s as hbs.AST.ContentStatement).value)\n\t\t\t.join(\"\")\n\t\t\t.trim();\n\n\t\tif (text === \"\") return { type: \"string\" };\n\n\t\t// If an explicit coerceSchema was provided and declares a specific\n\t\t// primitive type, respect it instead of auto-detecting. For example,\n\t\t// \"123\" with coerceSchema `{ type: \"string\" }` should stay \"string\".\n\t\t// This only applies when coerceSchema is explicitly set — the\n\t\t// inputSchema is NEVER used for coercion.\n\t\t//\n\t\t// Only the **type** is extracted from coerceSchema. Value-level\n\t\t// constraints (enum, const, format, pattern, minLength, …) are NOT\n\t\t// propagated because they describe what the *consumer* accepts, not\n\t\t// what the literal *produces*. The actual literal value is set as\n\t\t// `const` so downstream compatibility checkers can detect mismatches.\n\t\tconst coercedType = ctx.coerceSchema?.type;\n\t\tif (\n\t\t\ttypeof coercedType === \"string\" &&\n\t\t\t(coercedType === \"string\" ||\n\t\t\t\tcoercedType === \"number\" ||\n\t\t\t\tcoercedType === \"integer\" ||\n\t\t\t\tcoercedType === \"boolean\" ||\n\t\t\t\tcoercedType === \"null\")\n\t\t) {\n\t\t\tconst coercedValue = coerceTextValue(text, coercedType);\n\t\t\treturn { type: coercedType, const: coercedValue } as JSONSchema7;\n\t\t}\n\n\t\tconst literalType = detectLiteralType(text);\n\t\tif (literalType) return { type: literalType };\n\t}\n\n\t// ── Case 4: multiple blocks only (no significant text between them) ────\n\t// When the effective body consists entirely of BlockStatements, gather\n\t// each block's inferred type and combine them via oneOf. This handles\n\t// templates like:\n\t// {{#if showName}}{{name}}{{/if}}\n\t// {{#if showAge}}{{age}}{{/if}}\n\t// where the output could be string OR number depending on which branch\n\t// is active.\n\tconst allBlocks = effective.every((s) => s.type === \"BlockStatement\");\n\tif (allBlocks) {\n\t\tconst types: JSONSchema7[] = [];\n\t\tfor (const stmt of effective) {\n\t\t\tconst t = inferBlockType(stmt as hbs.AST.BlockStatement, ctx);\n\t\t\tif (t) types.push(t);\n\t\t}\n\t\tif (types.length === 1) return types[0] as JSONSchema7;\n\t\tif (types.length > 1) return simplifySchema({ oneOf: types });\n\t\treturn { type: \"string\" };\n\t}\n\n\t// ── Case 5: mixed template (text + expressions, blocks…) ───────────────\n\t// Traverse all statements for validation (side-effects: diagnostics).\n\t// The result is always string (concatenation).\n\tfor (const stmt of program.body) {\n\t\tprocessStatement(stmt, ctx);\n\t}\n\treturn { type: \"string\" };\n}\n\n/**\n * Infers the output type of a BlockStatement and validates its content.\n *\n * Supports built-in helpers (`if`, `unless`, `each`, `with`) and custom\n * helpers registered via `Typebars.registerHelper()`.\n *\n * Uses the **save/restore** pattern for context: instead of creating a new\n * object `{ ...ctx, current: X }` on each recursion, we save `ctx.current`,\n * mutate it, process the body, then restore. This reduces GC pressure for\n * deeply nested templates.\n */\nfunction inferBlockType(\n\tstmt: hbs.AST.BlockStatement,\n\tctx: AnalysisContext,\n): JSONSchema7 {\n\tconst helperName = getBlockHelperName(stmt);\n\n\tswitch (helperName) {\n\t\t// ── if / unless ──────────────────────────────────────────────────────\n\t\t// Validate the condition argument, then infer types from both branches.\n\t\tcase \"if\":\n\t\tcase \"unless\": {\n\t\t\tconst arg = getBlockArgument(stmt);\n\t\t\tif (arg) {\n\t\t\t\tresolveExpressionWithDiagnostics(arg, ctx, stmt);\n\t\t\t} else {\n\t\t\t\taddDiagnostic(\n\t\t\t\t\tctx,\n\t\t\t\t\t\"MISSING_ARGUMENT\",\n\t\t\t\t\t\"error\",\n\t\t\t\t\tcreateMissingArgumentMessage(helperName),\n\t\t\t\t\tstmt,\n\t\t\t\t\t{ helperName },\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Infer the type of the \"then\" branch\n\t\t\tconst thenType = inferProgramType(stmt.program, ctx);\n\n\t\t\tif (stmt.inverse) {\n\t\t\t\tconst elseType = inferProgramType(stmt.inverse, ctx);\n\t\t\t\t// If both branches have the same type → single type\n\t\t\t\tif (deepEqual(thenType, elseType)) return thenType;\n\t\t\t\t// Otherwise → union of both types\n\t\t\t\treturn simplifySchema({ oneOf: [thenType, elseType] });\n\t\t\t}\n\n\t\t\t// No else branch → the result is the type of the then branch\n\t\t\t// (conceptually optional, but Handlebars returns \"\" for falsy)\n\t\t\treturn thenType;\n\t\t}\n\n\t\t// ── each ─────────────────────────────────────────────────────────────\n\t\t// Resolve the collection schema, then validate the body with the item\n\t\t// schema as the new context.\n\t\tcase \"each\": {\n\t\t\tconst arg = getBlockArgument(stmt);\n\t\t\tif (!arg) {\n\t\t\t\taddDiagnostic(\n\t\t\t\t\tctx,\n\t\t\t\t\t\"MISSING_ARGUMENT\",\n\t\t\t\t\t\"error\",\n\t\t\t\t\tcreateMissingArgumentMessage(\"each\"),\n\t\t\t\t\tstmt,\n\t\t\t\t\t{ helperName: \"each\" },\n\t\t\t\t);\n\t\t\t\t// Validate the body with an empty context (best-effort)\n\t\t\t\tconst saved = ctx.current;\n\t\t\t\tctx.current = {};\n\t\t\t\tinferProgramType(stmt.program, ctx);\n\t\t\t\tctx.current = saved;\n\t\t\t\tif (stmt.inverse) inferProgramType(stmt.inverse, ctx);\n\t\t\t\treturn { type: \"string\" };\n\t\t\t}\n\n\t\t\tconst collectionSchema = resolveExpressionWithDiagnostics(arg, ctx, stmt);\n\t\t\tif (!collectionSchema) {\n\t\t\t\t// The path could not be resolved — diagnostic already emitted.\n\t\t\t\tconst saved = ctx.current;\n\t\t\t\tctx.current = {};\n\t\t\t\tinferProgramType(stmt.program, ctx);\n\t\t\t\tctx.current = saved;\n\t\t\t\tif (stmt.inverse) inferProgramType(stmt.inverse, ctx);\n\t\t\t\treturn { type: \"string\" };\n\t\t\t}\n\n\t\t\t// Resolve the schema of the array elements\n\t\t\tconst itemSchema = resolveArrayItems(collectionSchema, ctx.root);\n\t\t\tif (!itemSchema) {\n\t\t\t\taddDiagnostic(\n\t\t\t\t\tctx,\n\t\t\t\t\t\"TYPE_MISMATCH\",\n\t\t\t\t\t\"error\",\n\t\t\t\t\tcreateTypeMismatchMessage(\n\t\t\t\t\t\t\"each\",\n\t\t\t\t\t\t\"an array\",\n\t\t\t\t\t\tschemaTypeLabel(collectionSchema),\n\t\t\t\t\t),\n\t\t\t\t\tstmt,\n\t\t\t\t\t{\n\t\t\t\t\t\thelperName: \"each\",\n\t\t\t\t\t\texpected: \"array\",\n\t\t\t\t\t\tactual: schemaTypeLabel(collectionSchema),\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t\t// Validate the body with an empty context (best-effort)\n\t\t\t\tconst saved = ctx.current;\n\t\t\t\tctx.current = {};\n\t\t\t\tinferProgramType(stmt.program, ctx);\n\t\t\t\tctx.current = saved;\n\t\t\t\tif (stmt.inverse) inferProgramType(stmt.inverse, ctx);\n\t\t\t\treturn { type: \"string\" };\n\t\t\t}\n\n\t\t\t// Validate the body with the item schema as the new context\n\t\t\tconst saved = ctx.current;\n\t\t\tctx.current = itemSchema;\n\t\t\tinferProgramType(stmt.program, ctx);\n\t\t\tctx.current = saved;\n\n\t\t\t// The inverse branch ({{else}}) keeps the parent context\n\t\t\tif (stmt.inverse) inferProgramType(stmt.inverse, ctx);\n\n\t\t\t// An each concatenates renders → always string\n\t\t\treturn { type: \"string\" };\n\t\t}\n\n\t\t// ── with ─────────────────────────────────────────────────────────────\n\t\t// Resolve the inner schema, then validate the body with it as the\n\t\t// new context.\n\t\tcase \"with\": {\n\t\t\tconst arg = getBlockArgument(stmt);\n\t\t\tif (!arg) {\n\t\t\t\taddDiagnostic(\n\t\t\t\t\tctx,\n\t\t\t\t\t\"MISSING_ARGUMENT\",\n\t\t\t\t\t\"error\",\n\t\t\t\t\tcreateMissingArgumentMessage(\"with\"),\n\t\t\t\t\tstmt,\n\t\t\t\t\t{ helperName: \"with\" },\n\t\t\t\t);\n\t\t\t\t// Validate the body with an empty context\n\t\t\t\tconst saved = ctx.current;\n\t\t\t\tctx.current = {};\n\t\t\t\tconst result = inferProgramType(stmt.program, ctx);\n\t\t\t\tctx.current = saved;\n\t\t\t\tif (stmt.inverse) inferProgramType(stmt.inverse, ctx);\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tconst innerSchema = resolveExpressionWithDiagnostics(arg, ctx, stmt);\n\n\t\t\tconst saved = ctx.current;\n\t\t\tctx.current = innerSchema ?? {};\n\t\t\tconst result = inferProgramType(stmt.program, ctx);\n\t\t\tctx.current = saved;\n\n\t\t\t// The inverse branch keeps the parent context\n\t\t\tif (stmt.inverse) inferProgramType(stmt.inverse, ctx);\n\n\t\t\treturn result;\n\t\t}\n\n\t\t// ── Custom or unknown helper ─────────────────────────────────────────\n\t\tdefault: {\n\t\t\tconst helper = ctx.helpers?.get(helperName);\n\t\t\tif (helper) {\n\t\t\t\t// Registered custom helper — validate parameters\n\t\t\t\tfor (const param of stmt.params) {\n\t\t\t\t\tresolveExpressionWithDiagnostics(\n\t\t\t\t\t\tparam as hbs.AST.Expression,\n\t\t\t\t\t\tctx,\n\t\t\t\t\t\tstmt,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\t// Validate the body with the current context\n\t\t\t\tinferProgramType(stmt.program, ctx);\n\t\t\t\tif (stmt.inverse) inferProgramType(stmt.inverse, ctx);\n\t\t\t\treturn helper.returnType ?? { type: \"string\" };\n\t\t\t}\n\n\t\t\t// Unknown helper — warning\n\t\t\taddDiagnostic(\n\t\t\t\tctx,\n\t\t\t\t\"UNKNOWN_HELPER\",\n\t\t\t\t\"warning\",\n\t\t\t\tcreateUnknownHelperMessage(helperName),\n\t\t\t\tstmt,\n\t\t\t\t{ helperName },\n\t\t\t);\n\t\t\t// Still validate the body with the current context (best-effort)\n\t\t\tinferProgramType(stmt.program, ctx);\n\t\t\tif (stmt.inverse) inferProgramType(stmt.inverse, ctx);\n\t\t\treturn { type: \"string\" };\n\t\t}\n\t}\n}\n\n// ─── Expression Resolution ───────────────────────────────────────────────────\n\n/**\n * Resolves an AST expression to a sub-schema, emitting a diagnostic\n * if the path cannot be resolved.\n *\n * Handles the `{{key:N}}` syntax:\n * - If the expression has an identifier N → resolution in `identifierSchemas[N]`\n * - If identifier N has no associated schema → error\n * - If no identifier → resolution in `ctx.current` (standard behavior)\n *\n * @returns The resolved sub-schema, or `undefined` if the path is invalid.\n */\nfunction resolveExpressionWithDiagnostics(\n\texpr: hbs.AST.Expression,\n\tctx: AnalysisContext,\n\t/** Parent AST node (for diagnostic location) */\n\tparentNode?: hbs.AST.Node,\n): JSONSchema7 | undefined {\n\t// Handle `this` / `.` → return the current context\n\tif (isThisExpression(expr)) {\n\t\treturn ctx.current;\n\t}\n\n\t// ── SubExpression (nested helper call, e.g. `(lt account.balance 500)`) ──\n\tif (expr.type === \"SubExpression\") {\n\t\treturn resolveSubExpression(expr as hbs.AST.SubExpression, ctx, parentNode);\n\t}\n\n\tconst segments = extractPathSegments(expr);\n\tif (segments.length === 0) {\n\t\t// Expression that is not a PathExpression (e.g. literal)\n\t\tif (expr.type === \"StringLiteral\") return { type: \"string\" };\n\t\tif (expr.type === \"NumberLiteral\") return { type: \"number\" };\n\t\tif (expr.type === \"BooleanLiteral\") return { type: \"boolean\" };\n\t\tif (expr.type === \"NullLiteral\") return { type: \"null\" };\n\t\tif (expr.type === \"UndefinedLiteral\") return {};\n\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"UNANALYZABLE\",\n\t\t\t\"warning\",\n\t\t\tcreateUnanalyzableMessage(expr.type),\n\t\t\tparentNode ?? expr,\n\t\t);\n\t\treturn undefined;\n\t}\n\n\t// ── Identifier extraction ──────────────────────────────────────────────\n\t// Extract the `:N` suffix BEFORE checking for `$root` so that both\n\t// `{{$root}}` and `{{$root:2}}` are handled uniformly.\n\tconst { cleanSegments, identifier } = extractExpressionIdentifier(segments);\n\n\t// ── $root token ──────────────────────────────────────────────────────\n\t// Path traversal ($root.name, $root.address.city) is always forbidden,\n\t// regardless of whether an identifier is present.\n\tif (isRootPathTraversal(cleanSegments)) {\n\t\tconst fullPath = cleanSegments.join(\".\");\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"ROOT_PATH_TRAVERSAL\",\n\t\t\t\"error\",\n\t\t\tcreateRootPathTraversalMessage(fullPath),\n\t\t\tparentNode ?? expr,\n\t\t\t{ path: fullPath },\n\t\t);\n\t\treturn undefined;\n\t}\n\n\t// `{{$root}}` → return the entire current context schema\n\t// `{{$root:N}}` → return the entire schema for identifier N\n\tif (isRootSegments(cleanSegments)) {\n\t\tif (identifier !== null) {\n\t\t\treturn resolveRootWithIdentifier(identifier, ctx, parentNode ?? expr);\n\t\t}\n\t\treturn ctx.current;\n\t}\n\n\tif (identifier !== null) {\n\t\t// The expression uses the {{key:N}} syntax — resolve from\n\t\t// the schema of identifier N.\n\t\treturn resolveWithIdentifier(\n\t\t\tcleanSegments,\n\t\t\tidentifier,\n\t\t\tctx,\n\t\t\tparentNode ?? expr,\n\t\t);\n\t}\n\n\t// ── Standard resolution (no identifier) ────────────────────────────────\n\tconst resolved = resolveSchemaPath(ctx.current, cleanSegments);\n\tif (resolved === undefined) {\n\t\tconst fullPath = cleanSegments.join(\".\");\n\t\tconst availableProperties = getSchemaPropertyNames(ctx.current);\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"UNKNOWN_PROPERTY\",\n\t\t\t\"error\",\n\t\t\tcreatePropertyNotFoundMessage(fullPath, availableProperties),\n\t\t\tparentNode ?? expr,\n\t\t\t{ path: fullPath, availableProperties },\n\t\t);\n\t\treturn undefined;\n\t}\n\n\treturn resolved;\n}\n\n/**\n * Resolves `{{$root:N}}` — returns the **entire** schema for identifier N.\n *\n * This is the identifier-aware counterpart of returning `ctx.current` for\n * a plain `{{$root}}`. Instead of navigating into properties, it returns\n * the identifier's root schema directly.\n *\n * Emits an error diagnostic if:\n * - No `identifierSchemas` were provided\n * - Identifier N has no associated schema\n */\nfunction resolveRootWithIdentifier(\n\tidentifier: number,\n\tctx: AnalysisContext,\n\tnode: hbs.AST.Node,\n): JSONSchema7 | undefined {\n\t// No identifierSchemas provided at all\n\tif (!ctx.identifierSchemas) {\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"MISSING_IDENTIFIER_SCHEMAS\",\n\t\t\t\"error\",\n\t\t\t`Property \"$root:${identifier}\" uses an identifier but no identifier schemas were provided`,\n\t\t\tnode,\n\t\t\t{ path: `$root:${identifier}`, identifier },\n\t\t);\n\t\treturn undefined;\n\t}\n\n\t// The identifier does not exist in the provided schemas\n\tconst idSchema = ctx.identifierSchemas[identifier];\n\tif (!idSchema) {\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"UNKNOWN_IDENTIFIER\",\n\t\t\t\"error\",\n\t\t\t`Property \"$root:${identifier}\" references identifier ${identifier} but no schema exists for this identifier`,\n\t\t\tnode,\n\t\t\t{ path: `$root:${identifier}`, identifier },\n\t\t);\n\t\treturn undefined;\n\t}\n\n\t// Return the entire schema for identifier N\n\treturn idSchema;\n}\n\n/**\n * Resolves an expression with identifier `{{key:N}}` by looking up the\n * schema associated with identifier N.\n *\n * Emits an error diagnostic if:\n * - No `identifierSchemas` were provided\n * - Identifier N has no associated schema\n * - The property does not exist in the identifier's schema\n */\nfunction resolveWithIdentifier(\n\tcleanSegments: string[],\n\tidentifier: number,\n\tctx: AnalysisContext,\n\tnode: hbs.AST.Node,\n): JSONSchema7 | undefined {\n\tconst fullPath = cleanSegments.join(\".\");\n\n\t// No identifierSchemas provided at all\n\tif (!ctx.identifierSchemas) {\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"MISSING_IDENTIFIER_SCHEMAS\",\n\t\t\t\"error\",\n\t\t\t`Property \"${fullPath}:${identifier}\" uses an identifier but no identifier schemas were provided`,\n\t\t\tnode,\n\t\t\t{ path: `${fullPath}:${identifier}`, identifier },\n\t\t);\n\t\treturn undefined;\n\t}\n\n\t// The identifier does not exist in the provided schemas\n\tconst idSchema = ctx.identifierSchemas[identifier];\n\tif (!idSchema) {\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"UNKNOWN_IDENTIFIER\",\n\t\t\t\"error\",\n\t\t\t`Property \"${fullPath}:${identifier}\" references identifier ${identifier} but no schema exists for this identifier`,\n\t\t\tnode,\n\t\t\t{ path: `${fullPath}:${identifier}`, identifier },\n\t\t);\n\t\treturn undefined;\n\t}\n\n\t// Resolve the path within the identifier's schema\n\tconst resolved = resolveSchemaPath(idSchema, cleanSegments);\n\tif (resolved === undefined) {\n\t\tconst availableProperties = getSchemaPropertyNames(idSchema);\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"IDENTIFIER_PROPERTY_NOT_FOUND\",\n\t\t\t\"error\",\n\t\t\t`Property \"${fullPath}\" does not exist in the schema for identifier ${identifier}`,\n\t\t\tnode,\n\t\t\t{\n\t\t\t\tpath: fullPath,\n\t\t\t\tidentifier,\n\t\t\t\tavailableProperties,\n\t\t\t},\n\t\t);\n\t\treturn undefined;\n\t}\n\n\treturn resolved;\n}\n\n// ─── Utilities ───────────────────────────────────────────────────────────────\n\n/**\n * Extracts the first argument of a BlockStatement.\n *\n * In the Handlebars AST, for `{{#if active}}`:\n * - `stmt.path` → PathExpression(\"if\") ← the helper name\n * - `stmt.params[0]` → PathExpression(\"active\") ← the actual argument\n *\n * @returns The argument expression, or `undefined` if the block has no argument.\n */\n// ─── SubExpression Resolution ────────────────────────────────────────────────\n\n/**\n * Resolves a SubExpression (nested helper call) such as `(lt account.balance 500)`.\n *\n * This mirrors the helper-call logic in `processMustache` but applies to\n * expressions used as arguments (e.g. inside `{{#if (lt a b)}}`).\n *\n * Steps:\n * 1. Extract the helper name from the SubExpression's path.\n * 2. Look up the helper in `ctx.helpers`.\n * 3. Validate argument count and types.\n * 4. Return the helper's declared `returnType` (defaults to `{ type: \"string\" }`).\n */\nfunction resolveSubExpression(\n\texpr: hbs.AST.SubExpression,\n\tctx: AnalysisContext,\n\tparentNode?: hbs.AST.Node,\n): JSONSchema7 | undefined {\n\tconst helperName = getExpressionName(expr.path);\n\n\t// ── Special-case: map helper ─────────────────────────────────────\n\t// The `map` helper requires deep static analysis to infer the\n\t// precise return type `{ type: \"array\", items: <property schema> }`.\n\t// The generic path would only return `{ type: \"array\" }` (the static\n\t// returnType), losing the item schema needed by nested map calls.\n\tif (helperName === MapHelpers.MAP_HELPER_NAME) {\n\t\treturn processMapSubExpression(expr, ctx, parentNode);\n\t}\n\n\tconst helper = ctx.helpers?.get(helperName);\n\tif (!helper) {\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"UNKNOWN_HELPER\",\n\t\t\t\"warning\",\n\t\t\t`Unknown sub-expression helper \"${helperName}\" — cannot analyze statically`,\n\t\t\tparentNode ?? expr,\n\t\t\t{ helperName },\n\t\t);\n\t\treturn { type: \"string\" };\n\t}\n\n\tconst helperParams = helper.params;\n\n\t// ── Check the number of required parameters ──────────────────────\n\tif (helperParams) {\n\t\tconst requiredCount = helperParams.filter((p) => !p.optional).length;\n\t\tif (expr.params.length < requiredCount) {\n\t\t\taddDiagnostic(\n\t\t\t\tctx,\n\t\t\t\t\"MISSING_ARGUMENT\",\n\t\t\t\t\"error\",\n\t\t\t\t`Helper \"${helperName}\" expects at least ${requiredCount} argument(s), but got ${expr.params.length}`,\n\t\t\t\tparentNode ?? expr,\n\t\t\t\t{\n\t\t\t\t\thelperName,\n\t\t\t\t\texpected: `${requiredCount} argument(s)`,\n\t\t\t\t\tactual: `${expr.params.length} argument(s)`,\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t}\n\n\t// ── Validate each parameter (existence + type) ───────────────────\n\tfor (let i = 0; i < expr.params.length; i++) {\n\t\tconst resolvedSchema = resolveExpressionWithDiagnostics(\n\t\t\texpr.params[i] as hbs.AST.Expression,\n\t\t\tctx,\n\t\t\tparentNode ?? expr,\n\t\t);\n\n\t\tconst helperParam = helperParams?.[i];\n\t\tif (resolvedSchema && helperParam?.type) {\n\t\t\tconst expectedType = helperParam.type;\n\t\t\tif (!isParamTypeCompatible(resolvedSchema, expectedType)) {\n\t\t\t\tconst paramName = helperParam.name;\n\t\t\t\taddDiagnostic(\n\t\t\t\t\tctx,\n\t\t\t\t\t\"TYPE_MISMATCH\",\n\t\t\t\t\t\"error\",\n\t\t\t\t\t`Helper \"${helperName}\" parameter \"${paramName}\" expects ${schemaTypeLabel(expectedType)}, but got ${schemaTypeLabel(resolvedSchema)}`,\n\t\t\t\t\tparentNode ?? expr,\n\t\t\t\t\t{\n\t\t\t\t\t\thelperName,\n\t\t\t\t\t\texpected: schemaTypeLabel(expectedType),\n\t\t\t\t\t\tactual: schemaTypeLabel(resolvedSchema),\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn helper.returnType ?? { type: \"string\" };\n}\n\n// ─── map helper — sub-expression analysis ────────────────────────────────────\n// Mirrors processMapHelper but for SubExpression nodes (e.g.\n// `(map users 'cartItems')` used as an argument to another helper).\n// This enables nested map: `{{ map (map users 'cartItems') 'productId' }}`\n\nfunction processMapSubExpression(\n\texpr: hbs.AST.SubExpression,\n\tctx: AnalysisContext,\n\tparentNode?: hbs.AST.Node,\n): JSONSchema7 {\n\tconst helperName = MapHelpers.MAP_HELPER_NAME;\n\tconst node = parentNode ?? expr;\n\n\t// ── 1. Check argument count ──────────────────────────────────────────\n\tif (expr.params.length < 2) {\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"MISSING_ARGUMENT\",\n\t\t\t\"error\",\n\t\t\t`Helper \"${helperName}\" expects at least 2 argument(s), but got ${expr.params.length}`,\n\t\t\tnode,\n\t\t\t{\n\t\t\t\thelperName,\n\t\t\t\texpected: \"2 argument(s)\",\n\t\t\t\tactual: `${expr.params.length} argument(s)`,\n\t\t\t},\n\t\t);\n\t\treturn { type: \"array\" };\n\t}\n\n\t// ── 2. Resolve the first argument (collection path) ──────────────────\n\tconst collectionExpr = expr.params[0] as hbs.AST.Expression;\n\tconst collectionSchema = resolveExpressionWithDiagnostics(\n\t\tcollectionExpr,\n\t\tctx,\n\t\tnode,\n\t);\n\n\tif (!collectionSchema) {\n\t\treturn { type: \"array\" };\n\t}\n\n\t// ── 3. Validate that the collection is an array ──────────────────────\n\tconst itemSchema = resolveArrayItems(collectionSchema, ctx.root);\n\tif (!itemSchema) {\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"TYPE_MISMATCH\",\n\t\t\t\"error\",\n\t\t\t`Helper \"${helperName}\" parameter \"collection\" expects an array, but got ${schemaTypeLabel(collectionSchema)}`,\n\t\t\tnode,\n\t\t\t{\n\t\t\t\thelperName,\n\t\t\t\texpected: \"array\",\n\t\t\t\tactual: schemaTypeLabel(collectionSchema),\n\t\t\t},\n\t\t);\n\t\treturn { type: \"array\" };\n\t}\n\n\t// ── 4. Validate that the items are objects ───────────────────────────\n\t// If the items are arrays (e.g. from a nested map), flatten one level\n\t// to match the runtime `flat(1)` behavior and use the inner items instead.\n\tlet effectiveItemSchema = itemSchema;\n\tconst itemType = effectiveItemSchema.type;\n\tif (\n\t\titemType === \"array\" ||\n\t\t(Array.isArray(itemType) && itemType.includes(\"array\"))\n\t) {\n\t\tconst innerItems = resolveArrayItems(effectiveItemSchema, ctx.root);\n\t\tif (innerItems) {\n\t\t\teffectiveItemSchema = innerItems;\n\t\t}\n\t}\n\n\tconst effectiveItemType = effectiveItemSchema.type;\n\tconst isObject =\n\t\teffectiveItemType === \"object\" ||\n\t\t(Array.isArray(effectiveItemType) &&\n\t\t\teffectiveItemType.includes(\"object\")) ||\n\t\t(!effectiveItemType && effectiveItemSchema.properties !== undefined);\n\n\tif (!isObject && effectiveItemType !== undefined) {\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"TYPE_MISMATCH\",\n\t\t\t\"error\",\n\t\t\t`Helper \"${helperName}\" expects an array of objects, but the array items have type \"${schemaTypeLabel(effectiveItemSchema)}\"`,\n\t\t\tnode,\n\t\t\t{\n\t\t\t\thelperName,\n\t\t\t\texpected: \"object\",\n\t\t\t\tactual: schemaTypeLabel(effectiveItemSchema),\n\t\t\t},\n\t\t);\n\t\treturn { type: \"array\" };\n\t}\n\n\t// ── 5. Validate the second argument (property name) ──────────────────\n\tconst propertyExpr = expr.params[1] as hbs.AST.Expression;\n\tlet propertyName: string | undefined;\n\n\tif (propertyExpr.type === \"PathExpression\") {\n\t\tconst bare = (propertyExpr as hbs.AST.PathExpression).original;\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"TYPE_MISMATCH\",\n\t\t\t\"error\",\n\t\t\t`Helper \"${helperName}\" parameter \"property\" must be a quoted string. ` +\n\t\t\t\t`Use (${helperName} … \"${bare}\") instead of (${helperName} … ${bare})`,\n\t\t\tnode,\n\t\t\t{\n\t\t\t\thelperName,\n\t\t\t\texpected: 'StringLiteral (e.g. \"property\")',\n\t\t\t\tactual: `PathExpression (${bare})`,\n\t\t\t},\n\t\t);\n\t\treturn { type: \"array\" };\n\t}\n\n\tif (propertyExpr.type === \"StringLiteral\") {\n\t\tpropertyName = (propertyExpr as hbs.AST.StringLiteral).value;\n\t}\n\n\tif (!propertyName) {\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"TYPE_MISMATCH\",\n\t\t\t\"error\",\n\t\t\t`Helper \"${helperName}\" parameter \"property\" expects a quoted string literal, but got ${propertyExpr.type}`,\n\t\t\tnode,\n\t\t\t{\n\t\t\t\thelperName,\n\t\t\t\texpected: 'StringLiteral (e.g. \"property\")',\n\t\t\t\tactual: propertyExpr.type,\n\t\t\t},\n\t\t);\n\t\treturn { type: \"array\" };\n\t}\n\n\t// ── 6. Resolve the property within the item schema ───────────────────\n\tconst propertySchema = resolveSchemaPath(effectiveItemSchema, [propertyName]);\n\tif (!propertySchema) {\n\t\tconst availableProperties = getSchemaPropertyNames(effectiveItemSchema);\n\t\taddDiagnostic(\n\t\t\tctx,\n\t\t\t\"UNKNOWN_PROPERTY\",\n\t\t\t\"error\",\n\t\t\tcreatePropertyNotFoundMessage(propertyName, availableProperties),\n\t\t\tnode,\n\t\t\t{\n\t\t\t\tpath: propertyName,\n\t\t\t\tavailableProperties,\n\t\t\t},\n\t\t);\n\t\treturn { type: \"array\" };\n\t}\n\n\t// ── 7. Return the inferred output schema ─────────────────────────────\n\treturn { type: \"array\", items: propertySchema };\n}\n\nfunction getBlockArgument(\n\tstmt: hbs.AST.BlockStatement,\n): hbs.AST.Expression | undefined {\n\treturn stmt.params[0] as hbs.AST.Expression | undefined;\n}\n\n/**\n * Retrieves the helper name from a BlockStatement (e.g. \"if\", \"each\", \"with\").\n */\nfunction getBlockHelperName(stmt: hbs.AST.BlockStatement): string {\n\tif (stmt.path.type === \"PathExpression\") {\n\t\treturn (stmt.path as hbs.AST.PathExpression).original;\n\t}\n\treturn \"\";\n}\n\n/**\n * Retrieves the name of an expression (first segment of the PathExpression).\n * Used to identify inline helpers.\n */\nfunction getExpressionName(expr: hbs.AST.Expression): string {\n\tif (expr.type === \"PathExpression\") {\n\t\treturn (expr as hbs.AST.PathExpression).original;\n\t}\n\treturn \"\";\n}\n\n/**\n * Adds an enriched diagnostic to the analysis context.\n *\n * Each diagnostic includes:\n * - A machine-readable `code` for the frontend\n * - A human-readable `message` describing the problem\n * - A `source` snippet from the template (if the position is available)\n * - Structured `details` for debugging\n */\nfunction addDiagnostic(\n\tctx: AnalysisContext,\n\tcode: DiagnosticCode,\n\tseverity: \"error\" | \"warning\",\n\tmessage: string,\n\tnode?: hbs.AST.Node,\n\tdetails?: DiagnosticDetails,\n): void {\n\tconst diagnostic: TemplateDiagnostic = { severity, code, message };\n\n\t// Extract the position and source snippet if available\n\tif (node && \"loc\" in node && node.loc) {\n\t\tdiagnostic.loc = {\n\t\t\tstart: { line: node.loc.start.line, column: node.loc.start.column },\n\t\t\tend: { line: node.loc.end.line, column: node.loc.end.column },\n\t\t};\n\t\t// Extract the template fragment around the error\n\t\tdiagnostic.source = extractSourceSnippet(ctx.template, diagnostic.loc);\n\t}\n\n\tif (details) {\n\t\tdiagnostic.details = details;\n\t}\n\n\tctx.diagnostics.push(diagnostic);\n}\n\n/**\n * Returns a human-readable label for a schema's type (for error messages).\n */\nfunction schemaTypeLabel(schema: JSONSchema7): string {\n\tif (schema.type) {\n\t\treturn Array.isArray(schema.type) ? schema.type.join(\" | \") : schema.type;\n\t}\n\tif (schema.oneOf) return \"oneOf(...)\";\n\tif (schema.anyOf) return \"anyOf(...)\";\n\tif (schema.allOf) return \"allOf(...)\";\n\tif (schema.enum) return \"enum\";\n\treturn \"unknown\";\n}\n\n// ─── Export for Internal Use ─────────────────────────────────────────────────\n// `inferBlockType` is exported to allow targeted unit tests\n// on block type inference.\nexport { inferBlockType };\n"],"names":["dispatchAnalyze","createMissingArgumentMessage","createPropertyNotFoundMessage","createRootPathTraversalMessage","createTypeMismatchMessage","createUnanalyzableMessage","createUnknownHelperMessage","MapHelpers","detectLiteralType","extractExpressionIdentifier","extractPathSegments","getEffectiveBody","getEffectivelySingleBlock","getEffectivelySingleExpression","isRootPathTraversal","isRootSegments","isThisExpression","parse","findConditionalSchemaLocations","resolveArrayItems","resolveSchemaPath","simplifySchema","deepEqual","extractSourceSnippet","getSchemaPropertyNames","coerceTextValue","text","targetType","undefined","num","Number","isNaN","isInteger","lower","toLowerCase","analyze","template","inputSchema","options","tpl","coerceSchema","ast","analyzeFromAst","identifierSchemas","child","childOptions","ctx","root","current","diagnostics","helpers","conditionalLocations","loc","addDiagnostic","keyword","schemaPath","path","id","idSchema","Object","entries","idLocations","length","valid","outputSchema","inferProgramType","hasErrors","some","d","severity","processStatement","stmt","type","processMustache","inferBlockType","params","hash","helperName","getExpressionName","MAP_HELPER_NAME","processMapHelper","helper","get","helperParams","requiredCount","filter","p","optional","expected","actual","i","resolvedSchema","resolveExpressionWithDiagnostics","helperParam","expectedType","isParamTypeCompatible","paramName","name","schemaTypeLabel","returnType","collectionExpr","collectionSchema","itemSchema","effectiveItemSchema","itemType","Array","isArray","includes","innerItems","effectiveItemType","isObject","properties","propertyExpr","propertyName","bare","original","value","propertySchema","availableProperties","items","resolved","expectedTypes","resolvedTypes","rt","et","program","effective","singleExpr","singleBlock","allContent","every","s","map","join","trim","coercedType","coercedValue","const","literalType","allBlocks","types","t","push","oneOf","body","getBlockHelperName","arg","getBlockArgument","thenType","inverse","elseType","saved","result","innerSchema","param","expr","parentNode","resolveSubExpression","segments","cleanSegments","identifier","fullPath","resolveRootWithIdentifier","resolveWithIdentifier","node","processMapSubExpression","code","message","details","diagnostic","start","line","column","end","source","schema","anyOf","allOf","enum"],"mappings":"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"}
|
package/dist/esm/executor.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import Handlebars from"handlebars";import{dispatchExecute}from"./dispatch.js";import{TemplateRuntimeError}from"./errors.js";import{MapHelpers}from"./helpers/map-helpers.js";import{canUseFastPath,coerceLiteral,extractExpressionIdentifier,extractPathSegments,getEffectiveBody,getEffectivelySingleBlock,getEffectivelySingleExpression,isRootPathTraversal,isRootSegments,isSingleExpression,isThisExpression,parse,ROOT_TOKEN}from"./parser.js";import{LRUCache}from"./utils.js";const globalCompilationCache=new LRUCache(128);export function execute(template,data,identifierData){return dispatchExecute(template,undefined,tpl=>{const ast=parse(tpl);return executeFromAst(ast,tpl,data,{identifierData})},child=>execute(child,data,identifierData))}export function executeFromAst(ast,template,data,ctx){const identifierData=ctx?.identifierData;if(isSingleExpression(ast)){const stmt=ast.body[0];if(stmt.params.length===0&&!stmt.hash){return resolveExpression(stmt.path,data,identifierData,ctx?.helpers)}}const singleExpr=getEffectivelySingleExpression(ast);if(singleExpr&&singleExpr.params.length===0&&!singleExpr.hash){return resolveExpression(singleExpr.path,data,identifierData,ctx?.helpers)}if(singleExpr&&(singleExpr.params.length>0||singleExpr.hash)){const directResult=tryDirectHelperExecution(singleExpr,data,ctx);if(directResult!==undefined){return directResult.value}const merged=mergeDataWithIdentifiers(data,identifierData);const raw=renderWithHandlebars(template,merged,ctx);return coerceValue(raw,ctx?.coerceSchema)}if(canUseFastPath(ast)&&ast.body.length>1){return executeFastPath(ast,data,identifierData)}const singleBlock=getEffectivelySingleBlock(ast);if(singleBlock){const merged=mergeDataWithIdentifiers(data,identifierData);const raw=renderWithHandlebars(template,merged,ctx);return coerceValue(raw,ctx?.coerceSchema)}const merged=mergeDataWithIdentifiers(data,identifierData);const raw=renderWithHandlebars(template,merged,ctx);const effective=getEffectiveBody(ast);const allContent=effective.every(s=>s.type==="ContentStatement");if(allContent){return coerceValue(raw,ctx?.coerceSchema)}return raw}function coerceValue(raw,coerceSchema){if(coerceSchema){const targetType=coerceSchema.type;if(typeof targetType==="string"){if(targetType==="string")return raw;if(targetType==="number"||targetType==="integer")return Number(
|
|
1
|
+
import Handlebars from"handlebars";import{dispatchExecute}from"./dispatch.js";import{TemplateRuntimeError}from"./errors.js";import{MapHelpers}from"./helpers/map-helpers.js";import{canUseFastPath,coerceLiteral,extractExpressionIdentifier,extractPathSegments,getEffectiveBody,getEffectivelySingleBlock,getEffectivelySingleExpression,isRootPathTraversal,isRootSegments,isSingleExpression,isThisExpression,parse,ROOT_TOKEN}from"./parser.js";import{LRUCache}from"./utils.js";const globalCompilationCache=new LRUCache(128);export function execute(template,data,identifierData){return dispatchExecute(template,undefined,tpl=>{const ast=parse(tpl);return executeFromAst(ast,tpl,data,{identifierData})},child=>execute(child,data,identifierData))}export function executeFromAst(ast,template,data,ctx){const identifierData=ctx?.identifierData;if(isSingleExpression(ast)){const stmt=ast.body[0];if(stmt.params.length===0&&!stmt.hash){return resolveExpression(stmt.path,data,identifierData,ctx?.helpers)}}const singleExpr=getEffectivelySingleExpression(ast);if(singleExpr&&singleExpr.params.length===0&&!singleExpr.hash){return resolveExpression(singleExpr.path,data,identifierData,ctx?.helpers)}if(singleExpr&&(singleExpr.params.length>0||singleExpr.hash)){const directResult=tryDirectHelperExecution(singleExpr,data,ctx);if(directResult!==undefined){return directResult.value}const merged=mergeDataWithIdentifiers(data,identifierData);const raw=renderWithHandlebars(template,merged,ctx);return coerceValue(raw,ctx?.coerceSchema)}if(canUseFastPath(ast)&&ast.body.length>1){return executeFastPath(ast,data,identifierData)}const singleBlock=getEffectivelySingleBlock(ast);if(singleBlock){const merged=mergeDataWithIdentifiers(data,identifierData);const raw=renderWithHandlebars(template,merged,ctx);return coerceValue(raw,ctx?.coerceSchema)}const merged=mergeDataWithIdentifiers(data,identifierData);const raw=renderWithHandlebars(template,merged,ctx);const effective=getEffectiveBody(ast);const allContent=effective.every(s=>s.type==="ContentStatement");if(allContent){return coerceValue(raw,ctx?.coerceSchema)}return raw}function coerceValue(raw,coerceSchema){if(coerceSchema){const targetType=coerceSchema.type;if(typeof targetType==="string"){if(targetType==="string")return raw;if(targetType==="number"||targetType==="integer"){const trimmed=raw.trim();if(trimmed==="")return undefined;const num=Number(trimmed);if(Number.isNaN(num))return undefined;if(targetType==="integer"&&!Number.isInteger(num))return undefined;return num}if(targetType==="boolean"){const lower=raw.trim().toLowerCase();if(lower==="true")return true;if(lower==="false")return false;return undefined}if(targetType==="null")return null}}return coerceLiteral(raw)}function executeFastPath(ast,data,identifierData){let result="";for(const stmt of ast.body){if(stmt.type==="ContentStatement"){result+=stmt.value}else if(stmt.type==="MustacheStatement"){const value=resolveExpression(stmt.path,data,identifierData);if(value!=null){result+=String(value)}}}return result}function resolveExpression(expr,data,identifierData,helpers){if(isThisExpression(expr)){return data}if(expr.type==="StringLiteral")return expr.value;if(expr.type==="NumberLiteral")return expr.value;if(expr.type==="BooleanLiteral")return expr.value;if(expr.type==="NullLiteral")return null;if(expr.type==="UndefinedLiteral")return undefined;if(expr.type==="SubExpression"){const subExpr=expr;if(subExpr.path.type==="PathExpression"){const helperName=subExpr.path.original;const helper=helpers?.get(helperName);if(helper){const isMap=helperName===MapHelpers.MAP_HELPER_NAME;const resolvedArgs=[];for(let i=0;i<subExpr.params.length;i++){const param=subExpr.params[i];if(isMap&&i===1&¶m.type==="StringLiteral"){resolvedArgs.push(param.value)}else{resolvedArgs.push(resolveExpression(param,data,identifierData,helpers))}}return helper.fn(...resolvedArgs)}}return undefined}const segments=extractPathSegments(expr);if(segments.length===0){throw new TemplateRuntimeError(`Cannot resolve expression of type "${expr.type}"`)}const{cleanSegments,identifier}=extractExpressionIdentifier(segments);if(isRootPathTraversal(cleanSegments)){return undefined}if(isRootSegments(cleanSegments)){if(identifier!==null&&identifierData){const source=identifierData[identifier];return source??undefined}if(identifier!==null){return undefined}return data}if(identifier!==null&&identifierData){const source=identifierData[identifier];if(source){return resolveDataPath(source,cleanSegments)}return undefined}if(identifier!==null&&!identifierData){return undefined}return resolveDataPath(data,cleanSegments)}export function resolveDataPath(data,segments){let current=data;for(const segment of segments){if(current===null||current===undefined){return undefined}if(typeof current!=="object"){return undefined}current=current[segment]}return current}function mergeDataWithIdentifiers(data,identifierData){const base=data!==null&&typeof data==="object"&&!Array.isArray(data)?data:{};const merged={...base,[ROOT_TOKEN]:data};if(!identifierData)return merged;for(const[id,idData]of Object.entries(identifierData)){merged[`${ROOT_TOKEN}:${id}`]=idData;for(const[key,value]of Object.entries(idData)){merged[`${key}:${id}`]=value}}return merged}function renderWithHandlebars(template,data,ctx){try{if(ctx?.compiledTemplate){return ctx.compiledTemplate(data)}const cache=ctx?.compilationCache??globalCompilationCache;const hbs=ctx?.hbs??Handlebars;let compiled=cache.get(template);if(!compiled){compiled=hbs.compile(template,{noEscape:true,strict:false});cache.set(template,compiled)}return compiled(data)}catch(error){const message=error instanceof Error?error.message:String(error);throw new TemplateRuntimeError(message)}}export function clearCompilationCache(){globalCompilationCache.clear()}const DIRECT_EXECUTION_HELPERS=new Set([MapHelpers.MAP_HELPER_NAME]);function tryDirectHelperExecution(stmt,data,ctx){if(stmt.path.type!=="PathExpression")return undefined;const helperName=stmt.path.original;if(!DIRECT_EXECUTION_HELPERS.has(helperName))return undefined;const helper=ctx?.helpers?.get(helperName);if(!helper)return undefined;const isMap=helperName===MapHelpers.MAP_HELPER_NAME;const resolvedArgs=[];for(let i=0;i<stmt.params.length;i++){const param=stmt.params[i];if(isMap&&i===1){if(param.type==="StringLiteral"){resolvedArgs.push(param.value)}else{resolvedArgs.push(resolveExpression(param,data,ctx?.identifierData,ctx?.helpers))}}else{resolvedArgs.push(resolveExpression(param,data,ctx?.identifierData,ctx?.helpers))}}const value=helper.fn(...resolvedArgs);return{value}}
|
|
2
2
|
//# sourceMappingURL=executor.js.map
|
package/dist/esm/executor.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/executor.ts"],"sourcesContent":["import Handlebars from \"handlebars\";\nimport type { JSONSchema7 } from \"json-schema\";\nimport { dispatchExecute } from \"./dispatch.ts\";\nimport { TemplateRuntimeError } from \"./errors.ts\";\nimport { MapHelpers } from \"./helpers/map-helpers.ts\";\nimport {\n\tcanUseFastPath,\n\tcoerceLiteral,\n\textractExpressionIdentifier,\n\textractPathSegments,\n\tgetEffectiveBody,\n\tgetEffectivelySingleBlock,\n\tgetEffectivelySingleExpression,\n\tisRootPathTraversal,\n\tisRootSegments,\n\tisSingleExpression,\n\tisThisExpression,\n\tparse,\n\tROOT_TOKEN,\n} from \"./parser.ts\";\nimport type { HelperDefinition, TemplateInput } 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\t/**\n\t * Explicit coercion schema for the output value.\n\t * When set with a primitive type, the execution result will be coerced\n\t * to match the declared type instead of using auto-detection.\n\t */\n\tcoerceSchema?: JSONSchema7;\n\t/** Registered helpers (for direct execution of special helpers like `map`) */\n\thelpers?: Map<string, HelperDefinition>;\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: unknown,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): unknown {\n\treturn dispatchExecute(\n\t\ttemplate,\n\t\tundefined,\n\t\t// String handler — parse and execute the AST\n\t\t(tpl) => {\n\t\t\tconst ast = parse(tpl);\n\t\t\treturn executeFromAst(ast, tpl, data, { identifierData });\n\t\t},\n\t\t// Recursive handler — re-enter execute() for child elements\n\t\t(child) => execute(child, data, identifierData),\n\t);\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: 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, ctx?.helpers);\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(\n\t\t\tsingleExpr.path,\n\t\t\tdata,\n\t\t\tidentifierData,\n\t\t\tctx?.helpers,\n\t\t);\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\t// ── Special case: helpers that return non-primitive values ────────\n\t\t// Some helpers (e.g. `map`) return arrays or objects. Handlebars\n\t\t// would stringify these, so we resolve their arguments directly and\n\t\t// call the helper's fn to preserve the raw return value.\n\t\tconst directResult = tryDirectHelperExecution(singleExpr, data, ctx);\n\t\tif (directResult !== undefined) {\n\t\t\treturn directResult.value;\n\t\t}\n\n\t\tconst merged = mergeDataWithIdentifiers(data, identifierData);\n\t\tconst raw = renderWithHandlebars(template, merged, ctx);\n\t\treturn coerceValue(raw, ctx?.coerceSchema);\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 coerceValue(raw, ctx?.coerceSchema);\n\t}\n\n\t// ── Case 4: mixed template ───────────────────────────────────────────\n\t// For purely static templates (only ContentStatements), coerce the\n\t// result to match the coerceSchema type or auto-detect the literal type.\n\t// For truly mixed templates (text + blocks + expressions), return string.\n\tconst merged = mergeDataWithIdentifiers(data, identifierData);\n\tconst raw = renderWithHandlebars(template, merged, ctx);\n\n\tconst effective = getEffectiveBody(ast);\n\tconst allContent = effective.every((s) => s.type === \"ContentStatement\");\n\tif (allContent) {\n\t\treturn coerceValue(raw, ctx?.coerceSchema);\n\t}\n\n\treturn raw;\n}\n\n// ─── Value Coercion ──────────────────────────────────────────────────────────\n// Coerces a raw string from Handlebars rendering based on an optional\n// coerceSchema. When no schema is provided, falls back to auto-detection\n// via `coerceLiteral`.\n\n/**\n * Coerces a raw string value based on an optional coercion schema.\n *\n * - If `coerceSchema` declares a primitive type (`string`, `number`,\n * `integer`, `boolean`, `null`), the value is cast to that type.\n * - Otherwise, falls back to `coerceLiteral` (auto-detection).\n *\n * @param raw - The raw string from Handlebars rendering\n * @param coerceSchema - Optional schema declaring the desired output type\n * @returns The coerced value\n */\nfunction coerceValue(raw: string, coerceSchema?: JSONSchema7): unknown {\n\tif (coerceSchema) {\n\t\tconst targetType = coerceSchema.type;\n\t\tif (typeof targetType === \"string\") {\n\t\t\tif (targetType === \"string\") return raw;\n\t\t\tif (targetType === \"number\" || targetType === \"integer\")\n\t\t\t\treturn Number(raw.trim());\n\t\t\tif (targetType === \"boolean\") return raw.trim() === \"true\";\n\t\t\tif (targetType === \"null\") return null;\n\t\t}\n\t}\n\t// No coerceSchema or non-primitive type → auto-detect\n\treturn coerceLiteral(raw);\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: 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: unknown,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n\thelpers?: Map<string, HelperDefinition>,\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// ── SubExpression (nested helper call) ────────────────────────────────\n\t// E.g. `(map users 'cartItems')` used as an argument to another helper.\n\t// Resolve all arguments recursively and call the helper's fn directly.\n\tif (expr.type === \"SubExpression\") {\n\t\tconst subExpr = expr as hbs.AST.SubExpression;\n\t\tif (subExpr.path.type === \"PathExpression\") {\n\t\t\tconst helperName = (subExpr.path as hbs.AST.PathExpression).original;\n\t\t\tconst helper = helpers?.get(helperName);\n\t\t\tif (helper) {\n\t\t\t\tconst isMap = helperName === MapHelpers.MAP_HELPER_NAME;\n\t\t\t\tconst resolvedArgs: unknown[] = [];\n\t\t\t\tfor (let i = 0; i < subExpr.params.length; i++) {\n\t\t\t\t\tconst param = subExpr.params[i] as hbs.AST.Expression;\n\t\t\t\t\t// For `map`, the second argument is a property name literal\n\t\t\t\t\tif (isMap && i === 1 && param.type === \"StringLiteral\") {\n\t\t\t\t\t\tresolvedArgs.push((param as hbs.AST.StringLiteral).value);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresolvedArgs.push(\n\t\t\t\t\t\t\tresolveExpression(param, data, identifierData, helpers),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn helper.fn(...resolvedArgs);\n\t\t\t}\n\t\t}\n\t\t// Unknown sub-expression helper — return undefined\n\t\treturn undefined;\n\t}\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 BEFORE\n\t// checking for $root, so that both {{$root}} and {{$root:N}} are\n\t// handled uniformly.\n\tconst { cleanSegments, identifier } = extractExpressionIdentifier(segments);\n\n\t// $root path traversal ($root.name) — not supported, return undefined\n\t// (the analyzer already rejects it with a diagnostic).\n\tif (isRootPathTraversal(cleanSegments)) {\n\t\treturn undefined;\n\t}\n\n\t// $root → return the entire data context (or identifier data)\n\tif (isRootSegments(cleanSegments)) {\n\t\tif (identifier !== null && identifierData) {\n\t\t\tconst source = identifierData[identifier];\n\t\t\treturn source ?? undefined;\n\t\t}\n\t\tif (identifier !== null) {\n\t\t\t// Template uses an identifier but no identifierData was provided\n\t\t\treturn undefined;\n\t\t}\n\t\treturn data;\n\t}\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: unknown,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): Record<string, unknown> {\n\t// Always include $root so that Handlebars can resolve {{$root}} in\n\t// mixed templates and block helpers (where we delegate to Handlebars\n\t// instead of resolving expressions ourselves).\n\t// When data is a primitive (e.g. number passed with {{$root}}), we\n\t// wrap it into an object so Handlebars can still function.\n\tconst base: Record<string, unknown> =\n\t\tdata !== null && typeof data === \"object\" && !Array.isArray(data)\n\t\t\t? (data as Record<string, unknown>)\n\t\t\t: {};\n\tconst merged: Record<string, unknown> = { ...base, [ROOT_TOKEN]: data };\n\n\tif (!identifierData) return merged;\n\n\tfor (const [id, idData] of Object.entries(identifierData)) {\n\t\t// Add `$root:N` so Handlebars can resolve {{$root:N}} in mixed/block\n\t\t// templates (where we delegate to Handlebars instead of resolving\n\t\t// expressions ourselves). The value is the entire identifier data object.\n\t\tmerged[`${ROOT_TOKEN}:${id}`] = idData;\n\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\n// ─── Direct Helper Execution ─────────────────────────────────────────────────\n// Some helpers (e.g. `map`) return non-primitive values (arrays, objects)\n// that Handlebars would stringify. For these helpers, we resolve their\n// arguments directly and call the helper's `fn` to preserve the raw value.\n\n/** Set of helper names that must be executed directly (bypass Handlebars) */\nconst DIRECT_EXECUTION_HELPERS = new Set<string>([MapHelpers.MAP_HELPER_NAME]);\n\n/**\n * Attempts to execute a helper directly (without Handlebars rendering).\n *\n * Returns `{ value }` if the helper was executed directly, or `undefined`\n * if the helper should go through the normal Handlebars rendering path.\n *\n * @param stmt - The MustacheStatement containing the helper call\n * @param data - The context data\n * @param ctx - Optional execution context (with helpers and identifierData)\n */\nfunction tryDirectHelperExecution(\n\tstmt: hbs.AST.MustacheStatement,\n\tdata: unknown,\n\tctx?: ExecutorContext,\n): { value: unknown } | undefined {\n\t// Get the helper name from the path\n\tif (stmt.path.type !== \"PathExpression\") return undefined;\n\tconst helperName = (stmt.path as hbs.AST.PathExpression).original;\n\n\t// Only intercept known direct-execution helpers\n\tif (!DIRECT_EXECUTION_HELPERS.has(helperName)) return undefined;\n\n\t// Look up the helper definition\n\tconst helper = ctx?.helpers?.get(helperName);\n\tif (!helper) return undefined;\n\n\t// Resolve each argument from the data context.\n\t// For the `map` helper, the resolution strategy is:\n\t// - Arg 0 (array): resolve as a data path (e.g. `users` → array)\n\t// - Arg 1 (property): must be a StringLiteral (e.g. `\"name\"`)\n\t// The analyzer enforces this — bare identifiers like `name` are\n\t// rejected at analysis time because Handlebars would resolve them\n\t// as a data path instead of a literal property name.\n\tconst isMap = helperName === MapHelpers.MAP_HELPER_NAME;\n\n\tconst resolvedArgs: unknown[] = [];\n\tfor (let i = 0; i < stmt.params.length; i++) {\n\t\tconst param = stmt.params[i] as hbs.AST.Expression;\n\n\t\t// For `map`, the second argument (index 1) is a property name —\n\t\t// it must be a StringLiteral (enforced by the analyzer).\n\t\tif (isMap && i === 1) {\n\t\t\tif (param.type === \"StringLiteral\") {\n\t\t\t\tresolvedArgs.push((param as hbs.AST.StringLiteral).value);\n\t\t\t} else {\n\t\t\t\t// Fallback: resolve normally (will likely be undefined at runtime)\n\t\t\t\tresolvedArgs.push(\n\t\t\t\t\tresolveExpression(param, data, ctx?.identifierData, ctx?.helpers),\n\t\t\t\t);\n\t\t\t}\n\t\t} else {\n\t\t\tresolvedArgs.push(\n\t\t\t\tresolveExpression(param, data, ctx?.identifierData, ctx?.helpers),\n\t\t\t);\n\t\t}\n\t}\n\n\t// Call the helper's fn directly with the resolved arguments\n\tconst value = helper.fn(...resolvedArgs);\n\treturn { value };\n}\n"],"names":["Handlebars","dispatchExecute","TemplateRuntimeError","MapHelpers","canUseFastPath","coerceLiteral","extractExpressionIdentifier","extractPathSegments","getEffectiveBody","getEffectivelySingleBlock","getEffectivelySingleExpression","isRootPathTraversal","isRootSegments","isSingleExpression","isThisExpression","parse","ROOT_TOKEN","LRUCache","globalCompilationCache","execute","template","data","identifierData","undefined","tpl","ast","executeFromAst","child","ctx","stmt","body","params","length","hash","resolveExpression","path","helpers","singleExpr","directResult","tryDirectHelperExecution","value","merged","mergeDataWithIdentifiers","raw","renderWithHandlebars","coerceValue","coerceSchema","executeFastPath","singleBlock","effective","allContent","every","s","type","targetType","Number","trim","result","String","expr","subExpr","helperName","original","helper","get","isMap","MAP_HELPER_NAME","resolvedArgs","i","param","push","fn","segments","cleanSegments","identifier","source","resolveDataPath","current","segment","base","Array","isArray","id","idData","Object","entries","key","compiledTemplate","cache","compilationCache","hbs","compiled","compile","noEscape","strict","set","error","message","Error","clearCompilationCache","clear","DIRECT_EXECUTION_HELPERS","Set","has"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/executor.ts"],"sourcesContent":["import Handlebars from \"handlebars\";\nimport type { JSONSchema7 } from \"json-schema\";\nimport { dispatchExecute } from \"./dispatch.ts\";\nimport { TemplateRuntimeError } from \"./errors.ts\";\nimport { MapHelpers } from \"./helpers/map-helpers.ts\";\nimport {\n\tcanUseFastPath,\n\tcoerceLiteral,\n\textractExpressionIdentifier,\n\textractPathSegments,\n\tgetEffectiveBody,\n\tgetEffectivelySingleBlock,\n\tgetEffectivelySingleExpression,\n\tisRootPathTraversal,\n\tisRootSegments,\n\tisSingleExpression,\n\tisThisExpression,\n\tparse,\n\tROOT_TOKEN,\n} from \"./parser.ts\";\nimport type { HelperDefinition, TemplateInput } 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\t/**\n\t * Explicit coercion schema for the output value.\n\t * When set with a primitive type, the execution result will be coerced\n\t * to match the declared type instead of using auto-detection.\n\t */\n\tcoerceSchema?: JSONSchema7;\n\t/** Registered helpers (for direct execution of special helpers like `map`) */\n\thelpers?: Map<string, HelperDefinition>;\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: unknown,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): unknown {\n\treturn dispatchExecute(\n\t\ttemplate,\n\t\tundefined,\n\t\t// String handler — parse and execute the AST\n\t\t(tpl) => {\n\t\t\tconst ast = parse(tpl);\n\t\t\treturn executeFromAst(ast, tpl, data, { identifierData });\n\t\t},\n\t\t// Recursive handler — re-enter execute() for child elements\n\t\t(child) => execute(child, data, identifierData),\n\t);\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: 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, ctx?.helpers);\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(\n\t\t\tsingleExpr.path,\n\t\t\tdata,\n\t\t\tidentifierData,\n\t\t\tctx?.helpers,\n\t\t);\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\t// ── Special case: helpers that return non-primitive values ────────\n\t\t// Some helpers (e.g. `map`) return arrays or objects. Handlebars\n\t\t// would stringify these, so we resolve their arguments directly and\n\t\t// call the helper's fn to preserve the raw return value.\n\t\tconst directResult = tryDirectHelperExecution(singleExpr, data, ctx);\n\t\tif (directResult !== undefined) {\n\t\t\treturn directResult.value;\n\t\t}\n\n\t\tconst merged = mergeDataWithIdentifiers(data, identifierData);\n\t\tconst raw = renderWithHandlebars(template, merged, ctx);\n\t\treturn coerceValue(raw, ctx?.coerceSchema);\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 coerceValue(raw, ctx?.coerceSchema);\n\t}\n\n\t// ── Case 4: mixed template ───────────────────────────────────────────\n\t// For purely static templates (only ContentStatements), coerce the\n\t// result to match the coerceSchema type or auto-detect the literal type.\n\t// For truly mixed templates (text + blocks + expressions), return string.\n\tconst merged = mergeDataWithIdentifiers(data, identifierData);\n\tconst raw = renderWithHandlebars(template, merged, ctx);\n\n\tconst effective = getEffectiveBody(ast);\n\tconst allContent = effective.every((s) => s.type === \"ContentStatement\");\n\tif (allContent) {\n\t\treturn coerceValue(raw, ctx?.coerceSchema);\n\t}\n\n\treturn raw;\n}\n\n// ─── Value Coercion ──────────────────────────────────────────────────────────\n// Coerces a raw string from Handlebars rendering based on an optional\n// coerceSchema. When no schema is provided, falls back to auto-detection\n// via `coerceLiteral`.\n\n/**\n * Coerces a raw string value based on an optional coercion schema.\n *\n * - If `coerceSchema` declares a primitive type (`string`, `number`,\n * `integer`, `boolean`, `null`), the value is cast to that type.\n * - Otherwise, falls back to `coerceLiteral` (auto-detection).\n *\n * @param raw - The raw string from Handlebars rendering\n * @param coerceSchema - Optional schema declaring the desired output type\n * @returns The coerced value\n */\nfunction coerceValue(raw: string, coerceSchema?: JSONSchema7): unknown {\n\tif (coerceSchema) {\n\t\tconst targetType = coerceSchema.type;\n\t\tif (typeof targetType === \"string\") {\n\t\t\tif (targetType === \"string\") return raw;\n\t\t\tif (targetType === \"number\" || targetType === \"integer\") {\n\t\t\t\tconst trimmed = raw.trim();\n\t\t\t\tif (trimmed === \"\") return undefined;\n\t\t\t\tconst num = Number(trimmed);\n\t\t\t\tif (Number.isNaN(num)) return undefined;\n\t\t\t\tif (targetType === \"integer\" && !Number.isInteger(num))\n\t\t\t\t\treturn undefined;\n\t\t\t\treturn num;\n\t\t\t}\n\t\t\tif (targetType === \"boolean\") {\n\t\t\t\tconst lower = raw.trim().toLowerCase();\n\t\t\t\tif (lower === \"true\") return true;\n\t\t\t\tif (lower === \"false\") return false;\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tif (targetType === \"null\") return null;\n\t\t}\n\t}\n\t// No coerceSchema or non-primitive type → auto-detect\n\treturn coerceLiteral(raw);\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: 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: unknown,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n\thelpers?: Map<string, HelperDefinition>,\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// ── SubExpression (nested helper call) ────────────────────────────────\n\t// E.g. `(map users 'cartItems')` used as an argument to another helper.\n\t// Resolve all arguments recursively and call the helper's fn directly.\n\tif (expr.type === \"SubExpression\") {\n\t\tconst subExpr = expr as hbs.AST.SubExpression;\n\t\tif (subExpr.path.type === \"PathExpression\") {\n\t\t\tconst helperName = (subExpr.path as hbs.AST.PathExpression).original;\n\t\t\tconst helper = helpers?.get(helperName);\n\t\t\tif (helper) {\n\t\t\t\tconst isMap = helperName === MapHelpers.MAP_HELPER_NAME;\n\t\t\t\tconst resolvedArgs: unknown[] = [];\n\t\t\t\tfor (let i = 0; i < subExpr.params.length; i++) {\n\t\t\t\t\tconst param = subExpr.params[i] as hbs.AST.Expression;\n\t\t\t\t\t// For `map`, the second argument is a property name literal\n\t\t\t\t\tif (isMap && i === 1 && param.type === \"StringLiteral\") {\n\t\t\t\t\t\tresolvedArgs.push((param as hbs.AST.StringLiteral).value);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresolvedArgs.push(\n\t\t\t\t\t\t\tresolveExpression(param, data, identifierData, helpers),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn helper.fn(...resolvedArgs);\n\t\t\t}\n\t\t}\n\t\t// Unknown sub-expression helper — return undefined\n\t\treturn undefined;\n\t}\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 BEFORE\n\t// checking for $root, so that both {{$root}} and {{$root:N}} are\n\t// handled uniformly.\n\tconst { cleanSegments, identifier } = extractExpressionIdentifier(segments);\n\n\t// $root path traversal ($root.name) — not supported, return undefined\n\t// (the analyzer already rejects it with a diagnostic).\n\tif (isRootPathTraversal(cleanSegments)) {\n\t\treturn undefined;\n\t}\n\n\t// $root → return the entire data context (or identifier data)\n\tif (isRootSegments(cleanSegments)) {\n\t\tif (identifier !== null && identifierData) {\n\t\t\tconst source = identifierData[identifier];\n\t\t\treturn source ?? undefined;\n\t\t}\n\t\tif (identifier !== null) {\n\t\t\t// Template uses an identifier but no identifierData was provided\n\t\t\treturn undefined;\n\t\t}\n\t\treturn data;\n\t}\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: unknown,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): Record<string, unknown> {\n\t// Always include $root so that Handlebars can resolve {{$root}} in\n\t// mixed templates and block helpers (where we delegate to Handlebars\n\t// instead of resolving expressions ourselves).\n\t// When data is a primitive (e.g. number passed with {{$root}}), we\n\t// wrap it into an object so Handlebars can still function.\n\tconst base: Record<string, unknown> =\n\t\tdata !== null && typeof data === \"object\" && !Array.isArray(data)\n\t\t\t? (data as Record<string, unknown>)\n\t\t\t: {};\n\tconst merged: Record<string, unknown> = { ...base, [ROOT_TOKEN]: data };\n\n\tif (!identifierData) return merged;\n\n\tfor (const [id, idData] of Object.entries(identifierData)) {\n\t\t// Add `$root:N` so Handlebars can resolve {{$root:N}} in mixed/block\n\t\t// templates (where we delegate to Handlebars instead of resolving\n\t\t// expressions ourselves). The value is the entire identifier data object.\n\t\tmerged[`${ROOT_TOKEN}:${id}`] = idData;\n\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\n// ─── Direct Helper Execution ─────────────────────────────────────────────────\n// Some helpers (e.g. `map`) return non-primitive values (arrays, objects)\n// that Handlebars would stringify. For these helpers, we resolve their\n// arguments directly and call the helper's `fn` to preserve the raw value.\n\n/** Set of helper names that must be executed directly (bypass Handlebars) */\nconst DIRECT_EXECUTION_HELPERS = new Set<string>([MapHelpers.MAP_HELPER_NAME]);\n\n/**\n * Attempts to execute a helper directly (without Handlebars rendering).\n *\n * Returns `{ value }` if the helper was executed directly, or `undefined`\n * if the helper should go through the normal Handlebars rendering path.\n *\n * @param stmt - The MustacheStatement containing the helper call\n * @param data - The context data\n * @param ctx - Optional execution context (with helpers and identifierData)\n */\nfunction tryDirectHelperExecution(\n\tstmt: hbs.AST.MustacheStatement,\n\tdata: unknown,\n\tctx?: ExecutorContext,\n): { value: unknown } | undefined {\n\t// Get the helper name from the path\n\tif (stmt.path.type !== \"PathExpression\") return undefined;\n\tconst helperName = (stmt.path as hbs.AST.PathExpression).original;\n\n\t// Only intercept known direct-execution helpers\n\tif (!DIRECT_EXECUTION_HELPERS.has(helperName)) return undefined;\n\n\t// Look up the helper definition\n\tconst helper = ctx?.helpers?.get(helperName);\n\tif (!helper) return undefined;\n\n\t// Resolve each argument from the data context.\n\t// For the `map` helper, the resolution strategy is:\n\t// - Arg 0 (array): resolve as a data path (e.g. `users` → array)\n\t// - Arg 1 (property): must be a StringLiteral (e.g. `\"name\"`)\n\t// The analyzer enforces this — bare identifiers like `name` are\n\t// rejected at analysis time because Handlebars would resolve them\n\t// as a data path instead of a literal property name.\n\tconst isMap = helperName === MapHelpers.MAP_HELPER_NAME;\n\n\tconst resolvedArgs: unknown[] = [];\n\tfor (let i = 0; i < stmt.params.length; i++) {\n\t\tconst param = stmt.params[i] as hbs.AST.Expression;\n\n\t\t// For `map`, the second argument (index 1) is a property name —\n\t\t// it must be a StringLiteral (enforced by the analyzer).\n\t\tif (isMap && i === 1) {\n\t\t\tif (param.type === \"StringLiteral\") {\n\t\t\t\tresolvedArgs.push((param as hbs.AST.StringLiteral).value);\n\t\t\t} else {\n\t\t\t\t// Fallback: resolve normally (will likely be undefined at runtime)\n\t\t\t\tresolvedArgs.push(\n\t\t\t\t\tresolveExpression(param, data, ctx?.identifierData, ctx?.helpers),\n\t\t\t\t);\n\t\t\t}\n\t\t} else {\n\t\t\tresolvedArgs.push(\n\t\t\t\tresolveExpression(param, data, ctx?.identifierData, ctx?.helpers),\n\t\t\t);\n\t\t}\n\t}\n\n\t// Call the helper's fn directly with the resolved arguments\n\tconst value = helper.fn(...resolvedArgs);\n\treturn { value };\n}\n"],"names":["Handlebars","dispatchExecute","TemplateRuntimeError","MapHelpers","canUseFastPath","coerceLiteral","extractExpressionIdentifier","extractPathSegments","getEffectiveBody","getEffectivelySingleBlock","getEffectivelySingleExpression","isRootPathTraversal","isRootSegments","isSingleExpression","isThisExpression","parse","ROOT_TOKEN","LRUCache","globalCompilationCache","execute","template","data","identifierData","undefined","tpl","ast","executeFromAst","child","ctx","stmt","body","params","length","hash","resolveExpression","path","helpers","singleExpr","directResult","tryDirectHelperExecution","value","merged","mergeDataWithIdentifiers","raw","renderWithHandlebars","coerceValue","coerceSchema","executeFastPath","singleBlock","effective","allContent","every","s","type","targetType","trimmed","trim","num","Number","isNaN","isInteger","lower","toLowerCase","result","String","expr","subExpr","helperName","original","helper","get","isMap","MAP_HELPER_NAME","resolvedArgs","i","param","push","fn","segments","cleanSegments","identifier","source","resolveDataPath","current","segment","base","Array","isArray","id","idData","Object","entries","key","compiledTemplate","cache","compilationCache","hbs","compiled","compile","noEscape","strict","set","error","message","Error","clearCompilationCache","clear","DIRECT_EXECUTION_HELPERS","Set","has"],"mappings":"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"}
|