typebars 1.0.12 → 1.0.14

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.
Files changed (39) hide show
  1. package/dist/cjs/analyzer.js +1 -1
  2. package/dist/cjs/analyzer.js.map +1 -1
  3. package/dist/cjs/compiled-template.js +1 -1
  4. package/dist/cjs/compiled-template.js.map +1 -1
  5. package/dist/cjs/executor.d.ts +3 -1
  6. package/dist/cjs/executor.js +1 -1
  7. package/dist/cjs/executor.js.map +1 -1
  8. package/dist/cjs/helpers/collection-helpers.d.ts +9 -0
  9. package/dist/cjs/helpers/collection-helpers.js +2 -0
  10. package/dist/cjs/helpers/collection-helpers.js.map +1 -0
  11. package/dist/cjs/helpers/index.d.ts +1 -0
  12. package/dist/cjs/helpers/index.js +1 -1
  13. package/dist/cjs/helpers/index.js.map +1 -1
  14. package/dist/cjs/parser.js +1 -1
  15. package/dist/cjs/parser.js.map +1 -1
  16. package/dist/cjs/schema-resolver.js +1 -1
  17. package/dist/cjs/schema-resolver.js.map +1 -1
  18. package/dist/cjs/typebars.js +1 -1
  19. package/dist/cjs/typebars.js.map +1 -1
  20. package/dist/esm/analyzer.js +1 -1
  21. package/dist/esm/analyzer.js.map +1 -1
  22. package/dist/esm/compiled-template.js +1 -1
  23. package/dist/esm/compiled-template.js.map +1 -1
  24. package/dist/esm/executor.d.ts +3 -1
  25. package/dist/esm/executor.js +1 -1
  26. package/dist/esm/executor.js.map +1 -1
  27. package/dist/esm/helpers/collection-helpers.d.ts +9 -0
  28. package/dist/esm/helpers/collection-helpers.js +2 -0
  29. package/dist/esm/helpers/collection-helpers.js.map +1 -0
  30. package/dist/esm/helpers/index.d.ts +1 -0
  31. package/dist/esm/helpers/index.js +1 -1
  32. package/dist/esm/helpers/index.js.map +1 -1
  33. package/dist/esm/parser.js +1 -1
  34. package/dist/esm/parser.js.map +1 -1
  35. package/dist/esm/schema-resolver.js +1 -1
  36. package/dist/esm/schema-resolver.js.map +1 -1
  37. package/dist/esm/typebars.js +1 -1
  38. package/dist/esm/typebars.js.map +1 -1
  39. package/package.json +1 -1
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});function _export(target,all){for(var name in all)Object.defineProperty(target,name,{enumerable:true,get:Object.getOwnPropertyDescriptor(all,name).get})}_export(exports,{get analyze(){return analyze},get analyzeFromAst(){return analyzeFromAst},get inferBlockType(){return inferBlockType}});const _dispatchts=require("./dispatch.js");const _errors=require("./errors.js");const _parser=require("./parser.js");const _schemaresolver=require("./schema-resolver.js");const _utils=require("./utils.js");function analyze(template,inputSchema={},options){return(0,_dispatchts.dispatchAnalyze)(template,options,(tpl,coerceSchema)=>{const ast=(0,_parser.parse)(tpl);return analyzeFromAst(ast,tpl,inputSchema,{identifierSchemas:options?.identifierSchemas,coerceSchema})},(child,childOptions)=>analyze(child,inputSchema,childOptions))}function analyzeFromAst(ast,template,inputSchema={},options){(0,_schemaresolver.assertNoConditionalSchema)(inputSchema);if(options?.identifierSchemas){for(const[id,idSchema]of Object.entries(options.identifierSchemas)){(0,_schemaresolver.assertNoConditionalSchema)(idSchema,`/identifierSchemas/${id}`)}}const ctx={root:inputSchema,current:inputSchema,diagnostics:[],template,identifierSchemas:options?.identifierSchemas,helpers:options?.helpers,coerceSchema:options?.coerceSchema};const outputSchema=inferProgramType(ast,ctx);const hasErrors=ctx.diagnostics.some(d=>d.severity==="error");return{valid:!hasErrors,diagnostics:ctx.diagnostics,outputSchema:(0,_schemaresolver.simplifySchema)(outputSchema)}}function processStatement(stmt,ctx){switch(stmt.type){case"ContentStatement":case"CommentStatement":return undefined;case"MustacheStatement":return processMustache(stmt,ctx);case"BlockStatement":return inferBlockType(stmt,ctx);default:addDiagnostic(ctx,"UNANALYZABLE","warning",`Unsupported AST node type: "${stmt.type}"`,stmt);return undefined}}function processMustache(stmt,ctx){if(stmt.path.type==="SubExpression"){addDiagnostic(ctx,"UNANALYZABLE","warning","Sub-expressions are not statically analyzable",stmt);return{}}if(stmt.params.length>0||stmt.hash){const helperName=getExpressionName(stmt.path);const helper=ctx.helpers?.get(helperName);if(helper){const helperParams=helper.params;if(helperParams){const requiredCount=helperParams.filter(p=>!p.optional).length;if(stmt.params.length<requiredCount){addDiagnostic(ctx,"MISSING_ARGUMENT","error",`Helper "${helperName}" expects at least ${requiredCount} argument(s), but got ${stmt.params.length}`,stmt,{helperName,expected:`${requiredCount} argument(s)`,actual:`${stmt.params.length} argument(s)`})}}for(let i=0;i<stmt.params.length;i++){const resolvedSchema=resolveExpressionWithDiagnostics(stmt.params[i],ctx,stmt);const helperParam=helperParams?.[i];if(resolvedSchema&&helperParam?.type){const expectedType=helperParam.type;if(!isParamTypeCompatible(resolvedSchema,expectedType)){const paramName=helperParam.name;addDiagnostic(ctx,"TYPE_MISMATCH","error",`Helper "${helperName}" parameter "${paramName}" expects ${schemaTypeLabel(expectedType)}, but got ${schemaTypeLabel(resolvedSchema)}`,stmt,{helperName,expected:schemaTypeLabel(expectedType),actual:schemaTypeLabel(resolvedSchema)})}}}return helper.returnType??{type:"string"}}addDiagnostic(ctx,"UNKNOWN_HELPER","warning",`Unknown inline helper "${helperName}" — cannot analyze statically`,stmt,{helperName});return{type:"string"}}return resolveExpressionWithDiagnostics(stmt.path,ctx,stmt)??{}}function isParamTypeCompatible(resolved,expected){if(!expected.type||!resolved.type)return true;const expectedTypes=Array.isArray(expected.type)?expected.type:[expected.type];const resolvedTypes=Array.isArray(resolved.type)?resolved.type:[resolved.type];return resolvedTypes.some(rt=>expectedTypes.some(et=>rt===et||et==="number"&&rt==="integer"||et==="integer"&&rt==="number"))}function inferProgramType(program,ctx){const effective=(0,_parser.getEffectiveBody)(program);if(effective.length===0){return{type:"string"}}const singleExpr=(0,_parser.getEffectivelySingleExpression)(program);if(singleExpr){return processMustache(singleExpr,ctx)}const singleBlock=(0,_parser.getEffectivelySingleBlock)(program);if(singleBlock){return inferBlockType(singleBlock,ctx)}const allContent=effective.every(s=>s.type==="ContentStatement");if(allContent){const text=effective.map(s=>s.value).join("").trim();if(text==="")return{type:"string"};const coercedType=ctx.coerceSchema?.type;if(typeof coercedType==="string"&&(coercedType==="string"||coercedType==="number"||coercedType==="integer"||coercedType==="boolean"||coercedType==="null")){return{type:coercedType}}const literalType=(0,_parser.detectLiteralType)(text);if(literalType)return{type:literalType}}const allBlocks=effective.every(s=>s.type==="BlockStatement");if(allBlocks){const types=[];for(const stmt of effective){const t=inferBlockType(stmt,ctx);if(t)types.push(t)}if(types.length===1)return types[0];if(types.length>1)return(0,_schemaresolver.simplifySchema)({oneOf:types});return{type:"string"}}for(const stmt of program.body){processStatement(stmt,ctx)}return{type:"string"}}function inferBlockType(stmt,ctx){const helperName=getBlockHelperName(stmt);switch(helperName){case"if":case"unless":{const arg=getBlockArgument(stmt);if(arg){resolveExpressionWithDiagnostics(arg,ctx,stmt)}else{addDiagnostic(ctx,"MISSING_ARGUMENT","error",(0,_errors.createMissingArgumentMessage)(helperName),stmt,{helperName})}const thenType=inferProgramType(stmt.program,ctx);if(stmt.inverse){const elseType=inferProgramType(stmt.inverse,ctx);if((0,_utils.deepEqual)(thenType,elseType))return thenType;return(0,_schemaresolver.simplifySchema)({oneOf:[thenType,elseType]})}return thenType}case"each":{const arg=getBlockArgument(stmt);if(!arg){addDiagnostic(ctx,"MISSING_ARGUMENT","error",(0,_errors.createMissingArgumentMessage)("each"),stmt,{helperName:"each"});const saved=ctx.current;ctx.current={};inferProgramType(stmt.program,ctx);ctx.current=saved;if(stmt.inverse)inferProgramType(stmt.inverse,ctx);return{type:"string"}}const collectionSchema=resolveExpressionWithDiagnostics(arg,ctx,stmt);if(!collectionSchema){const saved=ctx.current;ctx.current={};inferProgramType(stmt.program,ctx);ctx.current=saved;if(stmt.inverse)inferProgramType(stmt.inverse,ctx);return{type:"string"}}const itemSchema=(0,_schemaresolver.resolveArrayItems)(collectionSchema,ctx.root);if(!itemSchema){addDiagnostic(ctx,"TYPE_MISMATCH","error",(0,_errors.createTypeMismatchMessage)("each","an array",schemaTypeLabel(collectionSchema)),stmt,{helperName:"each",expected:"array",actual:schemaTypeLabel(collectionSchema)});const saved=ctx.current;ctx.current={};inferProgramType(stmt.program,ctx);ctx.current=saved;if(stmt.inverse)inferProgramType(stmt.inverse,ctx);return{type:"string"}}const saved=ctx.current;ctx.current=itemSchema;inferProgramType(stmt.program,ctx);ctx.current=saved;if(stmt.inverse)inferProgramType(stmt.inverse,ctx);return{type:"string"}}case"with":{const arg=getBlockArgument(stmt);if(!arg){addDiagnostic(ctx,"MISSING_ARGUMENT","error",(0,_errors.createMissingArgumentMessage)("with"),stmt,{helperName:"with"});const saved=ctx.current;ctx.current={};const result=inferProgramType(stmt.program,ctx);ctx.current=saved;if(stmt.inverse)inferProgramType(stmt.inverse,ctx);return result}const innerSchema=resolveExpressionWithDiagnostics(arg,ctx,stmt);const saved=ctx.current;ctx.current=innerSchema??{};const result=inferProgramType(stmt.program,ctx);ctx.current=saved;if(stmt.inverse)inferProgramType(stmt.inverse,ctx);return result}default:{const helper=ctx.helpers?.get(helperName);if(helper){for(const param of stmt.params){resolveExpressionWithDiagnostics(param,ctx,stmt)}inferProgramType(stmt.program,ctx);if(stmt.inverse)inferProgramType(stmt.inverse,ctx);return helper.returnType??{type:"string"}}addDiagnostic(ctx,"UNKNOWN_HELPER","warning",(0,_errors.createUnknownHelperMessage)(helperName),stmt,{helperName});inferProgramType(stmt.program,ctx);if(stmt.inverse)inferProgramType(stmt.inverse,ctx);return{type:"string"}}}}function resolveExpressionWithDiagnostics(expr,ctx,parentNode){if((0,_parser.isThisExpression)(expr)){return ctx.current}if(expr.type==="SubExpression"){return resolveSubExpression(expr,ctx,parentNode)}const segments=(0,_parser.extractPathSegments)(expr);if(segments.length===0){if(expr.type==="StringLiteral")return{type:"string"};if(expr.type==="NumberLiteral")return{type:"number"};if(expr.type==="BooleanLiteral")return{type:"boolean"};if(expr.type==="NullLiteral")return{type:"null"};if(expr.type==="UndefinedLiteral")return{};addDiagnostic(ctx,"UNANALYZABLE","warning",(0,_errors.createUnanalyzableMessage)(expr.type),parentNode??expr);return undefined}const{cleanSegments,identifier}=(0,_parser.extractExpressionIdentifier)(segments);if((0,_parser.isRootPathTraversal)(cleanSegments)){const fullPath=cleanSegments.join(".");addDiagnostic(ctx,"ROOT_PATH_TRAVERSAL","error",(0,_errors.createRootPathTraversalMessage)(fullPath),parentNode??expr,{path:fullPath});return undefined}if((0,_parser.isRootSegments)(cleanSegments)){if(identifier!==null){return resolveRootWithIdentifier(identifier,ctx,parentNode??expr)}return ctx.current}if(identifier!==null){return resolveWithIdentifier(cleanSegments,identifier,ctx,parentNode??expr)}const resolved=(0,_schemaresolver.resolveSchemaPath)(ctx.current,cleanSegments);if(resolved===undefined){const fullPath=cleanSegments.join(".");const availableProperties=(0,_utils.getSchemaPropertyNames)(ctx.current);addDiagnostic(ctx,"UNKNOWN_PROPERTY","error",(0,_errors.createPropertyNotFoundMessage)(fullPath,availableProperties),parentNode??expr,{path:fullPath,availableProperties});return undefined}return resolved}function resolveRootWithIdentifier(identifier,ctx,node){if(!ctx.identifierSchemas){addDiagnostic(ctx,"MISSING_IDENTIFIER_SCHEMAS","error",`Property "$root:${identifier}" uses an identifier but no identifier schemas were provided`,node,{path:`$root:${identifier}`,identifier});return undefined}const idSchema=ctx.identifierSchemas[identifier];if(!idSchema){addDiagnostic(ctx,"UNKNOWN_IDENTIFIER","error",`Property "$root:${identifier}" references identifier ${identifier} but no schema exists for this identifier`,node,{path:`$root:${identifier}`,identifier});return undefined}return idSchema}function resolveWithIdentifier(cleanSegments,identifier,ctx,node){const fullPath=cleanSegments.join(".");if(!ctx.identifierSchemas){addDiagnostic(ctx,"MISSING_IDENTIFIER_SCHEMAS","error",`Property "${fullPath}:${identifier}" uses an identifier but no identifier schemas were provided`,node,{path:`${fullPath}:${identifier}`,identifier});return undefined}const idSchema=ctx.identifierSchemas[identifier];if(!idSchema){addDiagnostic(ctx,"UNKNOWN_IDENTIFIER","error",`Property "${fullPath}:${identifier}" references identifier ${identifier} but no schema exists for this identifier`,node,{path:`${fullPath}:${identifier}`,identifier});return undefined}const resolved=(0,_schemaresolver.resolveSchemaPath)(idSchema,cleanSegments);if(resolved===undefined){const availableProperties=(0,_utils.getSchemaPropertyNames)(idSchema);addDiagnostic(ctx,"IDENTIFIER_PROPERTY_NOT_FOUND","error",`Property "${fullPath}" does not exist in the schema for identifier ${identifier}`,node,{path:fullPath,identifier,availableProperties});return undefined}return resolved}function resolveSubExpression(expr,ctx,parentNode){const helperName=getExpressionName(expr.path);const helper=ctx.helpers?.get(helperName);if(!helper){addDiagnostic(ctx,"UNKNOWN_HELPER","warning",`Unknown sub-expression helper "${helperName}" — cannot analyze statically`,parentNode??expr,{helperName});return{type:"string"}}const helperParams=helper.params;if(helperParams){const requiredCount=helperParams.filter(p=>!p.optional).length;if(expr.params.length<requiredCount){addDiagnostic(ctx,"MISSING_ARGUMENT","error",`Helper "${helperName}" expects at least ${requiredCount} argument(s), but got ${expr.params.length}`,parentNode??expr,{helperName,expected:`${requiredCount} argument(s)`,actual:`${expr.params.length} argument(s)`})}}for(let i=0;i<expr.params.length;i++){const resolvedSchema=resolveExpressionWithDiagnostics(expr.params[i],ctx,parentNode??expr);const helperParam=helperParams?.[i];if(resolvedSchema&&helperParam?.type){const expectedType=helperParam.type;if(!isParamTypeCompatible(resolvedSchema,expectedType)){const paramName=helperParam.name;addDiagnostic(ctx,"TYPE_MISMATCH","error",`Helper "${helperName}" parameter "${paramName}" expects ${schemaTypeLabel(expectedType)}, but got ${schemaTypeLabel(resolvedSchema)}`,parentNode??expr,{helperName,expected:schemaTypeLabel(expectedType),actual:schemaTypeLabel(resolvedSchema)})}}}return helper.returnType??{type:"string"}}function getBlockArgument(stmt){return stmt.params[0]}function getBlockHelperName(stmt){if(stmt.path.type==="PathExpression"){return stmt.path.original}return""}function getExpressionName(expr){if(expr.type==="PathExpression"){return expr.original}return""}function addDiagnostic(ctx,code,severity,message,node,details){const diagnostic={severity,code,message};if(node&&"loc"in node&&node.loc){diagnostic.loc={start:{line:node.loc.start.line,column:node.loc.start.column},end:{line:node.loc.end.line,column:node.loc.end.column}};diagnostic.source=(0,_utils.extractSourceSnippet)(ctx.template,diagnostic.loc)}if(details){diagnostic.details=details}ctx.diagnostics.push(diagnostic)}function schemaTypeLabel(schema){if(schema.type){return Array.isArray(schema.type)?schema.type.join(" | "):schema.type}if(schema.oneOf)return"oneOf(...)";if(schema.anyOf)return"anyOf(...)";if(schema.allOf)return"allOf(...)";if(schema.enum)return"enum";return"unknown"}
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});function _export(target,all){for(var name in all)Object.defineProperty(target,name,{enumerable:true,get:Object.getOwnPropertyDescriptor(all,name).get})}_export(exports,{get analyze(){return analyze},get analyzeFromAst(){return analyzeFromAst},get inferBlockType(){return inferBlockType}});const _dispatchts=require("./dispatch.js");const _errors=require("./errors.js");const _collectionhelpersts=require("./helpers/collection-helpers.js");const _parser=require("./parser.js");const _schemaresolver=require("./schema-resolver.js");const _utils=require("./utils.js");function analyze(template,inputSchema={},options){return(0,_dispatchts.dispatchAnalyze)(template,options,(tpl,coerceSchema)=>{const ast=(0,_parser.parse)(tpl);return analyzeFromAst(ast,tpl,inputSchema,{identifierSchemas:options?.identifierSchemas,coerceSchema})},(child,childOptions)=>analyze(child,inputSchema,childOptions))}function analyzeFromAst(ast,template,inputSchema={},options){(0,_schemaresolver.assertNoConditionalSchema)(inputSchema);if(options?.identifierSchemas){for(const[id,idSchema]of Object.entries(options.identifierSchemas)){(0,_schemaresolver.assertNoConditionalSchema)(idSchema,`/identifierSchemas/${id}`)}}const ctx={root:inputSchema,current:inputSchema,diagnostics:[],template,identifierSchemas:options?.identifierSchemas,helpers:options?.helpers,coerceSchema:options?.coerceSchema};const outputSchema=inferProgramType(ast,ctx);const hasErrors=ctx.diagnostics.some(d=>d.severity==="error");return{valid:!hasErrors,diagnostics:ctx.diagnostics,outputSchema:(0,_schemaresolver.simplifySchema)(outputSchema)}}function processStatement(stmt,ctx){switch(stmt.type){case"ContentStatement":case"CommentStatement":return undefined;case"MustacheStatement":return processMustache(stmt,ctx);case"BlockStatement":return inferBlockType(stmt,ctx);default:addDiagnostic(ctx,"UNANALYZABLE","warning",`Unsupported AST node type: "${stmt.type}"`,stmt);return undefined}}function processMustache(stmt,ctx){if(stmt.path.type==="SubExpression"){addDiagnostic(ctx,"UNANALYZABLE","warning","Sub-expressions are not statically analyzable",stmt);return{}}if(stmt.params.length>0||stmt.hash){const helperName=getExpressionName(stmt.path);if(helperName===_collectionhelpersts.CollectionHelpers.COLLECT_HELPER_NAME){return processCollectHelper(stmt,ctx)}const helper=ctx.helpers?.get(helperName);if(helper){const helperParams=helper.params;if(helperParams){const requiredCount=helperParams.filter(p=>!p.optional).length;if(stmt.params.length<requiredCount){addDiagnostic(ctx,"MISSING_ARGUMENT","error",`Helper "${helperName}" expects at least ${requiredCount} argument(s), but got ${stmt.params.length}`,stmt,{helperName,expected:`${requiredCount} argument(s)`,actual:`${stmt.params.length} argument(s)`})}}for(let i=0;i<stmt.params.length;i++){const resolvedSchema=resolveExpressionWithDiagnostics(stmt.params[i],ctx,stmt);const helperParam=helperParams?.[i];if(resolvedSchema&&helperParam?.type){const expectedType=helperParam.type;if(!isParamTypeCompatible(resolvedSchema,expectedType)){const paramName=helperParam.name;addDiagnostic(ctx,"TYPE_MISMATCH","error",`Helper "${helperName}" parameter "${paramName}" expects ${schemaTypeLabel(expectedType)}, but got ${schemaTypeLabel(resolvedSchema)}`,stmt,{helperName,expected:schemaTypeLabel(expectedType),actual:schemaTypeLabel(resolvedSchema)})}}}return helper.returnType??{type:"string"}}addDiagnostic(ctx,"UNKNOWN_HELPER","warning",`Unknown inline helper "${helperName}" — cannot analyze statically`,stmt,{helperName});return{type:"string"}}return resolveExpressionWithDiagnostics(stmt.path,ctx,stmt)??{}}function processCollectHelper(stmt,ctx){const helperName=_collectionhelpersts.CollectionHelpers.COLLECT_HELPER_NAME;if(stmt.params.length<2){addDiagnostic(ctx,"MISSING_ARGUMENT","error",`Helper "${helperName}" expects at least 2 argument(s), but got ${stmt.params.length}`,stmt,{helperName,expected:"2 argument(s)",actual:`${stmt.params.length} argument(s)`});return{type:"array"}}const collectionExpr=stmt.params[0];const collectionSchema=resolveExpressionWithDiagnostics(collectionExpr,ctx,stmt);if(!collectionSchema){return{type:"array"}}const itemSchema=(0,_schemaresolver.resolveArrayItems)(collectionSchema,ctx.root);if(!itemSchema){addDiagnostic(ctx,"TYPE_MISMATCH","error",`Helper "${helperName}" parameter "collection" expects an array, but got ${schemaTypeLabel(collectionSchema)}`,stmt,{helperName,expected:"array",actual:schemaTypeLabel(collectionSchema)});return{type:"array"}}let effectiveItemSchema=itemSchema;const itemType=effectiveItemSchema.type;if(itemType==="array"||Array.isArray(itemType)&&itemType.includes("array")){const innerItems=(0,_schemaresolver.resolveArrayItems)(effectiveItemSchema,ctx.root);if(innerItems){effectiveItemSchema=innerItems}}const effectiveItemType=effectiveItemSchema.type;const isObject=effectiveItemType==="object"||Array.isArray(effectiveItemType)&&effectiveItemType.includes("object")||!effectiveItemType&&effectiveItemSchema.properties!==undefined;if(!isObject&&effectiveItemType!==undefined){addDiagnostic(ctx,"TYPE_MISMATCH","error",`Helper "${helperName}" expects an array of objects, but the array items have type "${schemaTypeLabel(effectiveItemSchema)}"`,stmt,{helperName,expected:"object",actual:schemaTypeLabel(effectiveItemSchema)});return{type:"array"}}const propertyExpr=stmt.params[1];let propertyName;if(propertyExpr.type==="PathExpression"){const bare=propertyExpr.original;addDiagnostic(ctx,"TYPE_MISMATCH","error",`Helper "${helperName}" parameter "property" must be a quoted string. `+`Use {{ ${helperName} … "${bare}" }} instead of {{ ${helperName} … ${bare} }}`,stmt,{helperName,expected:'StringLiteral (e.g. "property")',actual:`PathExpression (${bare})`});return{type:"array"}}if(propertyExpr.type==="StringLiteral"){propertyName=propertyExpr.value}if(!propertyName){addDiagnostic(ctx,"TYPE_MISMATCH","error",`Helper "${helperName}" parameter "property" expects a quoted string literal, but got ${propertyExpr.type}`,stmt,{helperName,expected:'StringLiteral (e.g. "property")',actual:propertyExpr.type});return{type:"array"}}const propertySchema=(0,_schemaresolver.resolveSchemaPath)(effectiveItemSchema,[propertyName]);if(!propertySchema){const availableProperties=(0,_utils.getSchemaPropertyNames)(effectiveItemSchema);addDiagnostic(ctx,"UNKNOWN_PROPERTY","error",(0,_errors.createPropertyNotFoundMessage)(propertyName,availableProperties),stmt,{path:propertyName,availableProperties});return{type:"array"}}return{type:"array",items:propertySchema}}function isParamTypeCompatible(resolved,expected){if(!expected.type||!resolved.type)return true;const expectedTypes=Array.isArray(expected.type)?expected.type:[expected.type];const resolvedTypes=Array.isArray(resolved.type)?resolved.type:[resolved.type];return resolvedTypes.some(rt=>expectedTypes.some(et=>rt===et||et==="number"&&rt==="integer"||et==="integer"&&rt==="number"))}function inferProgramType(program,ctx){const effective=(0,_parser.getEffectiveBody)(program);if(effective.length===0){return{type:"string"}}const singleExpr=(0,_parser.getEffectivelySingleExpression)(program);if(singleExpr){return processMustache(singleExpr,ctx)}const singleBlock=(0,_parser.getEffectivelySingleBlock)(program);if(singleBlock){return inferBlockType(singleBlock,ctx)}const allContent=effective.every(s=>s.type==="ContentStatement");if(allContent){const text=effective.map(s=>s.value).join("").trim();if(text==="")return{type:"string"};const coercedType=ctx.coerceSchema?.type;if(typeof coercedType==="string"&&(coercedType==="string"||coercedType==="number"||coercedType==="integer"||coercedType==="boolean"||coercedType==="null")){return{type:coercedType}}const literalType=(0,_parser.detectLiteralType)(text);if(literalType)return{type:literalType}}const allBlocks=effective.every(s=>s.type==="BlockStatement");if(allBlocks){const types=[];for(const stmt of effective){const t=inferBlockType(stmt,ctx);if(t)types.push(t)}if(types.length===1)return types[0];if(types.length>1)return(0,_schemaresolver.simplifySchema)({oneOf:types});return{type:"string"}}for(const stmt of program.body){processStatement(stmt,ctx)}return{type:"string"}}function inferBlockType(stmt,ctx){const helperName=getBlockHelperName(stmt);switch(helperName){case"if":case"unless":{const arg=getBlockArgument(stmt);if(arg){resolveExpressionWithDiagnostics(arg,ctx,stmt)}else{addDiagnostic(ctx,"MISSING_ARGUMENT","error",(0,_errors.createMissingArgumentMessage)(helperName),stmt,{helperName})}const thenType=inferProgramType(stmt.program,ctx);if(stmt.inverse){const elseType=inferProgramType(stmt.inverse,ctx);if((0,_utils.deepEqual)(thenType,elseType))return thenType;return(0,_schemaresolver.simplifySchema)({oneOf:[thenType,elseType]})}return thenType}case"each":{const arg=getBlockArgument(stmt);if(!arg){addDiagnostic(ctx,"MISSING_ARGUMENT","error",(0,_errors.createMissingArgumentMessage)("each"),stmt,{helperName:"each"});const saved=ctx.current;ctx.current={};inferProgramType(stmt.program,ctx);ctx.current=saved;if(stmt.inverse)inferProgramType(stmt.inverse,ctx);return{type:"string"}}const collectionSchema=resolveExpressionWithDiagnostics(arg,ctx,stmt);if(!collectionSchema){const saved=ctx.current;ctx.current={};inferProgramType(stmt.program,ctx);ctx.current=saved;if(stmt.inverse)inferProgramType(stmt.inverse,ctx);return{type:"string"}}const itemSchema=(0,_schemaresolver.resolveArrayItems)(collectionSchema,ctx.root);if(!itemSchema){addDiagnostic(ctx,"TYPE_MISMATCH","error",(0,_errors.createTypeMismatchMessage)("each","an array",schemaTypeLabel(collectionSchema)),stmt,{helperName:"each",expected:"array",actual:schemaTypeLabel(collectionSchema)});const saved=ctx.current;ctx.current={};inferProgramType(stmt.program,ctx);ctx.current=saved;if(stmt.inverse)inferProgramType(stmt.inverse,ctx);return{type:"string"}}const saved=ctx.current;ctx.current=itemSchema;inferProgramType(stmt.program,ctx);ctx.current=saved;if(stmt.inverse)inferProgramType(stmt.inverse,ctx);return{type:"string"}}case"with":{const arg=getBlockArgument(stmt);if(!arg){addDiagnostic(ctx,"MISSING_ARGUMENT","error",(0,_errors.createMissingArgumentMessage)("with"),stmt,{helperName:"with"});const saved=ctx.current;ctx.current={};const result=inferProgramType(stmt.program,ctx);ctx.current=saved;if(stmt.inverse)inferProgramType(stmt.inverse,ctx);return result}const innerSchema=resolveExpressionWithDiagnostics(arg,ctx,stmt);const saved=ctx.current;ctx.current=innerSchema??{};const result=inferProgramType(stmt.program,ctx);ctx.current=saved;if(stmt.inverse)inferProgramType(stmt.inverse,ctx);return result}default:{const helper=ctx.helpers?.get(helperName);if(helper){for(const param of stmt.params){resolveExpressionWithDiagnostics(param,ctx,stmt)}inferProgramType(stmt.program,ctx);if(stmt.inverse)inferProgramType(stmt.inverse,ctx);return helper.returnType??{type:"string"}}addDiagnostic(ctx,"UNKNOWN_HELPER","warning",(0,_errors.createUnknownHelperMessage)(helperName),stmt,{helperName});inferProgramType(stmt.program,ctx);if(stmt.inverse)inferProgramType(stmt.inverse,ctx);return{type:"string"}}}}function resolveExpressionWithDiagnostics(expr,ctx,parentNode){if((0,_parser.isThisExpression)(expr)){return ctx.current}if(expr.type==="SubExpression"){return resolveSubExpression(expr,ctx,parentNode)}const segments=(0,_parser.extractPathSegments)(expr);if(segments.length===0){if(expr.type==="StringLiteral")return{type:"string"};if(expr.type==="NumberLiteral")return{type:"number"};if(expr.type==="BooleanLiteral")return{type:"boolean"};if(expr.type==="NullLiteral")return{type:"null"};if(expr.type==="UndefinedLiteral")return{};addDiagnostic(ctx,"UNANALYZABLE","warning",(0,_errors.createUnanalyzableMessage)(expr.type),parentNode??expr);return undefined}const{cleanSegments,identifier}=(0,_parser.extractExpressionIdentifier)(segments);if((0,_parser.isRootPathTraversal)(cleanSegments)){const fullPath=cleanSegments.join(".");addDiagnostic(ctx,"ROOT_PATH_TRAVERSAL","error",(0,_errors.createRootPathTraversalMessage)(fullPath),parentNode??expr,{path:fullPath});return undefined}if((0,_parser.isRootSegments)(cleanSegments)){if(identifier!==null){return resolveRootWithIdentifier(identifier,ctx,parentNode??expr)}return ctx.current}if(identifier!==null){return resolveWithIdentifier(cleanSegments,identifier,ctx,parentNode??expr)}const resolved=(0,_schemaresolver.resolveSchemaPath)(ctx.current,cleanSegments);if(resolved===undefined){const fullPath=cleanSegments.join(".");const availableProperties=(0,_utils.getSchemaPropertyNames)(ctx.current);addDiagnostic(ctx,"UNKNOWN_PROPERTY","error",(0,_errors.createPropertyNotFoundMessage)(fullPath,availableProperties),parentNode??expr,{path:fullPath,availableProperties});return undefined}return resolved}function resolveRootWithIdentifier(identifier,ctx,node){if(!ctx.identifierSchemas){addDiagnostic(ctx,"MISSING_IDENTIFIER_SCHEMAS","error",`Property "$root:${identifier}" uses an identifier but no identifier schemas were provided`,node,{path:`$root:${identifier}`,identifier});return undefined}const idSchema=ctx.identifierSchemas[identifier];if(!idSchema){addDiagnostic(ctx,"UNKNOWN_IDENTIFIER","error",`Property "$root:${identifier}" references identifier ${identifier} but no schema exists for this identifier`,node,{path:`$root:${identifier}`,identifier});return undefined}return idSchema}function resolveWithIdentifier(cleanSegments,identifier,ctx,node){const fullPath=cleanSegments.join(".");if(!ctx.identifierSchemas){addDiagnostic(ctx,"MISSING_IDENTIFIER_SCHEMAS","error",`Property "${fullPath}:${identifier}" uses an identifier but no identifier schemas were provided`,node,{path:`${fullPath}:${identifier}`,identifier});return undefined}const idSchema=ctx.identifierSchemas[identifier];if(!idSchema){addDiagnostic(ctx,"UNKNOWN_IDENTIFIER","error",`Property "${fullPath}:${identifier}" references identifier ${identifier} but no schema exists for this identifier`,node,{path:`${fullPath}:${identifier}`,identifier});return undefined}const resolved=(0,_schemaresolver.resolveSchemaPath)(idSchema,cleanSegments);if(resolved===undefined){const availableProperties=(0,_utils.getSchemaPropertyNames)(idSchema);addDiagnostic(ctx,"IDENTIFIER_PROPERTY_NOT_FOUND","error",`Property "${fullPath}" does not exist in the schema for identifier ${identifier}`,node,{path:fullPath,identifier,availableProperties});return undefined}return resolved}function resolveSubExpression(expr,ctx,parentNode){const helperName=getExpressionName(expr.path);if(helperName===_collectionhelpersts.CollectionHelpers.COLLECT_HELPER_NAME){return processCollectSubExpression(expr,ctx,parentNode)}const helper=ctx.helpers?.get(helperName);if(!helper){addDiagnostic(ctx,"UNKNOWN_HELPER","warning",`Unknown sub-expression helper "${helperName}" — cannot analyze statically`,parentNode??expr,{helperName});return{type:"string"}}const helperParams=helper.params;if(helperParams){const requiredCount=helperParams.filter(p=>!p.optional).length;if(expr.params.length<requiredCount){addDiagnostic(ctx,"MISSING_ARGUMENT","error",`Helper "${helperName}" expects at least ${requiredCount} argument(s), but got ${expr.params.length}`,parentNode??expr,{helperName,expected:`${requiredCount} argument(s)`,actual:`${expr.params.length} argument(s)`})}}for(let i=0;i<expr.params.length;i++){const resolvedSchema=resolveExpressionWithDiagnostics(expr.params[i],ctx,parentNode??expr);const helperParam=helperParams?.[i];if(resolvedSchema&&helperParam?.type){const expectedType=helperParam.type;if(!isParamTypeCompatible(resolvedSchema,expectedType)){const paramName=helperParam.name;addDiagnostic(ctx,"TYPE_MISMATCH","error",`Helper "${helperName}" parameter "${paramName}" expects ${schemaTypeLabel(expectedType)}, but got ${schemaTypeLabel(resolvedSchema)}`,parentNode??expr,{helperName,expected:schemaTypeLabel(expectedType),actual:schemaTypeLabel(resolvedSchema)})}}}return helper.returnType??{type:"string"}}function processCollectSubExpression(expr,ctx,parentNode){const helperName=_collectionhelpersts.CollectionHelpers.COLLECT_HELPER_NAME;const node=parentNode??expr;if(expr.params.length<2){addDiagnostic(ctx,"MISSING_ARGUMENT","error",`Helper "${helperName}" expects at least 2 argument(s), but got ${expr.params.length}`,node,{helperName,expected:"2 argument(s)",actual:`${expr.params.length} argument(s)`});return{type:"array"}}const collectionExpr=expr.params[0];const collectionSchema=resolveExpressionWithDiagnostics(collectionExpr,ctx,node);if(!collectionSchema){return{type:"array"}}const itemSchema=(0,_schemaresolver.resolveArrayItems)(collectionSchema,ctx.root);if(!itemSchema){addDiagnostic(ctx,"TYPE_MISMATCH","error",`Helper "${helperName}" parameter "collection" expects an array, but got ${schemaTypeLabel(collectionSchema)}`,node,{helperName,expected:"array",actual:schemaTypeLabel(collectionSchema)});return{type:"array"}}let effectiveItemSchema=itemSchema;const itemType=effectiveItemSchema.type;if(itemType==="array"||Array.isArray(itemType)&&itemType.includes("array")){const innerItems=(0,_schemaresolver.resolveArrayItems)(effectiveItemSchema,ctx.root);if(innerItems){effectiveItemSchema=innerItems}}const effectiveItemType=effectiveItemSchema.type;const isObject=effectiveItemType==="object"||Array.isArray(effectiveItemType)&&effectiveItemType.includes("object")||!effectiveItemType&&effectiveItemSchema.properties!==undefined;if(!isObject&&effectiveItemType!==undefined){addDiagnostic(ctx,"TYPE_MISMATCH","error",`Helper "${helperName}" expects an array of objects, but the array items have type "${schemaTypeLabel(effectiveItemSchema)}"`,node,{helperName,expected:"object",actual:schemaTypeLabel(effectiveItemSchema)});return{type:"array"}}const propertyExpr=expr.params[1];let propertyName;if(propertyExpr.type==="PathExpression"){const bare=propertyExpr.original;addDiagnostic(ctx,"TYPE_MISMATCH","error",`Helper "${helperName}" parameter "property" must be a quoted string. `+`Use (${helperName} … "${bare}") instead of (${helperName} … ${bare})`,node,{helperName,expected:'StringLiteral (e.g. "property")',actual:`PathExpression (${bare})`});return{type:"array"}}if(propertyExpr.type==="StringLiteral"){propertyName=propertyExpr.value}if(!propertyName){addDiagnostic(ctx,"TYPE_MISMATCH","error",`Helper "${helperName}" parameter "property" expects a quoted string literal, but got ${propertyExpr.type}`,node,{helperName,expected:'StringLiteral (e.g. "property")',actual:propertyExpr.type});return{type:"array"}}const propertySchema=(0,_schemaresolver.resolveSchemaPath)(effectiveItemSchema,[propertyName]);if(!propertySchema){const availableProperties=(0,_utils.getSchemaPropertyNames)(effectiveItemSchema);addDiagnostic(ctx,"UNKNOWN_PROPERTY","error",(0,_errors.createPropertyNotFoundMessage)(propertyName,availableProperties),node,{path:propertyName,availableProperties});return{type:"array"}}return{type:"array",items:propertySchema}}function getBlockArgument(stmt){return stmt.params[0]}function getBlockHelperName(stmt){if(stmt.path.type==="PathExpression"){return stmt.path.original}return""}function getExpressionName(expr){if(expr.type==="PathExpression"){return expr.original}return""}function addDiagnostic(ctx,code,severity,message,node,details){const diagnostic={severity,code,message};if(node&&"loc"in node&&node.loc){diagnostic.loc={start:{line:node.loc.start.line,column:node.loc.start.column},end:{line:node.loc.end.line,column:node.loc.end.column}};diagnostic.source=(0,_utils.extractSourceSnippet)(ctx.template,diagnostic.loc)}if(details){diagnostic.details=details}ctx.diagnostics.push(diagnostic)}function schemaTypeLabel(schema){if(schema.type){return Array.isArray(schema.type)?schema.type.join(" | "):schema.type}if(schema.oneOf)return"oneOf(...)";if(schema.anyOf)return"anyOf(...)";if(schema.allOf)return"allOf(...)";if(schema.enum)return"enum";return"unknown"}
2
2
  //# sourceMappingURL=analyzer.js.map
@@ -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 {\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/**\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// 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/**\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\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\treturn { type: coercedType };\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, collect\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\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\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":["analyze","analyzeFromAst","inferBlockType","template","inputSchema","options","dispatchAnalyze","tpl","coerceSchema","ast","parse","identifierSchemas","child","childOptions","assertNoConditionalSchema","id","idSchema","Object","entries","ctx","root","current","diagnostics","helpers","outputSchema","inferProgramType","hasErrors","some","d","severity","valid","simplifySchema","processStatement","stmt","type","undefined","processMustache","addDiagnostic","path","params","length","hash","helperName","getExpressionName","helper","get","helperParams","requiredCount","filter","p","optional","expected","actual","i","resolvedSchema","resolveExpressionWithDiagnostics","helperParam","expectedType","isParamTypeCompatible","paramName","name","schemaTypeLabel","returnType","resolved","expectedTypes","Array","isArray","resolvedTypes","rt","et","program","effective","getEffectiveBody","singleExpr","getEffectivelySingleExpression","singleBlock","getEffectivelySingleBlock","allContent","every","s","text","map","value","join","trim","coercedType","literalType","detectLiteralType","allBlocks","types","t","push","oneOf","body","getBlockHelperName","arg","getBlockArgument","createMissingArgumentMessage","thenType","inverse","elseType","deepEqual","saved","collectionSchema","itemSchema","resolveArrayItems","createTypeMismatchMessage","result","innerSchema","param","createUnknownHelperMessage","expr","parentNode","isThisExpression","resolveSubExpression","segments","extractPathSegments","createUnanalyzableMessage","cleanSegments","identifier","extractExpressionIdentifier","isRootPathTraversal","fullPath","createRootPathTraversalMessage","isRootSegments","resolveRootWithIdentifier","resolveWithIdentifier","resolveSchemaPath","availableProperties","getSchemaPropertyNames","createPropertyNotFoundMessage","node","original","code","message","details","diagnostic","loc","start","line","column","end","source","extractSourceSnippet","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 { CollectionHelpers } from \"./helpers/collection-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/**\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: collect helper ─────────────────────────────────\n\t\t// The `collect` 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 === CollectionHelpers.COLLECT_HELPER_NAME) {\n\t\t\treturn processCollectHelper(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// ─── collect helper — special-case analysis ──────────────────────────────────\n// Validates the arguments and infers the precise return type:\n// {{ collect <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 processCollectHelper(\n\tstmt: hbs.AST.MustacheStatement,\n\tctx: AnalysisContext,\n): JSONSchema7 {\n\tconst helperName = CollectionHelpers.COLLECT_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 collect), 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\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\treturn { type: coercedType };\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, collect\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: collect helper ─────────────────────────────────\n\t// The `collect` 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 collect calls.\n\tif (helperName === CollectionHelpers.COLLECT_HELPER_NAME) {\n\t\treturn processCollectSubExpression(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// ─── collect helper — sub-expression analysis ────────────────────────────────\n// Mirrors processCollectHelper but for SubExpression nodes (e.g.\n// `(collect users 'cartItems')` used as an argument to another helper).\n// This enables nested collect: `{{ collect (collect users 'cartItems') 'productId' }}`\n\nfunction processCollectSubExpression(\n\texpr: hbs.AST.SubExpression,\n\tctx: AnalysisContext,\n\tparentNode?: hbs.AST.Node,\n): JSONSchema7 {\n\tconst helperName = CollectionHelpers.COLLECT_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 collect), 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":["analyze","analyzeFromAst","inferBlockType","template","inputSchema","options","dispatchAnalyze","tpl","coerceSchema","ast","parse","identifierSchemas","child","childOptions","assertNoConditionalSchema","id","idSchema","Object","entries","ctx","root","current","diagnostics","helpers","outputSchema","inferProgramType","hasErrors","some","d","severity","valid","simplifySchema","processStatement","stmt","type","undefined","processMustache","addDiagnostic","path","params","length","hash","helperName","getExpressionName","CollectionHelpers","COLLECT_HELPER_NAME","processCollectHelper","helper","get","helperParams","requiredCount","filter","p","optional","expected","actual","i","resolvedSchema","resolveExpressionWithDiagnostics","helperParam","expectedType","isParamTypeCompatible","paramName","name","schemaTypeLabel","returnType","collectionExpr","collectionSchema","itemSchema","resolveArrayItems","effectiveItemSchema","itemType","Array","isArray","includes","innerItems","effectiveItemType","isObject","properties","propertyExpr","propertyName","bare","original","value","propertySchema","resolveSchemaPath","availableProperties","getSchemaPropertyNames","createPropertyNotFoundMessage","items","resolved","expectedTypes","resolvedTypes","rt","et","program","effective","getEffectiveBody","singleExpr","getEffectivelySingleExpression","singleBlock","getEffectivelySingleBlock","allContent","every","s","text","map","join","trim","coercedType","literalType","detectLiteralType","allBlocks","types","t","push","oneOf","body","getBlockHelperName","arg","getBlockArgument","createMissingArgumentMessage","thenType","inverse","elseType","deepEqual","saved","createTypeMismatchMessage","result","innerSchema","param","createUnknownHelperMessage","expr","parentNode","isThisExpression","resolveSubExpression","segments","extractPathSegments","createUnanalyzableMessage","cleanSegments","identifier","extractExpressionIdentifier","isRootPathTraversal","fullPath","createRootPathTraversalMessage","isRootSegments","resolveRootWithIdentifier","resolveWithIdentifier","node","processCollectSubExpression","code","message","details","diagnostic","loc","start","line","column","end","source","extractSourceSnippet","schema","anyOf","allOf","enum"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});Object.defineProperty(exports,"CompiledTemplate",{enumerable:true,get:function(){return CompiledTemplate}});const _analyzerts=require("./analyzer.js");const _dispatchts=require("./dispatch.js");const _errorsts=require("./errors.js");const _executorts=require("./executor.js");const _typests=require("./types.js");const _utils=require("./utils.js");function _define_property(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true})}else{obj[key]=value}return obj}class CompiledTemplate{get ast(){return this.state.kind==="template"?this.state.ast:null}get template(){return this.state.kind==="template"?this.state.source:""}static fromTemplate(ast,source,options){return new CompiledTemplate({kind:"template",ast,source},options)}static fromLiteral(value,options){return new CompiledTemplate({kind:"literal",value},options)}static fromObject(children,options){return new CompiledTemplate({kind:"object",children},options)}static fromArray(elements,options){return new CompiledTemplate({kind:"array",elements},options)}analyze(inputSchema={},options){const exclude=options?.excludeTemplateExpression===true;switch(this.state.kind){case"array":{const{elements}=this.state;if(exclude){const kept=elements.filter(el=>!isCompiledTemplateWithExpression(el));return(0,_utils.aggregateArrayAnalysis)(kept.length,index=>{const element=kept[index];if(!element)throw new Error(`unreachable: missing element at index ${index}`);return element.analyze(inputSchema,options)})}return(0,_utils.aggregateArrayAnalysis)(elements.length,index=>{const element=elements[index];if(!element)throw new Error(`unreachable: missing element at index ${index}`);return element.analyze(inputSchema,options)})}case"object":{const{children}=this.state;const coerceSchema=options?.coerceSchema;const keys=exclude?Object.keys(children).filter(key=>{const child=children[key];return!child||!isCompiledTemplateWithExpression(child)}):Object.keys(children);return(0,_utils.aggregateObjectAnalysis)(keys,key=>{const child=children[key];if(!child)throw new Error(`unreachable: missing child "${key}"`);const childCoerceSchema=(0,_dispatchts.resolveChildCoerceSchema)(coerceSchema,key);return child.analyze(inputSchema,{identifierSchemas:options?.identifierSchemas,coerceSchema:childCoerceSchema,excludeTemplateExpression:options?.excludeTemplateExpression})})}case"literal":return{valid:true,diagnostics:[],outputSchema:(0,_typests.inferPrimitiveSchema)(this.state.value)};case"template":return(0,_analyzerts.analyzeFromAst)(this.state.ast,this.state.source,inputSchema,{identifierSchemas:options?.identifierSchemas,helpers:this.options.helpers,coerceSchema:options?.coerceSchema})}}validate(inputSchema={},options){const analysis=this.analyze(inputSchema,options);return{valid:analysis.valid,diagnostics:analysis.diagnostics}}execute(data,options){switch(this.state.kind){case"array":{const{elements}=this.state;const result=[];for(const element of elements){result.push(element.execute(data,options))}return result}case"object":{const{children}=this.state;const coerceSchema=options?.coerceSchema;const result={};for(const[key,child]of Object.entries(children)){const childCoerceSchema=(0,_dispatchts.resolveChildCoerceSchema)(coerceSchema,key);result[key]=child.execute(data,{...options,coerceSchema:childCoerceSchema})}return result}case"literal":return this.state.value;case"template":{if(options?.schema){const analysis=this.analyze(options.schema,{identifierSchemas:options.identifierSchemas,coerceSchema:options.coerceSchema});if(!analysis.valid){throw new _errorsts.TemplateAnalysisError(analysis.diagnostics)}}return(0,_executorts.executeFromAst)(this.state.ast,this.state.source,data,this.buildExecutorContext(options))}}}analyzeAndExecute(inputSchema={},data,options){switch(this.state.kind){case"array":{const{elements}=this.state;return(0,_utils.aggregateArrayAnalysisAndExecution)(elements.length,index=>{const element=elements[index];if(!element)throw new Error(`unreachable: missing element at index ${index}`);return element.analyzeAndExecute(inputSchema,data,options)})}case"object":{const{children}=this.state;const coerceSchema=options?.coerceSchema;return(0,_utils.aggregateObjectAnalysisAndExecution)(Object.keys(children),key=>{const child=children[key];if(!child)throw new Error(`unreachable: missing child "${key}"`);const childCoerceSchema=(0,_dispatchts.resolveChildCoerceSchema)(coerceSchema,key);return child.analyzeAndExecute(inputSchema,data,{identifierSchemas:options?.identifierSchemas,identifierData:options?.identifierData,coerceSchema:childCoerceSchema})})}case"literal":return{analysis:{valid:true,diagnostics:[],outputSchema:(0,_typests.inferPrimitiveSchema)(this.state.value)},value:this.state.value};case"template":{const analysis=this.analyze(inputSchema,{identifierSchemas:options?.identifierSchemas,coerceSchema:options?.coerceSchema});if(!analysis.valid){return{analysis,value:undefined}}const value=(0,_executorts.executeFromAst)(this.state.ast,this.state.source,data,this.buildExecutorContext({identifierData:options?.identifierData,coerceSchema:options?.coerceSchema}));return{analysis,value}}}}buildExecutorContext(options){return{identifierData:options?.identifierData,compiledTemplate:this.getOrCompileHbs(),hbs:this.options.hbs,compilationCache:this.options.compilationCache,coerceSchema:options?.coerceSchema}}getOrCompileHbs(){if(!this.hbsCompiled){this.hbsCompiled=this.options.hbs.compile(this.template,{noEscape:true,strict:false})}return this.hbsCompiled}constructor(state,options){_define_property(this,"state",void 0);_define_property(this,"options",void 0);_define_property(this,"hbsCompiled",null);this.state=state;this.options=options}}function isCompiledTemplateWithExpression(ct){return ct.template!==""&&(0,_dispatchts.shouldExcludeEntry)(ct.template)}
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});Object.defineProperty(exports,"CompiledTemplate",{enumerable:true,get:function(){return CompiledTemplate}});const _analyzerts=require("./analyzer.js");const _dispatchts=require("./dispatch.js");const _errorsts=require("./errors.js");const _executorts=require("./executor.js");const _typests=require("./types.js");const _utils=require("./utils.js");function _define_property(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true})}else{obj[key]=value}return obj}class CompiledTemplate{get ast(){return this.state.kind==="template"?this.state.ast:null}get template(){return this.state.kind==="template"?this.state.source:""}static fromTemplate(ast,source,options){return new CompiledTemplate({kind:"template",ast,source},options)}static fromLiteral(value,options){return new CompiledTemplate({kind:"literal",value},options)}static fromObject(children,options){return new CompiledTemplate({kind:"object",children},options)}static fromArray(elements,options){return new CompiledTemplate({kind:"array",elements},options)}analyze(inputSchema={},options){const exclude=options?.excludeTemplateExpression===true;switch(this.state.kind){case"array":{const{elements}=this.state;if(exclude){const kept=elements.filter(el=>!isCompiledTemplateWithExpression(el));return(0,_utils.aggregateArrayAnalysis)(kept.length,index=>{const element=kept[index];if(!element)throw new Error(`unreachable: missing element at index ${index}`);return element.analyze(inputSchema,options)})}return(0,_utils.aggregateArrayAnalysis)(elements.length,index=>{const element=elements[index];if(!element)throw new Error(`unreachable: missing element at index ${index}`);return element.analyze(inputSchema,options)})}case"object":{const{children}=this.state;const coerceSchema=options?.coerceSchema;const keys=exclude?Object.keys(children).filter(key=>{const child=children[key];return!child||!isCompiledTemplateWithExpression(child)}):Object.keys(children);return(0,_utils.aggregateObjectAnalysis)(keys,key=>{const child=children[key];if(!child)throw new Error(`unreachable: missing child "${key}"`);const childCoerceSchema=(0,_dispatchts.resolveChildCoerceSchema)(coerceSchema,key);return child.analyze(inputSchema,{identifierSchemas:options?.identifierSchemas,coerceSchema:childCoerceSchema,excludeTemplateExpression:options?.excludeTemplateExpression})})}case"literal":return{valid:true,diagnostics:[],outputSchema:(0,_typests.inferPrimitiveSchema)(this.state.value)};case"template":return(0,_analyzerts.analyzeFromAst)(this.state.ast,this.state.source,inputSchema,{identifierSchemas:options?.identifierSchemas,helpers:this.options.helpers,coerceSchema:options?.coerceSchema})}}validate(inputSchema={},options){const analysis=this.analyze(inputSchema,options);return{valid:analysis.valid,diagnostics:analysis.diagnostics}}execute(data,options){switch(this.state.kind){case"array":{const{elements}=this.state;const result=[];for(const element of elements){result.push(element.execute(data,options))}return result}case"object":{const{children}=this.state;const coerceSchema=options?.coerceSchema;const result={};for(const[key,child]of Object.entries(children)){const childCoerceSchema=(0,_dispatchts.resolveChildCoerceSchema)(coerceSchema,key);result[key]=child.execute(data,{...options,coerceSchema:childCoerceSchema})}return result}case"literal":return this.state.value;case"template":{if(options?.schema){const analysis=this.analyze(options.schema,{identifierSchemas:options.identifierSchemas,coerceSchema:options.coerceSchema});if(!analysis.valid){throw new _errorsts.TemplateAnalysisError(analysis.diagnostics)}}return(0,_executorts.executeFromAst)(this.state.ast,this.state.source,data,this.buildExecutorContext(options))}}}analyzeAndExecute(inputSchema={},data,options){switch(this.state.kind){case"array":{const{elements}=this.state;return(0,_utils.aggregateArrayAnalysisAndExecution)(elements.length,index=>{const element=elements[index];if(!element)throw new Error(`unreachable: missing element at index ${index}`);return element.analyzeAndExecute(inputSchema,data,options)})}case"object":{const{children}=this.state;const coerceSchema=options?.coerceSchema;return(0,_utils.aggregateObjectAnalysisAndExecution)(Object.keys(children),key=>{const child=children[key];if(!child)throw new Error(`unreachable: missing child "${key}"`);const childCoerceSchema=(0,_dispatchts.resolveChildCoerceSchema)(coerceSchema,key);return child.analyzeAndExecute(inputSchema,data,{identifierSchemas:options?.identifierSchemas,identifierData:options?.identifierData,coerceSchema:childCoerceSchema})})}case"literal":return{analysis:{valid:true,diagnostics:[],outputSchema:(0,_typests.inferPrimitiveSchema)(this.state.value)},value:this.state.value};case"template":{const analysis=this.analyze(inputSchema,{identifierSchemas:options?.identifierSchemas,coerceSchema:options?.coerceSchema});if(!analysis.valid){return{analysis,value:undefined}}const value=(0,_executorts.executeFromAst)(this.state.ast,this.state.source,data,this.buildExecutorContext({identifierData:options?.identifierData,coerceSchema:options?.coerceSchema}));return{analysis,value}}}}buildExecutorContext(options){return{identifierData:options?.identifierData,compiledTemplate:this.getOrCompileHbs(),hbs:this.options.hbs,compilationCache:this.options.compilationCache,coerceSchema:options?.coerceSchema,helpers:this.options.helpers}}getOrCompileHbs(){if(!this.hbsCompiled){this.hbsCompiled=this.options.hbs.compile(this.template,{noEscape:true,strict:false})}return this.hbsCompiled}constructor(state,options){_define_property(this,"state",void 0);_define_property(this,"options",void 0);_define_property(this,"hbsCompiled",null);this.state=state;this.options=options}}function isCompiledTemplateWithExpression(ct){return ct.template!==""&&(0,_dispatchts.shouldExcludeEntry)(ct.template)}
2
2
  //# sourceMappingURL=compiled-template.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/compiled-template.ts"],"sourcesContent":["import type Handlebars from \"handlebars\";\nimport type { JSONSchema7 } from \"json-schema\";\nimport type { AnalyzeOptions } from \"./analyzer.ts\";\nimport { analyzeFromAst } from \"./analyzer.ts\";\nimport { resolveChildCoerceSchema, shouldExcludeEntry } from \"./dispatch.ts\";\nimport { TemplateAnalysisError } from \"./errors.ts\";\nimport { type ExecutorContext, executeFromAst } from \"./executor.ts\";\nimport type {\n\tAnalysisResult,\n\tExecuteOptions,\n\tHelperDefinition,\n\tTemplateData,\n\tValidationResult,\n} from \"./types.ts\";\nimport { inferPrimitiveSchema } from \"./types.ts\";\nimport {\n\taggregateArrayAnalysis,\n\taggregateArrayAnalysisAndExecution,\n\taggregateObjectAnalysis,\n\taggregateObjectAnalysisAndExecution,\n\ttype LRUCache,\n} from \"./utils\";\n\n// ─── CompiledTemplate ────────────────────────────────────────────────────────\n// Pre-parsed template ready to be executed or analyzed without re-parsing.\n//\n// The compile-once / execute-many pattern avoids the cost of Handlebars\n// parsing on every call. The AST is parsed once at compile time, and the\n// Handlebars template is lazily compiled on the first `execute()`.\n//\n// Usage:\n// const tpl = engine.compile(\"Hello {{name}}\");\n// tpl.execute({ name: \"Alice\" }); // no re-parsing\n// tpl.execute({ name: \"Bob\" }); // no re-parsing or recompilation\n// tpl.analyze(schema); // no re-parsing\n//\n// ─── Internal State (TemplateState) ──────────────────────────────────────────\n// CompiledTemplate operates in 4 exclusive modes, modeled by a discriminated\n// union `TemplateState`:\n//\n// - `\"template\"` — parsed Handlebars template (AST + source string)\n// - `\"literal\"` — primitive passthrough value (number, boolean, null)\n// - `\"object\"` — object where each property is a child CompiledTemplate\n// - `\"array\"` — array where each element is a child CompiledTemplate\n//\n// This design eliminates optional fields and `!` assertions in favor of\n// natural TypeScript narrowing via `switch (this.state.kind)`.\n//\n// ─── Advantages Over the Direct API ──────────────────────────────────────────\n// - **Performance**: parsing and compilation happen only once\n// - **Simplified API**: no need to re-pass the template string on each call\n// - **Consistency**: the same AST is used for both analysis and execution\n\n// ─── Internal Types ──────────────────────────────────────────────────────────\n\n/** Internal options passed by Typebars during compilation */\nexport interface CompiledTemplateOptions {\n\t/** Custom helpers registered on the engine */\n\thelpers: Map<string, HelperDefinition>;\n\t/** Isolated Handlebars environment (with registered helpers) */\n\thbs: typeof Handlebars;\n\t/** Compilation cache shared by the engine */\n\tcompilationCache: LRUCache<string, HandlebarsTemplateDelegate>;\n}\n\n/** Discriminated internal state of the CompiledTemplate */\ntype TemplateState =\n\t| {\n\t\t\treadonly kind: \"template\";\n\t\t\treadonly ast: hbs.AST.Program;\n\t\t\treadonly source: string;\n\t }\n\t| { readonly kind: \"literal\"; readonly value: number | boolean | null }\n\t| {\n\t\t\treadonly kind: \"object\";\n\t\t\treadonly children: Record<string, CompiledTemplate>;\n\t }\n\t| {\n\t\t\treadonly kind: \"array\";\n\t\t\treadonly elements: CompiledTemplate[];\n\t };\n\n// ─── Public Class ────────────────────────────────────────────────────────────\n\nexport class CompiledTemplate {\n\t/** Discriminated internal state */\n\tprivate readonly state: TemplateState;\n\n\t/** Options inherited from the parent Typebars instance */\n\tprivate readonly options: CompiledTemplateOptions;\n\n\t/** Compiled Handlebars template (lazy — created on the first `execute()` that needs it) */\n\tprivate hbsCompiled: HandlebarsTemplateDelegate | null = null;\n\n\t// ─── Public Accessors (backward-compatible) ──────────────────────────\n\n\t/** The pre-parsed Handlebars AST — `null` in literal, object, or array mode */\n\tget ast(): hbs.AST.Program | null {\n\t\treturn this.state.kind === \"template\" ? this.state.ast : null;\n\t}\n\n\t/** The original template source — empty string in literal, object, or array mode */\n\tget template(): string {\n\t\treturn this.state.kind === \"template\" ? this.state.source : \"\";\n\t}\n\n\t// ─── Construction ────────────────────────────────────────────────────\n\n\tprivate constructor(state: TemplateState, options: CompiledTemplateOptions) {\n\t\tthis.state = state;\n\t\tthis.options = options;\n\t}\n\n\t/**\n\t * Creates a CompiledTemplate for a parsed Handlebars template.\n\t *\n\t * @param ast - The pre-parsed Handlebars AST\n\t * @param source - The original template source\n\t * @param options - Options inherited from Typebars\n\t */\n\tstatic fromTemplate(\n\t\tast: hbs.AST.Program,\n\t\tsource: string,\n\t\toptions: CompiledTemplateOptions,\n\t): CompiledTemplate {\n\t\treturn new CompiledTemplate({ kind: \"template\", ast, source }, options);\n\t}\n\n\t/**\n\t * Creates a CompiledTemplate in passthrough mode for a literal value\n\t * (number, boolean, null). No parsing or compilation is performed.\n\t *\n\t * @param value - The primitive value\n\t * @param options - Options inherited from Typebars\n\t * @returns A CompiledTemplate that always returns `value`\n\t */\n\tstatic fromLiteral(\n\t\tvalue: number | boolean | null,\n\t\toptions: CompiledTemplateOptions,\n\t): CompiledTemplate {\n\t\treturn new CompiledTemplate({ kind: \"literal\", value }, options);\n\t}\n\n\t/**\n\t * Creates a CompiledTemplate in object mode, where each property is a\n\t * child CompiledTemplate. All operations are recursively delegated\n\t * to the children.\n\t *\n\t * @param children - The compiled child templates `{ [key]: CompiledTemplate }`\n\t * @param options - Options inherited from Typebars\n\t * @returns A CompiledTemplate that delegates to children\n\t */\n\tstatic fromObject(\n\t\tchildren: Record<string, CompiledTemplate>,\n\t\toptions: CompiledTemplateOptions,\n\t): CompiledTemplate {\n\t\treturn new CompiledTemplate({ kind: \"object\", children }, options);\n\t}\n\n\t/**\n\t * Creates a CompiledTemplate in array mode, where each element is a\n\t * child CompiledTemplate. All operations are recursively delegated\n\t * to the elements.\n\t *\n\t * @param elements - The compiled child templates (ordered array)\n\t * @param options - Options inherited from Typebars\n\t * @returns A CompiledTemplate that delegates to elements\n\t */\n\tstatic fromArray(\n\t\telements: CompiledTemplate[],\n\t\toptions: CompiledTemplateOptions,\n\t): CompiledTemplate {\n\t\treturn new CompiledTemplate({ kind: \"array\", elements }, options);\n\t}\n\n\t// ─── Static Analysis ─────────────────────────────────────────────────\n\n\t/**\n\t * Statically analyzes this template against a JSON Schema v7.\n\t *\n\t * Returns an `AnalysisResult` containing:\n\t * - `valid` — `true` if no errors\n\t * - `diagnostics` — list of diagnostics (errors + warnings)\n\t * - `outputSchema` — JSON Schema describing the return type\n\t *\n\t * Since the AST is pre-parsed, this method never re-parses the template.\n\t *\n\t * @param inputSchema - JSON Schema describing the available variables\n\t * @param options - (optional) Analysis options (identifierSchemas, coerceSchema)\n\t */\n\tanalyze(\n\t\tinputSchema: JSONSchema7 = {},\n\t\toptions?: AnalyzeOptions,\n\t): AnalysisResult {\n\t\tconst exclude = options?.excludeTemplateExpression === true;\n\n\t\tswitch (this.state.kind) {\n\t\t\tcase \"array\": {\n\t\t\t\tconst { elements } = this.state;\n\n\t\t\t\tif (exclude) {\n\t\t\t\t\t// When excludeTemplateExpression is enabled, filter out elements\n\t\t\t\t\t// that are string templates containing Handlebars expressions.\n\t\t\t\t\tconst kept = elements.filter(\n\t\t\t\t\t\t(el) => !isCompiledTemplateWithExpression(el),\n\t\t\t\t\t);\n\t\t\t\t\treturn aggregateArrayAnalysis(kept.length, (index) => {\n\t\t\t\t\t\tconst element = kept[index];\n\t\t\t\t\t\tif (!element)\n\t\t\t\t\t\t\tthrow new Error(`unreachable: missing element at index ${index}`);\n\t\t\t\t\t\treturn element.analyze(inputSchema, options);\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\treturn aggregateArrayAnalysis(elements.length, (index) => {\n\t\t\t\t\tconst element = elements[index];\n\t\t\t\t\tif (!element)\n\t\t\t\t\t\tthrow new Error(`unreachable: missing element at index ${index}`);\n\t\t\t\t\treturn element.analyze(inputSchema, options);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tcase \"object\": {\n\t\t\t\tconst { children } = this.state;\n\t\t\t\tconst coerceSchema = options?.coerceSchema;\n\n\t\t\t\t// When excludeTemplateExpression is enabled, filter out keys whose\n\t\t\t\t// compiled children are string templates with Handlebars expressions.\n\t\t\t\tconst keys = exclude\n\t\t\t\t\t? Object.keys(children).filter((key) => {\n\t\t\t\t\t\t\tconst child = children[key];\n\t\t\t\t\t\t\treturn !child || !isCompiledTemplateWithExpression(child);\n\t\t\t\t\t\t})\n\t\t\t\t\t: Object.keys(children);\n\n\t\t\t\treturn aggregateObjectAnalysis(keys, (key) => {\n\t\t\t\t\tconst child = children[key];\n\t\t\t\t\tif (!child) throw new Error(`unreachable: missing child \"${key}\"`);\n\t\t\t\t\tconst childCoerceSchema = resolveChildCoerceSchema(coerceSchema, key);\n\t\t\t\t\treturn child.analyze(inputSchema, {\n\t\t\t\t\t\tidentifierSchemas: options?.identifierSchemas,\n\t\t\t\t\t\tcoerceSchema: childCoerceSchema,\n\t\t\t\t\t\texcludeTemplateExpression: options?.excludeTemplateExpression,\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tcase \"literal\":\n\t\t\t\treturn {\n\t\t\t\t\tvalid: true,\n\t\t\t\t\tdiagnostics: [],\n\t\t\t\t\toutputSchema: inferPrimitiveSchema(this.state.value),\n\t\t\t\t};\n\n\t\t\tcase \"template\":\n\t\t\t\treturn analyzeFromAst(this.state.ast, this.state.source, inputSchema, {\n\t\t\t\t\tidentifierSchemas: options?.identifierSchemas,\n\t\t\t\t\thelpers: this.options.helpers,\n\t\t\t\t\tcoerceSchema: options?.coerceSchema,\n\t\t\t\t});\n\t\t}\n\t}\n\n\t// ─── Validation ──────────────────────────────────────────────────────\n\n\t/**\n\t * Validates the template against a schema without returning the output type.\n\t *\n\t * This is an API shortcut for `analyze()` that only returns `valid` and\n\t * `diagnostics`, without `outputSchema`. The full analysis (including type\n\t * inference) is executed internally — this method provides no performance\n\t * gain, only a simplified API.\n\t *\n\t * @param inputSchema - JSON Schema describing the available variables\n\t * @param options - (optional) Analysis options (identifierSchemas, coerceSchema)\n\t */\n\tvalidate(\n\t\tinputSchema: JSONSchema7 = {},\n\t\toptions?: AnalyzeOptions,\n\t): ValidationResult {\n\t\tconst analysis = this.analyze(inputSchema, options);\n\t\treturn {\n\t\t\tvalid: analysis.valid,\n\t\t\tdiagnostics: analysis.diagnostics,\n\t\t};\n\t}\n\n\t// ─── Execution ───────────────────────────────────────────────────────\n\n\t/**\n\t * Executes this template with the provided data.\n\t *\n\t * The return type depends on the template structure:\n\t * - Single expression `{{expr}}` → raw value (number, boolean, object…)\n\t * - Mixed template or with blocks → `string`\n\t * - Primitive literal → the value as-is\n\t * - Object template → object with resolved values\n\t * - Array template → array with resolved values\n\t *\n\t * If a `schema` is provided in options, static analysis is performed\n\t * before execution. A `TemplateAnalysisError` is thrown on errors.\n\t *\n\t * @param data - The context data for rendering\n\t * @param options - Execution options (schema, identifierData, coerceSchema, etc.)\n\t * @returns The execution result\n\t */\n\texecute(data: TemplateData, options?: ExecuteOptions): unknown {\n\t\tswitch (this.state.kind) {\n\t\t\tcase \"array\": {\n\t\t\t\tconst { elements } = this.state;\n\t\t\t\tconst result: unknown[] = [];\n\t\t\t\tfor (const element of elements) {\n\t\t\t\t\tresult.push(element.execute(data, options));\n\t\t\t\t}\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tcase \"object\": {\n\t\t\t\tconst { children } = this.state;\n\t\t\t\tconst coerceSchema = options?.coerceSchema;\n\t\t\t\tconst result: Record<string, unknown> = {};\n\t\t\t\tfor (const [key, child] of Object.entries(children)) {\n\t\t\t\t\tconst childCoerceSchema = resolveChildCoerceSchema(coerceSchema, key);\n\t\t\t\t\tresult[key] = child.execute(data, {\n\t\t\t\t\t\t...options,\n\t\t\t\t\t\tcoerceSchema: childCoerceSchema,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tcase \"literal\":\n\t\t\t\treturn this.state.value;\n\n\t\t\tcase \"template\": {\n\t\t\t\t// Pre-execution static validation if a schema is provided\n\t\t\t\tif (options?.schema) {\n\t\t\t\t\tconst analysis = this.analyze(options.schema, {\n\t\t\t\t\t\tidentifierSchemas: options.identifierSchemas,\n\t\t\t\t\t\tcoerceSchema: options.coerceSchema,\n\t\t\t\t\t});\n\t\t\t\t\tif (!analysis.valid) {\n\t\t\t\t\t\tthrow new TemplateAnalysisError(analysis.diagnostics);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn executeFromAst(\n\t\t\t\t\tthis.state.ast,\n\t\t\t\t\tthis.state.source,\n\t\t\t\t\tdata,\n\t\t\t\t\tthis.buildExecutorContext(options),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t// ─── Combined Shortcuts ──────────────────────────────────────────────\n\n\t/**\n\t * Analyzes and executes the template in a single call.\n\t *\n\t * Returns both the analysis result and the executed value.\n\t * If analysis fails, `value` is `undefined`.\n\t *\n\t * @param inputSchema - JSON Schema describing the available variables\n\t * @param data - The context data for rendering\n\t * @param options - Additional options (identifierSchemas, identifierData, coerceSchema)\n\t * @returns `{ analysis, value }`\n\t */\n\tanalyzeAndExecute(\n\t\tinputSchema: JSONSchema7 = {},\n\t\tdata: TemplateData,\n\t\toptions?: {\n\t\t\tidentifierSchemas?: Record<number, JSONSchema7>;\n\t\t\tidentifierData?: Record<number, Record<string, unknown>>;\n\t\t\tcoerceSchema?: JSONSchema7;\n\t\t},\n\t): { analysis: AnalysisResult; value: unknown } {\n\t\tswitch (this.state.kind) {\n\t\t\tcase \"array\": {\n\t\t\t\tconst { elements } = this.state;\n\t\t\t\treturn aggregateArrayAnalysisAndExecution(elements.length, (index) => {\n\t\t\t\t\tconst element = elements[index];\n\t\t\t\t\tif (!element)\n\t\t\t\t\t\tthrow new Error(`unreachable: missing element at index ${index}`);\n\t\t\t\t\treturn element.analyzeAndExecute(inputSchema, data, options);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tcase \"object\": {\n\t\t\t\tconst { children } = this.state;\n\t\t\t\tconst coerceSchema = options?.coerceSchema;\n\t\t\t\treturn aggregateObjectAnalysisAndExecution(\n\t\t\t\t\tObject.keys(children),\n\t\t\t\t\t(key) => {\n\t\t\t\t\t\tconst child = children[key];\n\t\t\t\t\t\tif (!child) throw new Error(`unreachable: missing child \"${key}\"`);\n\t\t\t\t\t\tconst childCoerceSchema = resolveChildCoerceSchema(\n\t\t\t\t\t\t\tcoerceSchema,\n\t\t\t\t\t\t\tkey,\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn child.analyzeAndExecute(inputSchema, data, {\n\t\t\t\t\t\t\tidentifierSchemas: options?.identifierSchemas,\n\t\t\t\t\t\t\tidentifierData: options?.identifierData,\n\t\t\t\t\t\t\tcoerceSchema: childCoerceSchema,\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\tcase \"literal\":\n\t\t\t\treturn {\n\t\t\t\t\tanalysis: {\n\t\t\t\t\t\tvalid: true,\n\t\t\t\t\t\tdiagnostics: [],\n\t\t\t\t\t\toutputSchema: inferPrimitiveSchema(this.state.value),\n\t\t\t\t\t},\n\t\t\t\t\tvalue: this.state.value,\n\t\t\t\t};\n\n\t\t\tcase \"template\": {\n\t\t\t\tconst analysis = this.analyze(inputSchema, {\n\t\t\t\t\tidentifierSchemas: options?.identifierSchemas,\n\t\t\t\t\tcoerceSchema: options?.coerceSchema,\n\t\t\t\t});\n\n\t\t\t\tif (!analysis.valid) {\n\t\t\t\t\treturn { analysis, value: undefined };\n\t\t\t\t}\n\n\t\t\t\tconst value = executeFromAst(\n\t\t\t\t\tthis.state.ast,\n\t\t\t\t\tthis.state.source,\n\t\t\t\t\tdata,\n\t\t\t\t\tthis.buildExecutorContext({\n\t\t\t\t\t\tidentifierData: options?.identifierData,\n\t\t\t\t\t\tcoerceSchema: options?.coerceSchema,\n\t\t\t\t\t}),\n\t\t\t\t);\n\n\t\t\t\treturn { analysis, value };\n\t\t\t}\n\t\t}\n\t}\n\n\t// ─── Internals ───────────────────────────────────────────────────────\n\n\t/**\n\t * Builds the execution context for `executeFromAst`.\n\t *\n\t * Uses lazy Handlebars compilation: the template is only compiled\n\t * on the first call that needs it (not for single expressions).\n\t */\n\tprivate buildExecutorContext(options?: ExecuteOptions): ExecutorContext {\n\t\treturn {\n\t\t\tidentifierData: options?.identifierData,\n\t\t\tcompiledTemplate: this.getOrCompileHbs(),\n\t\t\thbs: this.options.hbs,\n\t\t\tcompilationCache: this.options.compilationCache,\n\t\t\tcoerceSchema: options?.coerceSchema,\n\t\t};\n\t}\n\n\t/**\n\t * Lazily compiles the Handlebars template and caches it.\n\t *\n\t * Compilation happens only once — subsequent calls return the\n\t * in-memory compiled template.\n\t *\n\t * Precondition: this method is only called from \"template\" mode.\n\t */\n\tprivate getOrCompileHbs(): HandlebarsTemplateDelegate {\n\t\tif (!this.hbsCompiled) {\n\t\t\t// In \"template\" mode, `this.template` returns the source string\n\t\t\tthis.hbsCompiled = this.options.hbs.compile(this.template, {\n\t\t\t\tnoEscape: true,\n\t\t\t\tstrict: false,\n\t\t\t});\n\t\t}\n\t\treturn this.hbsCompiled;\n\t}\n}\n\n// ─── Internal Helpers ────────────────────────────────────────────────────────\n\n/**\n * Determines whether a `CompiledTemplate` represents a string template\n * containing Handlebars expressions (`{{…}}`).\n *\n * Used by `excludeTemplateExpression` filtering to skip dynamic entries\n * in object and array modes.\n */\nfunction isCompiledTemplateWithExpression(ct: CompiledTemplate): boolean {\n\t// Only \"template\" kind can contain expressions. Literals, objects,\n\t// and arrays are never excluded at the entry level — objects and\n\t// arrays are recursively filtered by the analysis method itself.\n\treturn ct.template !== \"\" && shouldExcludeEntry(ct.template);\n}\n"],"names":["CompiledTemplate","ast","state","kind","template","source","fromTemplate","options","fromLiteral","value","fromObject","children","fromArray","elements","analyze","inputSchema","exclude","excludeTemplateExpression","kept","filter","el","isCompiledTemplateWithExpression","aggregateArrayAnalysis","length","index","element","Error","coerceSchema","keys","Object","key","child","aggregateObjectAnalysis","childCoerceSchema","resolveChildCoerceSchema","identifierSchemas","valid","diagnostics","outputSchema","inferPrimitiveSchema","analyzeFromAst","helpers","validate","analysis","execute","data","result","push","entries","schema","TemplateAnalysisError","executeFromAst","buildExecutorContext","analyzeAndExecute","aggregateArrayAnalysisAndExecution","aggregateObjectAnalysisAndExecution","identifierData","undefined","compiledTemplate","getOrCompileHbs","hbs","compilationCache","hbsCompiled","compile","noEscape","strict","ct","shouldExcludeEntry"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/compiled-template.ts"],"sourcesContent":["import type Handlebars from \"handlebars\";\nimport type { JSONSchema7 } from \"json-schema\";\nimport type { AnalyzeOptions } from \"./analyzer.ts\";\nimport { analyzeFromAst } from \"./analyzer.ts\";\nimport { resolveChildCoerceSchema, shouldExcludeEntry } from \"./dispatch.ts\";\nimport { TemplateAnalysisError } from \"./errors.ts\";\nimport { type ExecutorContext, executeFromAst } from \"./executor.ts\";\nimport type {\n\tAnalysisResult,\n\tExecuteOptions,\n\tHelperDefinition,\n\tTemplateData,\n\tValidationResult,\n} from \"./types.ts\";\nimport { inferPrimitiveSchema } from \"./types.ts\";\nimport {\n\taggregateArrayAnalysis,\n\taggregateArrayAnalysisAndExecution,\n\taggregateObjectAnalysis,\n\taggregateObjectAnalysisAndExecution,\n\ttype LRUCache,\n} from \"./utils\";\n\n// ─── CompiledTemplate ────────────────────────────────────────────────────────\n// Pre-parsed template ready to be executed or analyzed without re-parsing.\n//\n// The compile-once / execute-many pattern avoids the cost of Handlebars\n// parsing on every call. The AST is parsed once at compile time, and the\n// Handlebars template is lazily compiled on the first `execute()`.\n//\n// Usage:\n// const tpl = engine.compile(\"Hello {{name}}\");\n// tpl.execute({ name: \"Alice\" }); // no re-parsing\n// tpl.execute({ name: \"Bob\" }); // no re-parsing or recompilation\n// tpl.analyze(schema); // no re-parsing\n//\n// ─── Internal State (TemplateState) ──────────────────────────────────────────\n// CompiledTemplate operates in 4 exclusive modes, modeled by a discriminated\n// union `TemplateState`:\n//\n// - `\"template\"` — parsed Handlebars template (AST + source string)\n// - `\"literal\"` — primitive passthrough value (number, boolean, null)\n// - `\"object\"` — object where each property is a child CompiledTemplate\n// - `\"array\"` — array where each element is a child CompiledTemplate\n//\n// This design eliminates optional fields and `!` assertions in favor of\n// natural TypeScript narrowing via `switch (this.state.kind)`.\n//\n// ─── Advantages Over the Direct API ──────────────────────────────────────────\n// - **Performance**: parsing and compilation happen only once\n// - **Simplified API**: no need to re-pass the template string on each call\n// - **Consistency**: the same AST is used for both analysis and execution\n\n// ─── Internal Types ──────────────────────────────────────────────────────────\n\n/** Internal options passed by Typebars during compilation */\nexport interface CompiledTemplateOptions {\n\t/** Custom helpers registered on the engine */\n\thelpers: Map<string, HelperDefinition>;\n\t/** Isolated Handlebars environment (with registered helpers) */\n\thbs: typeof Handlebars;\n\t/** Compilation cache shared by the engine */\n\tcompilationCache: LRUCache<string, HandlebarsTemplateDelegate>;\n}\n\n/** Discriminated internal state of the CompiledTemplate */\ntype TemplateState =\n\t| {\n\t\t\treadonly kind: \"template\";\n\t\t\treadonly ast: hbs.AST.Program;\n\t\t\treadonly source: string;\n\t }\n\t| { readonly kind: \"literal\"; readonly value: number | boolean | null }\n\t| {\n\t\t\treadonly kind: \"object\";\n\t\t\treadonly children: Record<string, CompiledTemplate>;\n\t }\n\t| {\n\t\t\treadonly kind: \"array\";\n\t\t\treadonly elements: CompiledTemplate[];\n\t };\n\n// ─── Public Class ────────────────────────────────────────────────────────────\n\nexport class CompiledTemplate {\n\t/** Discriminated internal state */\n\tprivate readonly state: TemplateState;\n\n\t/** Options inherited from the parent Typebars instance */\n\tprivate readonly options: CompiledTemplateOptions;\n\n\t/** Compiled Handlebars template (lazy — created on the first `execute()` that needs it) */\n\tprivate hbsCompiled: HandlebarsTemplateDelegate | null = null;\n\n\t// ─── Public Accessors (backward-compatible) ──────────────────────────\n\n\t/** The pre-parsed Handlebars AST — `null` in literal, object, or array mode */\n\tget ast(): hbs.AST.Program | null {\n\t\treturn this.state.kind === \"template\" ? this.state.ast : null;\n\t}\n\n\t/** The original template source — empty string in literal, object, or array mode */\n\tget template(): string {\n\t\treturn this.state.kind === \"template\" ? this.state.source : \"\";\n\t}\n\n\t// ─── Construction ────────────────────────────────────────────────────\n\n\tprivate constructor(state: TemplateState, options: CompiledTemplateOptions) {\n\t\tthis.state = state;\n\t\tthis.options = options;\n\t}\n\n\t/**\n\t * Creates a CompiledTemplate for a parsed Handlebars template.\n\t *\n\t * @param ast - The pre-parsed Handlebars AST\n\t * @param source - The original template source\n\t * @param options - Options inherited from Typebars\n\t */\n\tstatic fromTemplate(\n\t\tast: hbs.AST.Program,\n\t\tsource: string,\n\t\toptions: CompiledTemplateOptions,\n\t): CompiledTemplate {\n\t\treturn new CompiledTemplate({ kind: \"template\", ast, source }, options);\n\t}\n\n\t/**\n\t * Creates a CompiledTemplate in passthrough mode for a literal value\n\t * (number, boolean, null). No parsing or compilation is performed.\n\t *\n\t * @param value - The primitive value\n\t * @param options - Options inherited from Typebars\n\t * @returns A CompiledTemplate that always returns `value`\n\t */\n\tstatic fromLiteral(\n\t\tvalue: number | boolean | null,\n\t\toptions: CompiledTemplateOptions,\n\t): CompiledTemplate {\n\t\treturn new CompiledTemplate({ kind: \"literal\", value }, options);\n\t}\n\n\t/**\n\t * Creates a CompiledTemplate in object mode, where each property is a\n\t * child CompiledTemplate. All operations are recursively delegated\n\t * to the children.\n\t *\n\t * @param children - The compiled child templates `{ [key]: CompiledTemplate }`\n\t * @param options - Options inherited from Typebars\n\t * @returns A CompiledTemplate that delegates to children\n\t */\n\tstatic fromObject(\n\t\tchildren: Record<string, CompiledTemplate>,\n\t\toptions: CompiledTemplateOptions,\n\t): CompiledTemplate {\n\t\treturn new CompiledTemplate({ kind: \"object\", children }, options);\n\t}\n\n\t/**\n\t * Creates a CompiledTemplate in array mode, where each element is a\n\t * child CompiledTemplate. All operations are recursively delegated\n\t * to the elements.\n\t *\n\t * @param elements - The compiled child templates (ordered array)\n\t * @param options - Options inherited from Typebars\n\t * @returns A CompiledTemplate that delegates to elements\n\t */\n\tstatic fromArray(\n\t\telements: CompiledTemplate[],\n\t\toptions: CompiledTemplateOptions,\n\t): CompiledTemplate {\n\t\treturn new CompiledTemplate({ kind: \"array\", elements }, options);\n\t}\n\n\t// ─── Static Analysis ─────────────────────────────────────────────────\n\n\t/**\n\t * Statically analyzes this template against a JSON Schema v7.\n\t *\n\t * Returns an `AnalysisResult` containing:\n\t * - `valid` — `true` if no errors\n\t * - `diagnostics` — list of diagnostics (errors + warnings)\n\t * - `outputSchema` — JSON Schema describing the return type\n\t *\n\t * Since the AST is pre-parsed, this method never re-parses the template.\n\t *\n\t * @param inputSchema - JSON Schema describing the available variables\n\t * @param options - (optional) Analysis options (identifierSchemas, coerceSchema)\n\t */\n\tanalyze(\n\t\tinputSchema: JSONSchema7 = {},\n\t\toptions?: AnalyzeOptions,\n\t): AnalysisResult {\n\t\tconst exclude = options?.excludeTemplateExpression === true;\n\n\t\tswitch (this.state.kind) {\n\t\t\tcase \"array\": {\n\t\t\t\tconst { elements } = this.state;\n\n\t\t\t\tif (exclude) {\n\t\t\t\t\t// When excludeTemplateExpression is enabled, filter out elements\n\t\t\t\t\t// that are string templates containing Handlebars expressions.\n\t\t\t\t\tconst kept = elements.filter(\n\t\t\t\t\t\t(el) => !isCompiledTemplateWithExpression(el),\n\t\t\t\t\t);\n\t\t\t\t\treturn aggregateArrayAnalysis(kept.length, (index) => {\n\t\t\t\t\t\tconst element = kept[index];\n\t\t\t\t\t\tif (!element)\n\t\t\t\t\t\t\tthrow new Error(`unreachable: missing element at index ${index}`);\n\t\t\t\t\t\treturn element.analyze(inputSchema, options);\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\treturn aggregateArrayAnalysis(elements.length, (index) => {\n\t\t\t\t\tconst element = elements[index];\n\t\t\t\t\tif (!element)\n\t\t\t\t\t\tthrow new Error(`unreachable: missing element at index ${index}`);\n\t\t\t\t\treturn element.analyze(inputSchema, options);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tcase \"object\": {\n\t\t\t\tconst { children } = this.state;\n\t\t\t\tconst coerceSchema = options?.coerceSchema;\n\n\t\t\t\t// When excludeTemplateExpression is enabled, filter out keys whose\n\t\t\t\t// compiled children are string templates with Handlebars expressions.\n\t\t\t\tconst keys = exclude\n\t\t\t\t\t? Object.keys(children).filter((key) => {\n\t\t\t\t\t\t\tconst child = children[key];\n\t\t\t\t\t\t\treturn !child || !isCompiledTemplateWithExpression(child);\n\t\t\t\t\t\t})\n\t\t\t\t\t: Object.keys(children);\n\n\t\t\t\treturn aggregateObjectAnalysis(keys, (key) => {\n\t\t\t\t\tconst child = children[key];\n\t\t\t\t\tif (!child) throw new Error(`unreachable: missing child \"${key}\"`);\n\t\t\t\t\tconst childCoerceSchema = resolveChildCoerceSchema(coerceSchema, key);\n\t\t\t\t\treturn child.analyze(inputSchema, {\n\t\t\t\t\t\tidentifierSchemas: options?.identifierSchemas,\n\t\t\t\t\t\tcoerceSchema: childCoerceSchema,\n\t\t\t\t\t\texcludeTemplateExpression: options?.excludeTemplateExpression,\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tcase \"literal\":\n\t\t\t\treturn {\n\t\t\t\t\tvalid: true,\n\t\t\t\t\tdiagnostics: [],\n\t\t\t\t\toutputSchema: inferPrimitiveSchema(this.state.value),\n\t\t\t\t};\n\n\t\t\tcase \"template\":\n\t\t\t\treturn analyzeFromAst(this.state.ast, this.state.source, inputSchema, {\n\t\t\t\t\tidentifierSchemas: options?.identifierSchemas,\n\t\t\t\t\thelpers: this.options.helpers,\n\t\t\t\t\tcoerceSchema: options?.coerceSchema,\n\t\t\t\t});\n\t\t}\n\t}\n\n\t// ─── Validation ──────────────────────────────────────────────────────\n\n\t/**\n\t * Validates the template against a schema without returning the output type.\n\t *\n\t * This is an API shortcut for `analyze()` that only returns `valid` and\n\t * `diagnostics`, without `outputSchema`. The full analysis (including type\n\t * inference) is executed internally — this method provides no performance\n\t * gain, only a simplified API.\n\t *\n\t * @param inputSchema - JSON Schema describing the available variables\n\t * @param options - (optional) Analysis options (identifierSchemas, coerceSchema)\n\t */\n\tvalidate(\n\t\tinputSchema: JSONSchema7 = {},\n\t\toptions?: AnalyzeOptions,\n\t): ValidationResult {\n\t\tconst analysis = this.analyze(inputSchema, options);\n\t\treturn {\n\t\t\tvalid: analysis.valid,\n\t\t\tdiagnostics: analysis.diagnostics,\n\t\t};\n\t}\n\n\t// ─── Execution ───────────────────────────────────────────────────────\n\n\t/**\n\t * Executes this template with the provided data.\n\t *\n\t * The return type depends on the template structure:\n\t * - Single expression `{{expr}}` → raw value (number, boolean, object…)\n\t * - Mixed template or with blocks → `string`\n\t * - Primitive literal → the value as-is\n\t * - Object template → object with resolved values\n\t * - Array template → array with resolved values\n\t *\n\t * If a `schema` is provided in options, static analysis is performed\n\t * before execution. A `TemplateAnalysisError` is thrown on errors.\n\t *\n\t * @param data - The context data for rendering\n\t * @param options - Execution options (schema, identifierData, coerceSchema, etc.)\n\t * @returns The execution result\n\t */\n\texecute(data: TemplateData, options?: ExecuteOptions): unknown {\n\t\tswitch (this.state.kind) {\n\t\t\tcase \"array\": {\n\t\t\t\tconst { elements } = this.state;\n\t\t\t\tconst result: unknown[] = [];\n\t\t\t\tfor (const element of elements) {\n\t\t\t\t\tresult.push(element.execute(data, options));\n\t\t\t\t}\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tcase \"object\": {\n\t\t\t\tconst { children } = this.state;\n\t\t\t\tconst coerceSchema = options?.coerceSchema;\n\t\t\t\tconst result: Record<string, unknown> = {};\n\t\t\t\tfor (const [key, child] of Object.entries(children)) {\n\t\t\t\t\tconst childCoerceSchema = resolveChildCoerceSchema(coerceSchema, key);\n\t\t\t\t\tresult[key] = child.execute(data, {\n\t\t\t\t\t\t...options,\n\t\t\t\t\t\tcoerceSchema: childCoerceSchema,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tcase \"literal\":\n\t\t\t\treturn this.state.value;\n\n\t\t\tcase \"template\": {\n\t\t\t\t// Pre-execution static validation if a schema is provided\n\t\t\t\tif (options?.schema) {\n\t\t\t\t\tconst analysis = this.analyze(options.schema, {\n\t\t\t\t\t\tidentifierSchemas: options.identifierSchemas,\n\t\t\t\t\t\tcoerceSchema: options.coerceSchema,\n\t\t\t\t\t});\n\t\t\t\t\tif (!analysis.valid) {\n\t\t\t\t\t\tthrow new TemplateAnalysisError(analysis.diagnostics);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn executeFromAst(\n\t\t\t\t\tthis.state.ast,\n\t\t\t\t\tthis.state.source,\n\t\t\t\t\tdata,\n\t\t\t\t\tthis.buildExecutorContext(options),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t// ─── Combined Shortcuts ──────────────────────────────────────────────\n\n\t/**\n\t * Analyzes and executes the template in a single call.\n\t *\n\t * Returns both the analysis result and the executed value.\n\t * If analysis fails, `value` is `undefined`.\n\t *\n\t * @param inputSchema - JSON Schema describing the available variables\n\t * @param data - The context data for rendering\n\t * @param options - Additional options (identifierSchemas, identifierData, coerceSchema)\n\t * @returns `{ analysis, value }`\n\t */\n\tanalyzeAndExecute(\n\t\tinputSchema: JSONSchema7 = {},\n\t\tdata: TemplateData,\n\t\toptions?: {\n\t\t\tidentifierSchemas?: Record<number, JSONSchema7>;\n\t\t\tidentifierData?: Record<number, Record<string, unknown>>;\n\t\t\tcoerceSchema?: JSONSchema7;\n\t\t},\n\t): { analysis: AnalysisResult; value: unknown } {\n\t\tswitch (this.state.kind) {\n\t\t\tcase \"array\": {\n\t\t\t\tconst { elements } = this.state;\n\t\t\t\treturn aggregateArrayAnalysisAndExecution(elements.length, (index) => {\n\t\t\t\t\tconst element = elements[index];\n\t\t\t\t\tif (!element)\n\t\t\t\t\t\tthrow new Error(`unreachable: missing element at index ${index}`);\n\t\t\t\t\treturn element.analyzeAndExecute(inputSchema, data, options);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tcase \"object\": {\n\t\t\t\tconst { children } = this.state;\n\t\t\t\tconst coerceSchema = options?.coerceSchema;\n\t\t\t\treturn aggregateObjectAnalysisAndExecution(\n\t\t\t\t\tObject.keys(children),\n\t\t\t\t\t(key) => {\n\t\t\t\t\t\tconst child = children[key];\n\t\t\t\t\t\tif (!child) throw new Error(`unreachable: missing child \"${key}\"`);\n\t\t\t\t\t\tconst childCoerceSchema = resolveChildCoerceSchema(\n\t\t\t\t\t\t\tcoerceSchema,\n\t\t\t\t\t\t\tkey,\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn child.analyzeAndExecute(inputSchema, data, {\n\t\t\t\t\t\t\tidentifierSchemas: options?.identifierSchemas,\n\t\t\t\t\t\t\tidentifierData: options?.identifierData,\n\t\t\t\t\t\t\tcoerceSchema: childCoerceSchema,\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\tcase \"literal\":\n\t\t\t\treturn {\n\t\t\t\t\tanalysis: {\n\t\t\t\t\t\tvalid: true,\n\t\t\t\t\t\tdiagnostics: [],\n\t\t\t\t\t\toutputSchema: inferPrimitiveSchema(this.state.value),\n\t\t\t\t\t},\n\t\t\t\t\tvalue: this.state.value,\n\t\t\t\t};\n\n\t\t\tcase \"template\": {\n\t\t\t\tconst analysis = this.analyze(inputSchema, {\n\t\t\t\t\tidentifierSchemas: options?.identifierSchemas,\n\t\t\t\t\tcoerceSchema: options?.coerceSchema,\n\t\t\t\t});\n\n\t\t\t\tif (!analysis.valid) {\n\t\t\t\t\treturn { analysis, value: undefined };\n\t\t\t\t}\n\n\t\t\t\tconst value = executeFromAst(\n\t\t\t\t\tthis.state.ast,\n\t\t\t\t\tthis.state.source,\n\t\t\t\t\tdata,\n\t\t\t\t\tthis.buildExecutorContext({\n\t\t\t\t\t\tidentifierData: options?.identifierData,\n\t\t\t\t\t\tcoerceSchema: options?.coerceSchema,\n\t\t\t\t\t}),\n\t\t\t\t);\n\n\t\t\t\treturn { analysis, value };\n\t\t\t}\n\t\t}\n\t}\n\n\t// ─── Internals ───────────────────────────────────────────────────────\n\n\t/**\n\t * Builds the execution context for `executeFromAst`.\n\t *\n\t * Uses lazy Handlebars compilation: the template is only compiled\n\t * on the first call that needs it (not for single expressions).\n\t */\n\tprivate buildExecutorContext(options?: ExecuteOptions): ExecutorContext {\n\t\treturn {\n\t\t\tidentifierData: options?.identifierData,\n\t\t\tcompiledTemplate: this.getOrCompileHbs(),\n\t\t\thbs: this.options.hbs,\n\t\t\tcompilationCache: this.options.compilationCache,\n\t\t\tcoerceSchema: options?.coerceSchema,\n\t\t\thelpers: this.options.helpers,\n\t\t};\n\t}\n\n\t/**\n\t * Lazily compiles the Handlebars template and caches it.\n\t *\n\t * Compilation happens only once — subsequent calls return the\n\t * in-memory compiled template.\n\t *\n\t * Precondition: this method is only called from \"template\" mode.\n\t */\n\tprivate getOrCompileHbs(): HandlebarsTemplateDelegate {\n\t\tif (!this.hbsCompiled) {\n\t\t\t// In \"template\" mode, `this.template` returns the source string\n\t\t\tthis.hbsCompiled = this.options.hbs.compile(this.template, {\n\t\t\t\tnoEscape: true,\n\t\t\t\tstrict: false,\n\t\t\t});\n\t\t}\n\t\treturn this.hbsCompiled;\n\t}\n}\n\n// ─── Internal Helpers ────────────────────────────────────────────────────────\n\n/**\n * Determines whether a `CompiledTemplate` represents a string template\n * containing Handlebars expressions (`{{…}}`).\n *\n * Used by `excludeTemplateExpression` filtering to skip dynamic entries\n * in object and array modes.\n */\nfunction isCompiledTemplateWithExpression(ct: CompiledTemplate): boolean {\n\t// Only \"template\" kind can contain expressions. Literals, objects,\n\t// and arrays are never excluded at the entry level — objects and\n\t// arrays are recursively filtered by the analysis method itself.\n\treturn ct.template !== \"\" && shouldExcludeEntry(ct.template);\n}\n"],"names":["CompiledTemplate","ast","state","kind","template","source","fromTemplate","options","fromLiteral","value","fromObject","children","fromArray","elements","analyze","inputSchema","exclude","excludeTemplateExpression","kept","filter","el","isCompiledTemplateWithExpression","aggregateArrayAnalysis","length","index","element","Error","coerceSchema","keys","Object","key","child","aggregateObjectAnalysis","childCoerceSchema","resolveChildCoerceSchema","identifierSchemas","valid","diagnostics","outputSchema","inferPrimitiveSchema","analyzeFromAst","helpers","validate","analysis","execute","data","result","push","entries","schema","TemplateAnalysisError","executeFromAst","buildExecutorContext","analyzeAndExecute","aggregateArrayAnalysisAndExecution","aggregateObjectAnalysisAndExecution","identifierData","undefined","compiledTemplate","getOrCompileHbs","hbs","compilationCache","hbsCompiled","compile","noEscape","strict","ct","shouldExcludeEntry"],"mappings":"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"}