typebars 1.0.9 → 1.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +156 -8
- package/dist/cjs/analyzer.d.ts +32 -2
- package/dist/cjs/analyzer.js +1 -1
- package/dist/cjs/analyzer.js.map +1 -1
- package/dist/cjs/compiled-template.d.ts +12 -10
- package/dist/cjs/compiled-template.js +1 -1
- package/dist/cjs/compiled-template.js.map +1 -1
- package/dist/cjs/executor.d.ts +7 -0
- package/dist/cjs/executor.js +1 -1
- package/dist/cjs/executor.js.map +1 -1
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/typebars.d.ts +9 -6
- package/dist/cjs/typebars.js +1 -1
- package/dist/cjs/typebars.js.map +1 -1
- package/dist/cjs/types.d.ts +12 -0
- package/dist/cjs/types.js.map +1 -1
- package/dist/esm/analyzer.d.ts +32 -2
- package/dist/esm/analyzer.js +1 -1
- package/dist/esm/analyzer.js.map +1 -1
- package/dist/esm/compiled-template.d.ts +12 -10
- package/dist/esm/compiled-template.js +1 -1
- package/dist/esm/compiled-template.js.map +1 -1
- package/dist/esm/executor.d.ts +7 -0
- package/dist/esm/executor.js +1 -1
- package/dist/esm/executor.js.map +1 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/typebars.d.ts +9 -6
- package/dist/esm/typebars.js +1 -1
- package/dist/esm/typebars.js.map +1 -1
- package/dist/esm/types.d.ts +12 -0
- package/dist/esm/types.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type Handlebars from "handlebars";
|
|
2
2
|
import type { JSONSchema7 } from "json-schema";
|
|
3
|
+
import type { AnalyzeOptions } from "./analyzer.js";
|
|
3
4
|
import type { AnalysisResult, ExecuteOptions, HelperDefinition, ValidationResult } from "./types.js";
|
|
4
5
|
import { type LRUCache } from "./utils.js";
|
|
5
6
|
/** Internal options passed by Typebars during compilation */
|
|
@@ -70,10 +71,10 @@ export declare class CompiledTemplate {
|
|
|
70
71
|
*
|
|
71
72
|
* Since the AST is pre-parsed, this method never re-parses the template.
|
|
72
73
|
*
|
|
73
|
-
* @param inputSchema
|
|
74
|
-
* @param
|
|
74
|
+
* @param inputSchema - JSON Schema describing the available variables
|
|
75
|
+
* @param options - (optional) Analysis options (identifierSchemas, coerceSchema)
|
|
75
76
|
*/
|
|
76
|
-
analyze(inputSchema: JSONSchema7,
|
|
77
|
+
analyze(inputSchema: JSONSchema7, options?: AnalyzeOptions): AnalysisResult;
|
|
77
78
|
/**
|
|
78
79
|
* Validates the template against a schema without returning the output type.
|
|
79
80
|
*
|
|
@@ -82,10 +83,10 @@ export declare class CompiledTemplate {
|
|
|
82
83
|
* inference) is executed internally — this method provides no performance
|
|
83
84
|
* gain, only a simplified API.
|
|
84
85
|
*
|
|
85
|
-
* @param inputSchema
|
|
86
|
-
* @param
|
|
86
|
+
* @param inputSchema - JSON Schema describing the available variables
|
|
87
|
+
* @param options - (optional) Analysis options (identifierSchemas, coerceSchema)
|
|
87
88
|
*/
|
|
88
|
-
validate(inputSchema: JSONSchema7,
|
|
89
|
+
validate(inputSchema: JSONSchema7, options?: AnalyzeOptions): ValidationResult;
|
|
89
90
|
/**
|
|
90
91
|
* Executes this template with the provided data.
|
|
91
92
|
*
|
|
@@ -100,7 +101,7 @@ export declare class CompiledTemplate {
|
|
|
100
101
|
* before execution. A `TemplateAnalysisError` is thrown on errors.
|
|
101
102
|
*
|
|
102
103
|
* @param data - The context data for rendering
|
|
103
|
-
* @param options - Execution options (schema, identifierData, etc.)
|
|
104
|
+
* @param options - Execution options (schema, identifierData, coerceSchema, etc.)
|
|
104
105
|
* @returns The execution result
|
|
105
106
|
*/
|
|
106
107
|
execute(data: Record<string, unknown>, options?: ExecuteOptions): unknown;
|
|
@@ -110,14 +111,15 @@ export declare class CompiledTemplate {
|
|
|
110
111
|
* Returns both the analysis result and the executed value.
|
|
111
112
|
* If analysis fails, `value` is `undefined`.
|
|
112
113
|
*
|
|
113
|
-
* @param inputSchema
|
|
114
|
-
* @param data
|
|
115
|
-
* @param options
|
|
114
|
+
* @param inputSchema - JSON Schema describing the available variables
|
|
115
|
+
* @param data - The context data for rendering
|
|
116
|
+
* @param options - Additional options (identifierSchemas, identifierData, coerceSchema)
|
|
116
117
|
* @returns `{ analysis, value }`
|
|
117
118
|
*/
|
|
118
119
|
analyzeAndExecute(inputSchema: JSONSchema7, data: Record<string, unknown>, options?: {
|
|
119
120
|
identifierSchemas?: Record<number, JSONSchema7>;
|
|
120
121
|
identifierData?: Record<number, Record<string, unknown>>;
|
|
122
|
+
coerceSchema?: JSONSchema7;
|
|
121
123
|
}): {
|
|
122
124
|
analysis: AnalysisResult;
|
|
123
125
|
value: unknown;
|
|
@@ -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 _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,
|
|
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 _errorsts=require("./errors.js");const _executorts=require("./executor.js");const _schemaresolverts=require("./schema-resolver.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){switch(this.state.kind){case"array":{const{elements}=this.state;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;return(0,_utils.aggregateObjectAnalysis)(Object.keys(children),key=>{const child=children[key];if(!child)throw new Error(`unreachable: missing child "${key}"`);const childCoerceSchema=coerceSchema?(0,_schemaresolverts.resolveSchemaPath)(coerceSchema,[key]):undefined;return child.analyze(inputSchema,{identifierSchemas:options?.identifierSchemas,coerceSchema:childCoerceSchema})})}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=coerceSchema?(0,_schemaresolverts.resolveSchemaPath)(coerceSchema,[key]):undefined;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=coerceSchema?(0,_schemaresolverts.resolveSchemaPath)(coerceSchema,[key]):undefined;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}}
|
|
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 { analyzeFromAst } from \"./analyzer.ts\";\nimport { TemplateAnalysisError } from \"./errors.ts\";\nimport { type ExecutorContext, executeFromAst } from \"./executor.ts\";\nimport type {\n\tAnalysisResult,\n\tExecuteOptions,\n\tHelperDefinition,\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 identifierSchemas - (optional) Schemas by identifier `{ [id]: JSONSchema7 }`\n\t */\n\tanalyze(\n\t\tinputSchema: JSONSchema7,\n\t\tidentifierSchemas?: Record<number, JSONSchema7>,\n\t): AnalysisResult {\n\t\tswitch (this.state.kind) {\n\t\t\tcase \"array\": {\n\t\t\t\tconst { elements } = this.state;\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, identifierSchemas);\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\treturn aggregateObjectAnalysis(Object.keys(children), (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\treturn child.analyze(inputSchema, identifierSchemas);\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,\n\t\t\t\t\thelpers: this.options.helpers,\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 identifierSchemas - (optional) Schemas by identifier\n\t */\n\tvalidate(\n\t\tinputSchema: JSONSchema7,\n\t\tidentifierSchemas?: Record<number, JSONSchema7>,\n\t): ValidationResult {\n\t\tconst analysis = this.analyze(inputSchema, identifierSchemas);\n\t\treturn {\n\t\t\tvalid: analysis.valid,\n\t\t\tdiagnostics: analysis.diagnostics,\n\t\t};\n\t}\n\n\t// ─── 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, etc.)\n\t * @returns The execution result\n\t */\n\texecute(data: Record<string, unknown>, 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 result: Record<string, unknown> = {};\n\t\t\t\tfor (const [key, child] of Object.entries(children)) {\n\t\t\t\t\tresult[key] = child.execute(data, options);\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(\n\t\t\t\t\t\toptions.schema,\n\t\t\t\t\t\toptions.identifierSchemas,\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\n\t * @returns `{ analysis, value }`\n\t */\n\tanalyzeAndExecute(\n\t\tinputSchema: JSONSchema7,\n\t\tdata: Record<string, unknown>,\n\t\toptions?: {\n\t\t\tidentifierSchemas?: Record<number, JSONSchema7>;\n\t\t\tidentifierData?: Record<number, Record<string, unknown>>;\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\treturn aggregateObjectAnalysisAndExecution(\n\t\t\t\t\tObject.keys(children),\n\t\t\t\t\t// biome-ignore lint/style/noNonNullAssertion: key comes from Object.keys(children), access is guaranteed\n\t\t\t\t\t(key) => children[key]!.analyzeAndExecute(inputSchema, data, options),\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, options?.identifierSchemas);\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}),\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};\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"],"names":["CompiledTemplate","ast","state","kind","template","source","fromTemplate","options","fromLiteral","value","fromObject","children","fromArray","elements","analyze","inputSchema","identifierSchemas","aggregateArrayAnalysis","length","index","element","Error","aggregateObjectAnalysis","Object","keys","key","child","valid","diagnostics","outputSchema","inferPrimitiveSchema","analyzeFromAst","helpers","validate","analysis","execute","data","result","push","entries","schema","TemplateAnalysisError","executeFromAst","buildExecutorContext","analyzeAndExecute","aggregateArrayAnalysisAndExecution","aggregateObjectAnalysisAndExecution","undefined","identifierData","compiledTemplate","getOrCompileHbs","hbs","compilationCache","hbsCompiled","compile","noEscape","strict"],"mappings":"oGAiFaA,0DAAAA,8CA/EkB,yCACO,yCACe,wCAOhB,mCAO9B,+LA+DA,MAAMA,iBAaZ,IAAIC,KAA8B,CACjC,OAAO,IAAI,CAACC,KAAK,CAACC,IAAI,GAAK,WAAa,IAAI,CAACD,KAAK,CAACD,GAAG,CAAG,IAC1D,CAGA,IAAIG,UAAmB,CACtB,OAAO,IAAI,CAACF,KAAK,CAACC,IAAI,GAAK,WAAa,IAAI,CAACD,KAAK,CAACG,MAAM,CAAG,EAC7D,CAgBA,OAAOC,aACNL,GAAoB,CACpBI,MAAc,CACdE,OAAgC,CACb,CACnB,OAAO,IAAIP,iBAAiB,CAAEG,KAAM,WAAYF,IAAKI,MAAO,EAAGE,QAChE,CAUA,OAAOC,YACNC,KAA8B,CAC9BF,OAAgC,CACb,CACnB,OAAO,IAAIP,iBAAiB,CAAEG,KAAM,UAAWM,KAAM,EAAGF,QACzD,CAWA,OAAOG,WACNC,QAA0C,CAC1CJ,OAAgC,CACb,CACnB,OAAO,IAAIP,iBAAiB,CAAEG,KAAM,SAAUQ,QAAS,EAAGJ,QAC3D,CAWA,OAAOK,UACNC,QAA4B,CAC5BN,OAAgC,CACb,CACnB,OAAO,IAAIP,iBAAiB,CAAEG,KAAM,QAASU,QAAS,EAAGN,QAC1D,CAiBAO,QACCC,WAAwB,CACxBC,iBAA+C,CAC9B,CACjB,OAAQ,IAAI,CAACd,KAAK,CAACC,IAAI,EACtB,IAAK,QAAS,CACb,KAAM,CAAEU,QAAQ,CAAE,CAAG,IAAI,CAACX,KAAK,CAC/B,MAAOe,GAAAA,6BAAsB,EAACJ,SAASK,MAAM,CAAE,AAACC,QAC/C,MAAMC,QAAUP,QAAQ,CAACM,MAAM,CAC/B,GAAI,CAACC,QACJ,MAAM,IAAIC,MAAM,CAAC,sCAAsC,EAAEF,MAAM,CAAC,EACjE,OAAOC,QAAQN,OAAO,CAACC,YAAaC,kBACrC,EACD,CAEA,IAAK,SAAU,CACd,KAAM,CAAEL,QAAQ,CAAE,CAAG,IAAI,CAACT,KAAK,CAC/B,MAAOoB,GAAAA,8BAAuB,EAACC,OAAOC,IAAI,CAACb,UAAW,AAACc,MACtD,MAAMC,MAAQf,QAAQ,CAACc,IAAI,CAC3B,GAAI,CAACC,MAAO,MAAM,IAAIL,MAAM,CAAC,4BAA4B,EAAEI,IAAI,CAAC,CAAC,EACjE,OAAOC,MAAMZ,OAAO,CAACC,YAAaC,kBACnC,EACD,CAEA,IAAK,UACJ,MAAO,CACNW,MAAO,KACPC,YAAa,EAAE,CACfC,aAAcC,GAAAA,6BAAoB,EAAC,IAAI,CAAC5B,KAAK,CAACO,KAAK,CACpD,CAED,KAAK,WACJ,MAAOsB,GAAAA,0BAAc,EAAC,IAAI,CAAC7B,KAAK,CAACD,GAAG,CAAE,IAAI,CAACC,KAAK,CAACG,MAAM,CAAEU,YAAa,CACrEC,kBACAgB,QAAS,IAAI,CAACzB,OAAO,CAACyB,OAAO,AAC9B,EACF,CACD,CAeAC,SACClB,WAAwB,CACxBC,iBAA+C,CAC5B,CACnB,MAAMkB,SAAW,IAAI,CAACpB,OAAO,CAACC,YAAaC,mBAC3C,MAAO,CACNW,MAAOO,SAASP,KAAK,CACrBC,YAAaM,SAASN,WAAW,AAClC,CACD,CAqBAO,QAAQC,IAA6B,CAAE7B,OAAwB,CAAW,CACzE,OAAQ,IAAI,CAACL,KAAK,CAACC,IAAI,EACtB,IAAK,QAAS,CACb,KAAM,CAAEU,QAAQ,CAAE,CAAG,IAAI,CAACX,KAAK,CAC/B,MAAMmC,OAAoB,EAAE,CAC5B,IAAK,MAAMjB,WAAWP,SAAU,CAC/BwB,OAAOC,IAAI,CAAClB,QAAQe,OAAO,CAACC,KAAM7B,SACnC,CACA,OAAO8B,MACR,CAEA,IAAK,SAAU,CACd,KAAM,CAAE1B,QAAQ,CAAE,CAAG,IAAI,CAACT,KAAK,CAC/B,MAAMmC,OAAkC,CAAC,EACzC,IAAK,KAAM,CAACZ,IAAKC,MAAM,GAAIH,OAAOgB,OAAO,CAAC5B,UAAW,CACpD0B,MAAM,CAACZ,IAAI,CAAGC,MAAMS,OAAO,CAACC,KAAM7B,QACnC,CACA,OAAO8B,MACR,CAEA,IAAK,UACJ,OAAO,IAAI,CAACnC,KAAK,CAACO,KAAK,AAExB,KAAK,WAAY,CAEhB,GAAIF,SAASiC,OAAQ,CACpB,MAAMN,SAAW,IAAI,CAACpB,OAAO,CAC5BP,QAAQiC,MAAM,CACdjC,QAAQS,iBAAiB,EAE1B,GAAI,CAACkB,SAASP,KAAK,CAAE,CACpB,MAAM,IAAIc,+BAAqB,CAACP,SAASN,WAAW,CACrD,CACD,CAEA,MAAOc,GAAAA,0BAAc,EACpB,IAAI,CAACxC,KAAK,CAACD,GAAG,CACd,IAAI,CAACC,KAAK,CAACG,MAAM,CACjB+B,KACA,IAAI,CAACO,oBAAoB,CAACpC,SAE5B,CACD,CACD,CAeAqC,kBACC7B,WAAwB,CACxBqB,IAA6B,CAC7B7B,OAGC,CAC8C,CAC/C,OAAQ,IAAI,CAACL,KAAK,CAACC,IAAI,EACtB,IAAK,QAAS,CACb,KAAM,CAAEU,QAAQ,CAAE,CAAG,IAAI,CAACX,KAAK,CAC/B,MAAO2C,GAAAA,yCAAkC,EAAChC,SAASK,MAAM,CAAE,AAACC,QAC3D,MAAMC,QAAUP,QAAQ,CAACM,MAAM,CAC/B,GAAI,CAACC,QACJ,MAAM,IAAIC,MAAM,CAAC,sCAAsC,EAAEF,MAAM,CAAC,EACjE,OAAOC,QAAQwB,iBAAiB,CAAC7B,YAAaqB,KAAM7B,QACrD,EACD,CAEA,IAAK,SAAU,CACd,KAAM,CAAEI,QAAQ,CAAE,CAAG,IAAI,CAACT,KAAK,CAC/B,MAAO4C,GAAAA,0CAAmC,EACzCvB,OAAOC,IAAI,CAACb,UAEZ,AAACc,KAAQd,QAAQ,CAACc,IAAI,CAAEmB,iBAAiB,CAAC7B,YAAaqB,KAAM7B,SAE/D,CAEA,IAAK,UACJ,MAAO,CACN2B,SAAU,CACTP,MAAO,KACPC,YAAa,EAAE,CACfC,aAAcC,GAAAA,6BAAoB,EAAC,IAAI,CAAC5B,KAAK,CAACO,KAAK,CACpD,EACAA,MAAO,IAAI,CAACP,KAAK,CAACO,KAAK,AACxB,CAED,KAAK,WAAY,CAChB,MAAMyB,SAAW,IAAI,CAACpB,OAAO,CAACC,YAAaR,SAASS,mBAEpD,GAAI,CAACkB,SAASP,KAAK,CAAE,CACpB,MAAO,CAAEO,SAAUzB,MAAOsC,SAAU,CACrC,CAEA,MAAMtC,MAAQiC,GAAAA,0BAAc,EAC3B,IAAI,CAACxC,KAAK,CAACD,GAAG,CACd,IAAI,CAACC,KAAK,CAACG,MAAM,CACjB+B,KACA,IAAI,CAACO,oBAAoB,CAAC,CACzBK,eAAgBzC,SAASyC,cAC1B,IAGD,MAAO,CAAEd,SAAUzB,KAAM,CAC1B,CACD,CACD,CAUA,AAAQkC,qBAAqBpC,OAAwB,CAAmB,CACvE,MAAO,CACNyC,eAAgBzC,SAASyC,eACzBC,iBAAkB,IAAI,CAACC,eAAe,GACtCC,IAAK,IAAI,CAAC5C,OAAO,CAAC4C,GAAG,CACrBC,iBAAkB,IAAI,CAAC7C,OAAO,CAAC6C,gBAAgB,AAChD,CACD,CAUA,AAAQF,iBAA8C,CACrD,GAAI,CAAC,IAAI,CAACG,WAAW,CAAE,CAEtB,IAAI,CAACA,WAAW,CAAG,IAAI,CAAC9C,OAAO,CAAC4C,GAAG,CAACG,OAAO,CAAC,IAAI,CAAClD,QAAQ,CAAE,CAC1DmD,SAAU,KACVC,OAAQ,KACT,EACD,CACA,OAAO,IAAI,CAACH,WAAW,AACxB,CA3TA,YAAoBnD,KAAoB,CAAEK,OAAgC,CAAE,CAtB5E,sBAAiBL,QAAjB,KAAA,GAGA,sBAAiBK,UAAjB,KAAA,GAGA,sBAAQ8C,cAAiD,KAiBxD,CAAA,IAAI,CAACnD,KAAK,CAAGA,KACb,CAAA,IAAI,CAACK,OAAO,CAAGA,OAChB,CAyTD"}
|
|
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 { TemplateAnalysisError } from \"./errors.ts\";\nimport { type ExecutorContext, executeFromAst } from \"./executor.ts\";\nimport { resolveSchemaPath } from \"./schema-resolver.ts\";\nimport type {\n\tAnalysisResult,\n\tExecuteOptions,\n\tHelperDefinition,\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(inputSchema: JSONSchema7, options?: AnalyzeOptions): AnalysisResult {\n\t\tswitch (this.state.kind) {\n\t\t\tcase \"array\": {\n\t\t\t\tconst { elements } = this.state;\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\t\t\t\treturn aggregateObjectAnalysis(Object.keys(children), (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 = coerceSchema\n\t\t\t\t\t\t? resolveSchemaPath(coerceSchema, [key])\n\t\t\t\t\t\t: undefined;\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});\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: Record<string, unknown>, 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 = coerceSchema\n\t\t\t\t\t\t? resolveSchemaPath(coerceSchema, [key])\n\t\t\t\t\t\t: undefined;\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: Record<string, unknown>,\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 = coerceSchema\n\t\t\t\t\t\t\t? resolveSchemaPath(coerceSchema, [key])\n\t\t\t\t\t\t\t: undefined;\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"],"names":["CompiledTemplate","ast","state","kind","template","source","fromTemplate","options","fromLiteral","value","fromObject","children","fromArray","elements","analyze","inputSchema","aggregateArrayAnalysis","length","index","element","Error","coerceSchema","aggregateObjectAnalysis","Object","keys","key","child","childCoerceSchema","resolveSchemaPath","undefined","identifierSchemas","valid","diagnostics","outputSchema","inferPrimitiveSchema","analyzeFromAst","helpers","validate","analysis","execute","data","result","push","entries","schema","TemplateAnalysisError","executeFromAst","buildExecutorContext","analyzeAndExecute","aggregateArrayAnalysisAndExecution","aggregateObjectAnalysisAndExecution","identifierData","compiledTemplate","getOrCompileHbs","hbs","compilationCache","hbsCompiled","compile","noEscape","strict"],"mappings":"oGAmFaA,0DAAAA,8CAhFkB,yCACO,yCACe,iDACnB,+CAOG,mCAO9B,+LA+DA,MAAMA,iBAaZ,IAAIC,KAA8B,CACjC,OAAO,IAAI,CAACC,KAAK,CAACC,IAAI,GAAK,WAAa,IAAI,CAACD,KAAK,CAACD,GAAG,CAAG,IAC1D,CAGA,IAAIG,UAAmB,CACtB,OAAO,IAAI,CAACF,KAAK,CAACC,IAAI,GAAK,WAAa,IAAI,CAACD,KAAK,CAACG,MAAM,CAAG,EAC7D,CAgBA,OAAOC,aACNL,GAAoB,CACpBI,MAAc,CACdE,OAAgC,CACb,CACnB,OAAO,IAAIP,iBAAiB,CAAEG,KAAM,WAAYF,IAAKI,MAAO,EAAGE,QAChE,CAUA,OAAOC,YACNC,KAA8B,CAC9BF,OAAgC,CACb,CACnB,OAAO,IAAIP,iBAAiB,CAAEG,KAAM,UAAWM,KAAM,EAAGF,QACzD,CAWA,OAAOG,WACNC,QAA0C,CAC1CJ,OAAgC,CACb,CACnB,OAAO,IAAIP,iBAAiB,CAAEG,KAAM,SAAUQ,QAAS,EAAGJ,QAC3D,CAWA,OAAOK,UACNC,QAA4B,CAC5BN,OAAgC,CACb,CACnB,OAAO,IAAIP,iBAAiB,CAAEG,KAAM,QAASU,QAAS,EAAGN,QAC1D,CAiBAO,QAAQC,WAAwB,CAAER,OAAwB,CAAkB,CAC3E,OAAQ,IAAI,CAACL,KAAK,CAACC,IAAI,EACtB,IAAK,QAAS,CACb,KAAM,CAAEU,QAAQ,CAAE,CAAG,IAAI,CAACX,KAAK,CAC/B,MAAOc,GAAAA,6BAAsB,EAACH,SAASI,MAAM,CAAE,AAACC,QAC/C,MAAMC,QAAUN,QAAQ,CAACK,MAAM,CAC/B,GAAI,CAACC,QACJ,MAAM,IAAIC,MAAM,CAAC,sCAAsC,EAAEF,MAAM,CAAC,EACjE,OAAOC,QAAQL,OAAO,CAACC,YAAaR,QACrC,EACD,CAEA,IAAK,SAAU,CACd,KAAM,CAAEI,QAAQ,CAAE,CAAG,IAAI,CAACT,KAAK,CAC/B,MAAMmB,aAAed,SAASc,aAC9B,MAAOC,GAAAA,8BAAuB,EAACC,OAAOC,IAAI,CAACb,UAAW,AAACc,MACtD,MAAMC,MAAQf,QAAQ,CAACc,IAAI,CAC3B,GAAI,CAACC,MAAO,MAAM,IAAIN,MAAM,CAAC,4BAA4B,EAAEK,IAAI,CAAC,CAAC,EACjE,MAAME,kBAAoBN,aACvBO,GAAAA,mCAAiB,EAACP,aAAc,CAACI,IAAI,EACrCI,UACH,OAAOH,MAAMZ,OAAO,CAACC,YAAa,CACjCe,kBAAmBvB,SAASuB,kBAC5BT,aAAcM,iBACf,EACD,EACD,CAEA,IAAK,UACJ,MAAO,CACNI,MAAO,KACPC,YAAa,EAAE,CACfC,aAAcC,GAAAA,6BAAoB,EAAC,IAAI,CAAChC,KAAK,CAACO,KAAK,CACpD,CAED,KAAK,WACJ,MAAO0B,GAAAA,0BAAc,EAAC,IAAI,CAACjC,KAAK,CAACD,GAAG,CAAE,IAAI,CAACC,KAAK,CAACG,MAAM,CAAEU,YAAa,CACrEe,kBAAmBvB,SAASuB,kBAC5BM,QAAS,IAAI,CAAC7B,OAAO,CAAC6B,OAAO,CAC7Bf,aAAcd,SAASc,YACxB,EACF,CACD,CAeAgB,SACCtB,WAAwB,CACxBR,OAAwB,CACL,CACnB,MAAM+B,SAAW,IAAI,CAACxB,OAAO,CAACC,YAAaR,SAC3C,MAAO,CACNwB,MAAOO,SAASP,KAAK,CACrBC,YAAaM,SAASN,WAAW,AAClC,CACD,CAqBAO,QAAQC,IAA6B,CAAEjC,OAAwB,CAAW,CACzE,OAAQ,IAAI,CAACL,KAAK,CAACC,IAAI,EACtB,IAAK,QAAS,CACb,KAAM,CAAEU,QAAQ,CAAE,CAAG,IAAI,CAACX,KAAK,CAC/B,MAAMuC,OAAoB,EAAE,CAC5B,IAAK,MAAMtB,WAAWN,SAAU,CAC/B4B,OAAOC,IAAI,CAACvB,QAAQoB,OAAO,CAACC,KAAMjC,SACnC,CACA,OAAOkC,MACR,CAEA,IAAK,SAAU,CACd,KAAM,CAAE9B,QAAQ,CAAE,CAAG,IAAI,CAACT,KAAK,CAC/B,MAAMmB,aAAed,SAASc,aAC9B,MAAMoB,OAAkC,CAAC,EACzC,IAAK,KAAM,CAAChB,IAAKC,MAAM,GAAIH,OAAOoB,OAAO,CAAChC,UAAW,CACpD,MAAMgB,kBAAoBN,aACvBO,GAAAA,mCAAiB,EAACP,aAAc,CAACI,IAAI,EACrCI,SACHY,CAAAA,MAAM,CAAChB,IAAI,CAAGC,MAAMa,OAAO,CAACC,KAAM,CACjC,GAAGjC,OAAO,CACVc,aAAcM,iBACf,EACD,CACA,OAAOc,MACR,CAEA,IAAK,UACJ,OAAO,IAAI,CAACvC,KAAK,CAACO,KAAK,AAExB,KAAK,WAAY,CAEhB,GAAIF,SAASqC,OAAQ,CACpB,MAAMN,SAAW,IAAI,CAACxB,OAAO,CAACP,QAAQqC,MAAM,CAAE,CAC7Cd,kBAAmBvB,QAAQuB,iBAAiB,CAC5CT,aAAcd,QAAQc,YAAY,AACnC,GACA,GAAI,CAACiB,SAASP,KAAK,CAAE,CACpB,MAAM,IAAIc,+BAAqB,CAACP,SAASN,WAAW,CACrD,CACD,CAEA,MAAOc,GAAAA,0BAAc,EACpB,IAAI,CAAC5C,KAAK,CAACD,GAAG,CACd,IAAI,CAACC,KAAK,CAACG,MAAM,CACjBmC,KACA,IAAI,CAACO,oBAAoB,CAACxC,SAE5B,CACD,CACD,CAeAyC,kBACCjC,WAAwB,CACxByB,IAA6B,CAC7BjC,OAIC,CAC8C,CAC/C,OAAQ,IAAI,CAACL,KAAK,CAACC,IAAI,EACtB,IAAK,QAAS,CACb,KAAM,CAAEU,QAAQ,CAAE,CAAG,IAAI,CAACX,KAAK,CAC/B,MAAO+C,GAAAA,yCAAkC,EAACpC,SAASI,MAAM,CAAE,AAACC,QAC3D,MAAMC,QAAUN,QAAQ,CAACK,MAAM,CAC/B,GAAI,CAACC,QACJ,MAAM,IAAIC,MAAM,CAAC,sCAAsC,EAAEF,MAAM,CAAC,EACjE,OAAOC,QAAQ6B,iBAAiB,CAACjC,YAAayB,KAAMjC,QACrD,EACD,CAEA,IAAK,SAAU,CACd,KAAM,CAAEI,QAAQ,CAAE,CAAG,IAAI,CAACT,KAAK,CAC/B,MAAMmB,aAAed,SAASc,aAC9B,MAAO6B,GAAAA,0CAAmC,EACzC3B,OAAOC,IAAI,CAACb,UACZ,AAACc,MACA,MAAMC,MAAQf,QAAQ,CAACc,IAAI,CAC3B,GAAI,CAACC,MAAO,MAAM,IAAIN,MAAM,CAAC,4BAA4B,EAAEK,IAAI,CAAC,CAAC,EACjE,MAAME,kBAAoBN,aACvBO,GAAAA,mCAAiB,EAACP,aAAc,CAACI,IAAI,EACrCI,UACH,OAAOH,MAAMsB,iBAAiB,CAACjC,YAAayB,KAAM,CACjDV,kBAAmBvB,SAASuB,kBAC5BqB,eAAgB5C,SAAS4C,eACzB9B,aAAcM,iBACf,EACD,EAEF,CAEA,IAAK,UACJ,MAAO,CACNW,SAAU,CACTP,MAAO,KACPC,YAAa,EAAE,CACfC,aAAcC,GAAAA,6BAAoB,EAAC,IAAI,CAAChC,KAAK,CAACO,KAAK,CACpD,EACAA,MAAO,IAAI,CAACP,KAAK,CAACO,KAAK,AACxB,CAED,KAAK,WAAY,CAChB,MAAM6B,SAAW,IAAI,CAACxB,OAAO,CAACC,YAAa,CAC1Ce,kBAAmBvB,SAASuB,kBAC5BT,aAAcd,SAASc,YACxB,GAEA,GAAI,CAACiB,SAASP,KAAK,CAAE,CACpB,MAAO,CAAEO,SAAU7B,MAAOoB,SAAU,CACrC,CAEA,MAAMpB,MAAQqC,GAAAA,0BAAc,EAC3B,IAAI,CAAC5C,KAAK,CAACD,GAAG,CACd,IAAI,CAACC,KAAK,CAACG,MAAM,CACjBmC,KACA,IAAI,CAACO,oBAAoB,CAAC,CACzBI,eAAgB5C,SAAS4C,eACzB9B,aAAcd,SAASc,YACxB,IAGD,MAAO,CAAEiB,SAAU7B,KAAM,CAC1B,CACD,CACD,CAUA,AAAQsC,qBAAqBxC,OAAwB,CAAmB,CACvE,MAAO,CACN4C,eAAgB5C,SAAS4C,eACzBC,iBAAkB,IAAI,CAACC,eAAe,GACtCC,IAAK,IAAI,CAAC/C,OAAO,CAAC+C,GAAG,CACrBC,iBAAkB,IAAI,CAAChD,OAAO,CAACgD,gBAAgB,CAC/ClC,aAAcd,SAASc,YACxB,CACD,CAUA,AAAQgC,iBAA8C,CACrD,GAAI,CAAC,IAAI,CAACG,WAAW,CAAE,CAEtB,IAAI,CAACA,WAAW,CAAG,IAAI,CAACjD,OAAO,CAAC+C,GAAG,CAACG,OAAO,CAAC,IAAI,CAACrD,QAAQ,CAAE,CAC1DsD,SAAU,KACVC,OAAQ,KACT,EACD,CACA,OAAO,IAAI,CAACH,WAAW,AACxB,CAxVA,YAAoBtD,KAAoB,CAAEK,OAAgC,CAAE,CAtB5E,sBAAiBL,QAAjB,KAAA,GAGA,sBAAiBK,UAAjB,KAAA,GAGA,sBAAQiD,cAAiD,KAiBxD,CAAA,IAAI,CAACtD,KAAK,CAAGA,KACb,CAAA,IAAI,CAACK,OAAO,CAAGA,OAChB,CAsVD"}
|
package/dist/cjs/executor.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Handlebars from "handlebars";
|
|
2
|
+
import type { JSONSchema7 } from "json-schema";
|
|
2
3
|
import type { TemplateInput } from "./types.js";
|
|
3
4
|
import { LRUCache } from "./utils.js";
|
|
4
5
|
/** Optional context for execution (used by Typebars/CompiledTemplate) */
|
|
@@ -11,6 +12,12 @@ export interface ExecutorContext {
|
|
|
11
12
|
hbs?: typeof Handlebars;
|
|
12
13
|
/** Compilation cache shared by the engine */
|
|
13
14
|
compilationCache?: LRUCache<string, HandlebarsTemplateDelegate>;
|
|
15
|
+
/**
|
|
16
|
+
* Explicit coercion schema for the output value.
|
|
17
|
+
* When set with a primitive type, the execution result will be coerced
|
|
18
|
+
* to match the declared type instead of using auto-detection.
|
|
19
|
+
*/
|
|
20
|
+
coerceSchema?: JSONSchema7;
|
|
14
21
|
}
|
|
15
22
|
/**
|
|
16
23
|
* Executes a template with the provided data and returns the result.
|
package/dist/cjs/executor.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:true});function _export(target,all){for(var name in all)Object.defineProperty(target,name,{enumerable:true,get:Object.getOwnPropertyDescriptor(all,name).get})}_export(exports,{get clearCompilationCache(){return clearCompilationCache},get execute(){return execute},get executeFromAst(){return executeFromAst},get resolveDataPath(){return resolveDataPath}});const _handlebars=/*#__PURE__*/_interop_require_default(require("handlebars"));const _errorsts=require("./errors.js");const _parserts=require("./parser.js");const _typests=require("./types.js");const _utilsts=require("./utils.js");function _interop_require_default(obj){return obj&&obj.__esModule?obj:{default:obj}}const globalCompilationCache=new _utilsts.LRUCache(128);function execute(template,data,identifierData){if((0,_typests.isArrayInput)(template)){return executeArrayTemplate(template,data,identifierData)}if((0,_typests.isObjectInput)(template)){return executeObjectTemplate(template,data,identifierData)}if((0,_typests.isLiteralInput)(template))return template;const ast=(0,_parserts.parse)(template);return executeFromAst(ast,template,data,{identifierData})}function executeArrayTemplate(template,data,identifierData){const result=[];for(const element of template){result.push(execute(element,data,identifierData))}return result}function executeObjectTemplate(template,data,identifierData){const result={};for(const[key,value]of Object.entries(template)){result[key]=execute(value,data,identifierData)}return result}function executeFromAst(ast,template,data,ctx){const identifierData=ctx?.identifierData;if((0,_parserts.isSingleExpression)(ast)){const stmt=ast.body[0];if(stmt.params.length===0&&!stmt.hash){return resolveExpression(stmt.path,data,identifierData)}}const singleExpr=(0,_parserts.getEffectivelySingleExpression)(ast);if(singleExpr&&singleExpr.params.length===0&&!singleExpr.hash){return resolveExpression(singleExpr.path,data,identifierData)}if(singleExpr&&(singleExpr.params.length>0||singleExpr.hash)){const merged=mergeDataWithIdentifiers(data,identifierData);const raw=renderWithHandlebars(template,merged,ctx);return(
|
|
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 clearCompilationCache(){return clearCompilationCache},get execute(){return execute},get executeFromAst(){return executeFromAst},get resolveDataPath(){return resolveDataPath}});const _handlebars=/*#__PURE__*/_interop_require_default(require("handlebars"));const _errorsts=require("./errors.js");const _parserts=require("./parser.js");const _typests=require("./types.js");const _utilsts=require("./utils.js");function _interop_require_default(obj){return obj&&obj.__esModule?obj:{default:obj}}const globalCompilationCache=new _utilsts.LRUCache(128);function execute(template,data,identifierData){if((0,_typests.isArrayInput)(template)){return executeArrayTemplate(template,data,identifierData)}if((0,_typests.isObjectInput)(template)){return executeObjectTemplate(template,data,identifierData)}if((0,_typests.isLiteralInput)(template))return template;const ast=(0,_parserts.parse)(template);return executeFromAst(ast,template,data,{identifierData})}function executeArrayTemplate(template,data,identifierData){const result=[];for(const element of template){result.push(execute(element,data,identifierData))}return result}function executeObjectTemplate(template,data,identifierData){const result={};for(const[key,value]of Object.entries(template)){result[key]=execute(value,data,identifierData)}return result}function executeFromAst(ast,template,data,ctx){const identifierData=ctx?.identifierData;if((0,_parserts.isSingleExpression)(ast)){const stmt=ast.body[0];if(stmt.params.length===0&&!stmt.hash){return resolveExpression(stmt.path,data,identifierData)}}const singleExpr=(0,_parserts.getEffectivelySingleExpression)(ast);if(singleExpr&&singleExpr.params.length===0&&!singleExpr.hash){return resolveExpression(singleExpr.path,data,identifierData)}if(singleExpr&&(singleExpr.params.length>0||singleExpr.hash)){const merged=mergeDataWithIdentifiers(data,identifierData);const raw=renderWithHandlebars(template,merged,ctx);return coerceValue(raw,ctx?.coerceSchema)}if((0,_parserts.canUseFastPath)(ast)&&ast.body.length>1){return executeFastPath(ast,data,identifierData)}const singleBlock=(0,_parserts.getEffectivelySingleBlock)(ast);if(singleBlock){const merged=mergeDataWithIdentifiers(data,identifierData);const raw=renderWithHandlebars(template,merged,ctx);return coerceValue(raw,ctx?.coerceSchema)}const merged=mergeDataWithIdentifiers(data,identifierData);const raw=renderWithHandlebars(template,merged,ctx);const effective=(0,_parserts.getEffectiveBody)(ast);const allContent=effective.every(s=>s.type==="ContentStatement");if(allContent){return coerceValue(raw,ctx?.coerceSchema)}return raw}function coerceValue(raw,coerceSchema){if(coerceSchema){const targetType=coerceSchema.type;if(typeof targetType==="string"){if(targetType==="string")return raw;if(targetType==="number"||targetType==="integer")return Number(raw.trim());if(targetType==="boolean")return raw.trim()==="true";if(targetType==="null")return null}}return(0,_parserts.coerceLiteral)(raw)}function executeFastPath(ast,data,identifierData){let result="";for(const stmt of ast.body){if(stmt.type==="ContentStatement"){result+=stmt.value}else if(stmt.type==="MustacheStatement"){const value=resolveExpression(stmt.path,data,identifierData);if(value!=null){result+=String(value)}}}return result}function resolveExpression(expr,data,identifierData){if((0,_parserts.isThisExpression)(expr)){return data}if(expr.type==="StringLiteral")return expr.value;if(expr.type==="NumberLiteral")return expr.value;if(expr.type==="BooleanLiteral")return expr.value;if(expr.type==="NullLiteral")return null;if(expr.type==="UndefinedLiteral")return undefined;const segments=(0,_parserts.extractPathSegments)(expr);if(segments.length===0){throw new _errorsts.TemplateRuntimeError(`Cannot resolve expression of type "${expr.type}"`)}const{cleanSegments,identifier}=(0,_parserts.extractExpressionIdentifier)(segments);if(identifier!==null&&identifierData){const source=identifierData[identifier];if(source){return resolveDataPath(source,cleanSegments)}return undefined}if(identifier!==null&&!identifierData){return undefined}return resolveDataPath(data,cleanSegments)}function resolveDataPath(data,segments){let current=data;for(const segment of segments){if(current===null||current===undefined){return undefined}if(typeof current!=="object"){return undefined}current=current[segment]}return current}function mergeDataWithIdentifiers(data,identifierData){if(!identifierData)return data;const merged={...data};for(const[id,idData]of Object.entries(identifierData)){for(const[key,value]of Object.entries(idData)){merged[`${key}:${id}`]=value}}return merged}function renderWithHandlebars(template,data,ctx){try{if(ctx?.compiledTemplate){return ctx.compiledTemplate(data)}const cache=ctx?.compilationCache??globalCompilationCache;const hbs=ctx?.hbs??_handlebars.default;let compiled=cache.get(template);if(!compiled){compiled=hbs.compile(template,{noEscape:true,strict:false});cache.set(template,compiled)}return compiled(data)}catch(error){const message=error instanceof Error?error.message:String(error);throw new _errorsts.TemplateRuntimeError(message)}}function clearCompilationCache(){globalCompilationCache.clear()}
|
|
2
2
|
//# sourceMappingURL=executor.js.map
|
package/dist/cjs/executor.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/executor.ts"],"sourcesContent":["import Handlebars from \"handlebars\";\nimport { TemplateRuntimeError } from \"./errors.ts\";\nimport {\n\tcanUseFastPath,\n\tcoerceLiteral,\n\textractExpressionIdentifier,\n\textractPathSegments,\n\tgetEffectivelySingleBlock,\n\tgetEffectivelySingleExpression,\n\tisSingleExpression,\n\tisThisExpression,\n\tparse,\n} from \"./parser.ts\";\nimport type {\n\tTemplateInput,\n\tTemplateInputArray,\n\tTemplateInputObject,\n} from \"./types.ts\";\nimport { isArrayInput, isLiteralInput, isObjectInput } from \"./types.ts\";\nimport { LRUCache } from \"./utils.ts\";\n\n// ─── Template Executor ───────────────────────────────────────────────────────\n// Executes a Handlebars template with real data.\n//\n// Four execution modes (from fastest to most general):\n//\n// 1. **Single expression** (`{{value}}` or ` {{value}} `) → returns the raw\n// value without converting to string. This preserves the original type\n// (number, boolean, object, array, null).\n//\n// 2. **Fast-path** (text + simple expressions, no blocks or helpers) →\n// direct concatenation without going through Handlebars.compile(). Up to\n// 10-100x faster for simple templates like `Hello {{name}}`.\n//\n// 3. **Single block** (`{{#if x}}10{{else}}20{{/if}}` possibly surrounded\n// by whitespace) → rendered via Handlebars then intelligently coerced\n// (detecting number, boolean, null literals).\n//\n// 4. **Mixed template** (text + multiple blocks, helpers, …) →\n// delegates to Handlebars which always produces a string.\n//\n// ─── Caching ─────────────────────────────────────────────────────────────────\n// Handlebars-compiled templates are cached in an LRU cache to avoid costly\n// recompilation on repeated calls.\n//\n// Two cache levels:\n// - **Global cache** (module-level) for standalone `execute()` calls\n// - **Instance cache** for `Typebars` (passed via `ExecutorContext`)\n//\n// ─── Template Identifiers ────────────────────────────────────────────────────\n// The `{{key:N}}` syntax allows resolving a variable from a specific data\n// source, identified by an integer N. The optional `identifierData` parameter\n// provides a mapping `{ [id]: { key: value, ... } }`.\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n/** Optional context for execution (used by Typebars/CompiledTemplate) */\nexport interface ExecutorContext {\n\t/** Data by identifier `{ [id]: { key: value } }` */\n\tidentifierData?: Record<number, Record<string, unknown>>;\n\t/** Pre-compiled Handlebars template (for CompiledTemplate) */\n\tcompiledTemplate?: HandlebarsTemplateDelegate;\n\t/** Isolated Handlebars environment (for custom helpers) */\n\thbs?: typeof Handlebars;\n\t/** Compilation cache shared by the engine */\n\tcompilationCache?: LRUCache<string, HandlebarsTemplateDelegate>;\n}\n\n// ─── Global Compilation Cache ────────────────────────────────────────────────\n// Used by the standalone `execute()` function and `renderWithHandlebars()`.\n// `Typebars` instances use their own cache.\nconst globalCompilationCache = new LRUCache<string, HandlebarsTemplateDelegate>(\n\t128,\n);\n\n// ─── Public API (backward-compatible) ────────────────────────────────────────\n\n/**\n * Executes a template with the provided data and returns the result.\n *\n * The return type depends on the template structure:\n * - Single expression `{{expr}}` → raw value (any)\n * - Single block → coerced value (number, boolean, null, or string)\n * - Mixed template → `string`\n *\n * @param template - The template string\n * @param data - The main context data\n * @param identifierData - (optional) Data by identifier `{ [id]: { key: value } }`\n */\nexport function execute(\n\ttemplate: TemplateInput,\n\tdata: Record<string, unknown>,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): unknown {\n\tif (isArrayInput(template)) {\n\t\treturn executeArrayTemplate(template, data, identifierData);\n\t}\n\tif (isObjectInput(template)) {\n\t\treturn executeObjectTemplate(template, data, identifierData);\n\t}\n\tif (isLiteralInput(template)) return template;\n\tconst ast = parse(template);\n\treturn executeFromAst(ast, template, data, { identifierData });\n}\n\n/**\n * Executes an array template recursively (standalone version).\n * Each element is executed individually and the result is an array\n * with resolved values.\n */\nfunction executeArrayTemplate(\n\ttemplate: TemplateInputArray,\n\tdata: Record<string, unknown>,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): unknown[] {\n\tconst result: unknown[] = [];\n\tfor (const element of template) {\n\t\tresult.push(execute(element, data, identifierData));\n\t}\n\treturn result;\n}\n\n/**\n * Executes an object template recursively (standalone version).\n * Each property is executed individually and the result is an object\n * with the same structure but resolved values.\n */\nfunction executeObjectTemplate(\n\ttemplate: TemplateInputObject,\n\tdata: Record<string, unknown>,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): Record<string, unknown> {\n\tconst result: Record<string, unknown> = {};\n\tfor (const [key, value] of Object.entries(template)) {\n\t\tresult[key] = execute(value, data, identifierData);\n\t}\n\treturn result;\n}\n\n// ─── Internal API (for Typebars / CompiledTemplate) ──────────────────────\n\n/**\n * Executes a template from an already-parsed AST.\n *\n * This function is the core of execution. It is used by:\n * - `execute()` (backward-compatible wrapper)\n * - `CompiledTemplate.execute()` (with pre-parsed AST and cache)\n * - `Typebars.execute()` (with cache and helpers)\n *\n * @param ast - The already-parsed Handlebars AST\n * @param template - The template source (for Handlebars compilation if needed)\n * @param data - The main context data\n * @param ctx - Optional execution context\n */\nexport function executeFromAst(\n\tast: hbs.AST.Program,\n\ttemplate: string,\n\tdata: Record<string, unknown>,\n\tctx?: ExecutorContext,\n): unknown {\n\tconst identifierData = ctx?.identifierData;\n\n\t// ── Case 1: strict single expression `{{expr}}` ──────────────────────\n\t// Exclude helper calls (params > 0 or hash) because they must go\n\t// through Handlebars for correct execution.\n\tif (isSingleExpression(ast)) {\n\t\tconst stmt = ast.body[0] as hbs.AST.MustacheStatement;\n\t\tif (stmt.params.length === 0 && !stmt.hash) {\n\t\t\treturn resolveExpression(stmt.path, data, identifierData);\n\t\t}\n\t}\n\n\t// ── Case 1b: single expression with surrounding whitespace ` {{expr}} `\n\tconst singleExpr = getEffectivelySingleExpression(ast);\n\tif (singleExpr && singleExpr.params.length === 0 && !singleExpr.hash) {\n\t\treturn resolveExpression(singleExpr.path, data, identifierData);\n\t}\n\n\t// ── Case 1c: single expression with helper (params > 0) ──────────────\n\t// E.g. `{{ divide accountIds.length 10 }}` or `{{ math a \"+\" b }}`\n\t// The helper returns a typed value but Handlebars converts it to a\n\t// string. We render via Handlebars then coerce the result to recover\n\t// the original type (number, boolean, null).\n\tif (singleExpr && (singleExpr.params.length > 0 || singleExpr.hash)) {\n\t\tconst merged = mergeDataWithIdentifiers(data, identifierData);\n\t\tconst raw = renderWithHandlebars(template, merged, ctx);\n\t\treturn coerceLiteral(raw);\n\t}\n\n\t// ── Case 2: fast-path for simple templates (text + expressions) ──────\n\t// If the template only contains text and simple expressions (no blocks,\n\t// no helpers with parameters), we can do direct concatenation without\n\t// going through Handlebars.compile().\n\tif (canUseFastPath(ast) && ast.body.length > 1) {\n\t\treturn executeFastPath(ast, data, identifierData);\n\t}\n\n\t// ── Case 3: single block (possibly surrounded by whitespace) ─────────\n\t// Render via Handlebars then attempt to coerce the result to the\n\t// detected literal type (number, boolean, null).\n\tconst singleBlock = getEffectivelySingleBlock(ast);\n\tif (singleBlock) {\n\t\tconst merged = mergeDataWithIdentifiers(data, identifierData);\n\t\tconst raw = renderWithHandlebars(template, merged, ctx);\n\t\treturn coerceLiteral(raw);\n\t}\n\n\t// ── Case 4: mixed template → string ──────────────────────────────────\n\tconst merged = mergeDataWithIdentifiers(data, identifierData);\n\treturn renderWithHandlebars(template, merged, ctx);\n}\n\n// ─── Fast-Path Execution ─────────────────────────────────────────────────────\n// For templates consisting only of text and simple expressions (no blocks,\n// no helpers), we bypass Handlebars and do direct concatenation.\n// This is significantly faster.\n\n/**\n * Executes a template via the fast-path (direct concatenation).\n *\n * Precondition: `canUseFastPath(ast)` must return `true`.\n *\n * @param ast - The template AST (only ContentStatement and simple MustacheStatement)\n * @param data - The context data\n * @param identifierData - Data by identifier (optional)\n * @returns The resulting string\n */\nfunction executeFastPath(\n\tast: hbs.AST.Program,\n\tdata: Record<string, unknown>,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): string {\n\tlet result = \"\";\n\n\tfor (const stmt of ast.body) {\n\t\tif (stmt.type === \"ContentStatement\") {\n\t\t\tresult += (stmt as hbs.AST.ContentStatement).value;\n\t\t} else if (stmt.type === \"MustacheStatement\") {\n\t\t\tconst value = resolveExpression(\n\t\t\t\t(stmt as hbs.AST.MustacheStatement).path,\n\t\t\t\tdata,\n\t\t\t\tidentifierData,\n\t\t\t);\n\t\t\t// Handlebars converts values to strings for rendering.\n\t\t\t// We replicate this behavior: null/undefined → \"\", otherwise String(value).\n\t\t\tif (value != null) {\n\t\t\t\tresult += String(value);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn result;\n}\n\n// ─── Direct Expression Resolution ────────────────────────────────────────────\n// Used for single-expression templates and the fast-path, to return the raw\n// value without going through the Handlebars engine.\n\n/**\n * Resolves an AST expression by following the path through the data.\n *\n * If the expression contains an identifier (e.g. `meetingId:1`), resolution\n * is performed in `identifierData[1]` instead of `data`.\n *\n * @param expr - The AST expression to resolve\n * @param data - The main data context\n * @param identifierData - Data by identifier (optional)\n * @returns The raw value pointed to by the expression\n */\nfunction resolveExpression(\n\texpr: hbs.AST.Expression,\n\tdata: Record<string, unknown>,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): unknown {\n\t// this / . → return the entire context\n\tif (isThisExpression(expr)) {\n\t\treturn data;\n\t}\n\n\t// Literals\n\tif (expr.type === \"StringLiteral\")\n\t\treturn (expr as hbs.AST.StringLiteral).value;\n\tif (expr.type === \"NumberLiteral\")\n\t\treturn (expr as hbs.AST.NumberLiteral).value;\n\tif (expr.type === \"BooleanLiteral\")\n\t\treturn (expr as hbs.AST.BooleanLiteral).value;\n\tif (expr.type === \"NullLiteral\") return null;\n\tif (expr.type === \"UndefinedLiteral\") return undefined;\n\n\t// PathExpression — navigate through segments in the data object\n\tconst segments = extractPathSegments(expr);\n\tif (segments.length === 0) {\n\t\tthrow new TemplateRuntimeError(\n\t\t\t`Cannot resolve expression of type \"${expr.type}\"`,\n\t\t);\n\t}\n\n\t// Extract the potential identifier from the last segment\n\tconst { cleanSegments, identifier } = extractExpressionIdentifier(segments);\n\n\tif (identifier !== null && identifierData) {\n\t\tconst source = identifierData[identifier];\n\t\tif (source) {\n\t\t\treturn resolveDataPath(source, cleanSegments);\n\t\t}\n\t\t// Source does not exist → undefined (like a missing key)\n\t\treturn undefined;\n\t}\n\n\tif (identifier !== null && !identifierData) {\n\t\t// Template uses an identifier but no identifierData was provided\n\t\treturn undefined;\n\t}\n\n\treturn resolveDataPath(data, cleanSegments);\n}\n\n/**\n * Navigates through a data object by following a path of segments.\n *\n * @param data - The data object\n * @param segments - The path segments (e.g. `[\"user\", \"address\", \"city\"]`)\n * @returns The value at the end of the path, or `undefined` if an\n * intermediate segment is null/undefined\n */\nexport function resolveDataPath(data: unknown, segments: string[]): unknown {\n\tlet current: unknown = data;\n\n\tfor (const segment of segments) {\n\t\tif (current === null || current === undefined) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif (typeof current !== \"object\") {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tcurrent = (current as Record<string, unknown>)[segment];\n\t}\n\n\treturn current;\n}\n\n// ─── Data Merging ────────────────────────────────────────────────────────────\n// For Handlebars rendering (mixed templates / blocks), we cannot intercept\n// resolution on a per-expression basis. Instead, we merge identifier data\n// into the main object using the format `\"key:N\"`.\n//\n// Handlebars parses `{{meetingId:1}}` as a PathExpression with a single\n// segment `\"meetingId:1\"`, so it looks up the key `\"meetingId:1\"` in the\n// data object — which matches our flattened format exactly.\n\n/**\n * Merges the main data with identifier data.\n *\n * @param data - Main data\n * @param identifierData - Data by identifier\n * @returns A merged object where identifier data appears as `\"key:N\"` keys\n *\n * @example\n * ```\n * mergeDataWithIdentifiers(\n * { name: \"Alice\" },\n * { 1: { meetingId: \"val1\" }, 2: { meetingId: \"val2\" } }\n * )\n * // → { name: \"Alice\", \"meetingId:1\": \"val1\", \"meetingId:2\": \"val2\" }\n * ```\n */\nfunction mergeDataWithIdentifiers(\n\tdata: Record<string, unknown>,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): Record<string, unknown> {\n\tif (!identifierData) return data;\n\n\tconst merged: Record<string, unknown> = { ...data };\n\n\tfor (const [id, idData] of Object.entries(identifierData)) {\n\t\tfor (const [key, value] of Object.entries(idData)) {\n\t\t\tmerged[`${key}:${id}`] = value;\n\t\t}\n\t}\n\n\treturn merged;\n}\n\n// ─── Handlebars Rendering ────────────────────────────────────────────────────\n// For complex templates (blocks, helpers), we delegate to Handlebars.\n// Compilation is cached to avoid costly recompilations.\n\n/**\n * Compiles and executes a template via Handlebars.\n *\n * Uses a compilation cache (LRU) to avoid recompiling the same template\n * on repeated calls. The cache is either:\n * - The global cache (for the standalone `execute()` function)\n * - The instance cache provided via `ExecutorContext` (for `Typebars`)\n *\n * @param template - The template string\n * @param data - The context data\n * @param ctx - Optional execution context (cache, Handlebars env)\n * @returns Always a string\n */\nfunction renderWithHandlebars(\n\ttemplate: string,\n\tdata: Record<string, unknown>,\n\tctx?: ExecutorContext,\n): string {\n\ttry {\n\t\t// 1. Use the pre-compiled template if available (CompiledTemplate)\n\t\tif (ctx?.compiledTemplate) {\n\t\t\treturn ctx.compiledTemplate(data);\n\t\t}\n\n\t\t// 2. Look up in the cache (instance or global)\n\t\tconst cache = ctx?.compilationCache ?? globalCompilationCache;\n\t\tconst hbs = ctx?.hbs ?? Handlebars;\n\n\t\tlet compiled = cache.get(template);\n\t\tif (!compiled) {\n\t\t\tcompiled = hbs.compile(template, {\n\t\t\t\t// Disable HTML-escaping by default — this engine is not\n\t\t\t\t// HTML-specific, we want raw values.\n\t\t\t\tnoEscape: true,\n\t\t\t\t// Strict mode: throws if a path does not exist in the data.\n\t\t\t\tstrict: false,\n\t\t\t});\n\t\t\tcache.set(template, compiled);\n\t\t}\n\n\t\treturn compiled(data);\n\t} catch (error: unknown) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tthrow new TemplateRuntimeError(message);\n\t}\n}\n\n/**\n * Clears the global Handlebars compilation cache.\n * Useful for tests or to free memory.\n */\nexport function clearCompilationCache(): void {\n\tglobalCompilationCache.clear();\n}\n"],"names":["clearCompilationCache","execute","executeFromAst","resolveDataPath","globalCompilationCache","LRUCache","template","data","identifierData","isArrayInput","executeArrayTemplate","isObjectInput","executeObjectTemplate","isLiteralInput","ast","parse","result","element","push","key","value","Object","entries","ctx","isSingleExpression","stmt","body","params","length","hash","resolveExpression","path","singleExpr","getEffectivelySingleExpression","merged","mergeDataWithIdentifiers","raw","renderWithHandlebars","coerceLiteral","canUseFastPath","executeFastPath","singleBlock","getEffectivelySingleBlock","type","String","expr","isThisExpression","undefined","segments","extractPathSegments","TemplateRuntimeError","cleanSegments","identifier","extractExpressionIdentifier","source","current","segment","id","idData","compiledTemplate","cache","compilationCache","hbs","Handlebars","compiled","get","compile","noEscape","strict","set","error","message","Error","clear"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/executor.ts"],"sourcesContent":["import Handlebars from \"handlebars\";\nimport type { JSONSchema7 } from \"json-schema\";\nimport { TemplateRuntimeError } from \"./errors.ts\";\nimport {\n\tcanUseFastPath,\n\tcoerceLiteral,\n\textractExpressionIdentifier,\n\textractPathSegments,\n\tgetEffectiveBody,\n\tgetEffectivelySingleBlock,\n\tgetEffectivelySingleExpression,\n\tisSingleExpression,\n\tisThisExpression,\n\tparse,\n} from \"./parser.ts\";\nimport type {\n\tTemplateInput,\n\tTemplateInputArray,\n\tTemplateInputObject,\n} from \"./types.ts\";\nimport { isArrayInput, isLiteralInput, isObjectInput } from \"./types.ts\";\nimport { LRUCache } from \"./utils.ts\";\n\n// ─── Template Executor ───────────────────────────────────────────────────────\n// Executes a Handlebars template with real data.\n//\n// Four execution modes (from fastest to most general):\n//\n// 1. **Single expression** (`{{value}}` or ` {{value}} `) → returns the raw\n// value without converting to string. This preserves the original type\n// (number, boolean, object, array, null).\n//\n// 2. **Fast-path** (text + simple expressions, no blocks or helpers) →\n// direct concatenation without going through Handlebars.compile(). Up to\n// 10-100x faster for simple templates like `Hello {{name}}`.\n//\n// 3. **Single block** (`{{#if x}}10{{else}}20{{/if}}` possibly surrounded\n// by whitespace) → rendered via Handlebars then intelligently coerced\n// (detecting number, boolean, null literals).\n//\n// 4. **Mixed template** (text + multiple blocks, helpers, …) →\n// delegates to Handlebars which always produces a string.\n//\n// ─── Caching ─────────────────────────────────────────────────────────────────\n// Handlebars-compiled templates are cached in an LRU cache to avoid costly\n// recompilation on repeated calls.\n//\n// Two cache levels:\n// - **Global cache** (module-level) for standalone `execute()` calls\n// - **Instance cache** for `Typebars` (passed via `ExecutorContext`)\n//\n// ─── Template Identifiers ────────────────────────────────────────────────────\n// The `{{key:N}}` syntax allows resolving a variable from a specific data\n// source, identified by an integer N. The optional `identifierData` parameter\n// provides a mapping `{ [id]: { key: value, ... } }`.\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n/** Optional context for execution (used by Typebars/CompiledTemplate) */\nexport interface ExecutorContext {\n\t/** Data by identifier `{ [id]: { key: value } }` */\n\tidentifierData?: Record<number, Record<string, unknown>>;\n\t/** Pre-compiled Handlebars template (for CompiledTemplate) */\n\tcompiledTemplate?: HandlebarsTemplateDelegate;\n\t/** Isolated Handlebars environment (for custom helpers) */\n\thbs?: typeof Handlebars;\n\t/** Compilation cache shared by the engine */\n\tcompilationCache?: LRUCache<string, HandlebarsTemplateDelegate>;\n\t/**\n\t * Explicit coercion schema for the output value.\n\t * When set with a primitive type, the execution result will be coerced\n\t * to match the declared type instead of using auto-detection.\n\t */\n\tcoerceSchema?: JSONSchema7;\n}\n\n// ─── Global Compilation Cache ────────────────────────────────────────────────\n// Used by the standalone `execute()` function and `renderWithHandlebars()`.\n// `Typebars` instances use their own cache.\nconst globalCompilationCache = new LRUCache<string, HandlebarsTemplateDelegate>(\n\t128,\n);\n\n// ─── Public API (backward-compatible) ────────────────────────────────────────\n\n/**\n * Executes a template with the provided data and returns the result.\n *\n * The return type depends on the template structure:\n * - Single expression `{{expr}}` → raw value (any)\n * - Single block → coerced value (number, boolean, null, or string)\n * - Mixed template → `string`\n *\n * @param template - The template string\n * @param data - The main context data\n * @param identifierData - (optional) Data by identifier `{ [id]: { key: value } }`\n */\nexport function execute(\n\ttemplate: TemplateInput,\n\tdata: Record<string, unknown>,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): unknown {\n\tif (isArrayInput(template)) {\n\t\treturn executeArrayTemplate(template, data, identifierData);\n\t}\n\tif (isObjectInput(template)) {\n\t\treturn executeObjectTemplate(template, data, identifierData);\n\t}\n\tif (isLiteralInput(template)) return template;\n\tconst ast = parse(template);\n\treturn executeFromAst(ast, template, data, { identifierData });\n}\n\n/**\n * Executes an array template recursively (standalone version).\n * Each element is executed individually and the result is an array\n * with resolved values.\n */\nfunction executeArrayTemplate(\n\ttemplate: TemplateInputArray,\n\tdata: Record<string, unknown>,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): unknown[] {\n\tconst result: unknown[] = [];\n\tfor (const element of template) {\n\t\tresult.push(execute(element, data, identifierData));\n\t}\n\treturn result;\n}\n\n/**\n * Executes an object template recursively (standalone version).\n * Each property is executed individually and the result is an object\n * with the same structure but resolved values.\n */\nfunction executeObjectTemplate(\n\ttemplate: TemplateInputObject,\n\tdata: Record<string, unknown>,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): Record<string, unknown> {\n\tconst result: Record<string, unknown> = {};\n\tfor (const [key, value] of Object.entries(template)) {\n\t\tresult[key] = execute(value, data, identifierData);\n\t}\n\treturn result;\n}\n\n// ─── Internal API (for Typebars / CompiledTemplate) ──────────────────────\n\n/**\n * Executes a template from an already-parsed AST.\n *\n * This function is the core of execution. It is used by:\n * - `execute()` (backward-compatible wrapper)\n * - `CompiledTemplate.execute()` (with pre-parsed AST and cache)\n * - `Typebars.execute()` (with cache and helpers)\n *\n * @param ast - The already-parsed Handlebars AST\n * @param template - The template source (for Handlebars compilation if needed)\n * @param data - The main context data\n * @param ctx - Optional execution context\n */\nexport function executeFromAst(\n\tast: hbs.AST.Program,\n\ttemplate: string,\n\tdata: Record<string, unknown>,\n\tctx?: ExecutorContext,\n): unknown {\n\tconst identifierData = ctx?.identifierData;\n\n\t// ── Case 1: strict single expression `{{expr}}` ──────────────────────\n\t// Exclude helper calls (params > 0 or hash) because they must go\n\t// through Handlebars for correct execution.\n\tif (isSingleExpression(ast)) {\n\t\tconst stmt = ast.body[0] as hbs.AST.MustacheStatement;\n\t\tif (stmt.params.length === 0 && !stmt.hash) {\n\t\t\treturn resolveExpression(stmt.path, data, identifierData);\n\t\t}\n\t}\n\n\t// ── Case 1b: single expression with surrounding whitespace ` {{expr}} `\n\tconst singleExpr = getEffectivelySingleExpression(ast);\n\tif (singleExpr && singleExpr.params.length === 0 && !singleExpr.hash) {\n\t\treturn resolveExpression(singleExpr.path, data, identifierData);\n\t}\n\n\t// ── Case 1c: single expression with helper (params > 0) ──────────────\n\t// E.g. `{{ divide accountIds.length 10 }}` or `{{ math a \"+\" b }}`\n\t// The helper returns a typed value but Handlebars converts it to a\n\t// string. We render via Handlebars then coerce the result to recover\n\t// the original type (number, boolean, null).\n\tif (singleExpr && (singleExpr.params.length > 0 || singleExpr.hash)) {\n\t\tconst merged = mergeDataWithIdentifiers(data, identifierData);\n\t\tconst raw = renderWithHandlebars(template, merged, ctx);\n\t\treturn coerceValue(raw, ctx?.coerceSchema);\n\t}\n\n\t// ── Case 2: fast-path for simple templates (text + expressions) ──────\n\t// If the template only contains text and simple expressions (no blocks,\n\t// no helpers with parameters), we can do direct concatenation without\n\t// going through Handlebars.compile().\n\tif (canUseFastPath(ast) && ast.body.length > 1) {\n\t\treturn executeFastPath(ast, data, identifierData);\n\t}\n\n\t// ── Case 3: single block (possibly surrounded by whitespace) ─────────\n\t// Render via Handlebars then attempt to coerce the result to the\n\t// detected literal type (number, boolean, null).\n\tconst singleBlock = getEffectivelySingleBlock(ast);\n\tif (singleBlock) {\n\t\tconst merged = mergeDataWithIdentifiers(data, identifierData);\n\t\tconst raw = renderWithHandlebars(template, merged, ctx);\n\t\treturn coerceValue(raw, ctx?.coerceSchema);\n\t}\n\n\t// ── Case 4: mixed template ───────────────────────────────────────────\n\t// For purely static templates (only ContentStatements), coerce the\n\t// result to match the coerceSchema type or auto-detect the literal type.\n\t// For truly mixed templates (text + blocks + expressions), return string.\n\tconst merged = mergeDataWithIdentifiers(data, identifierData);\n\tconst raw = renderWithHandlebars(template, merged, ctx);\n\n\tconst effective = getEffectiveBody(ast);\n\tconst allContent = effective.every((s) => s.type === \"ContentStatement\");\n\tif (allContent) {\n\t\treturn coerceValue(raw, ctx?.coerceSchema);\n\t}\n\n\treturn raw;\n}\n\n// ─── Value Coercion ──────────────────────────────────────────────────────────\n// Coerces a raw string from Handlebars rendering based on an optional\n// coerceSchema. When no schema is provided, falls back to auto-detection\n// via `coerceLiteral`.\n\n/**\n * Coerces a raw string value based on an optional coercion schema.\n *\n * - If `coerceSchema` declares a primitive type (`string`, `number`,\n * `integer`, `boolean`, `null`), the value is cast to that type.\n * - Otherwise, falls back to `coerceLiteral` (auto-detection).\n *\n * @param raw - The raw string from Handlebars rendering\n * @param coerceSchema - Optional schema declaring the desired output type\n * @returns The coerced value\n */\nfunction coerceValue(raw: string, coerceSchema?: JSONSchema7): unknown {\n\tif (coerceSchema) {\n\t\tconst targetType = coerceSchema.type;\n\t\tif (typeof targetType === \"string\") {\n\t\t\tif (targetType === \"string\") return raw;\n\t\t\tif (targetType === \"number\" || targetType === \"integer\")\n\t\t\t\treturn Number(raw.trim());\n\t\t\tif (targetType === \"boolean\") return raw.trim() === \"true\";\n\t\t\tif (targetType === \"null\") return null;\n\t\t}\n\t}\n\t// No coerceSchema or non-primitive type → auto-detect\n\treturn coerceLiteral(raw);\n}\n\n// ─── Fast-Path Execution ─────────────────────────────────────────────────────\n// For templates consisting only of text and simple expressions (no blocks,\n// no helpers), we bypass Handlebars and do direct concatenation.\n// This is significantly faster.\n\n/**\n * Executes a template via the fast-path (direct concatenation).\n *\n * Precondition: `canUseFastPath(ast)` must return `true`.\n *\n * @param ast - The template AST (only ContentStatement and simple MustacheStatement)\n * @param data - The context data\n * @param identifierData - Data by identifier (optional)\n * @returns The resulting string\n */\nfunction executeFastPath(\n\tast: hbs.AST.Program,\n\tdata: Record<string, unknown>,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): string {\n\tlet result = \"\";\n\n\tfor (const stmt of ast.body) {\n\t\tif (stmt.type === \"ContentStatement\") {\n\t\t\tresult += (stmt as hbs.AST.ContentStatement).value;\n\t\t} else if (stmt.type === \"MustacheStatement\") {\n\t\t\tconst value = resolveExpression(\n\t\t\t\t(stmt as hbs.AST.MustacheStatement).path,\n\t\t\t\tdata,\n\t\t\t\tidentifierData,\n\t\t\t);\n\t\t\t// Handlebars converts values to strings for rendering.\n\t\t\t// We replicate this behavior: null/undefined → \"\", otherwise String(value).\n\t\t\tif (value != null) {\n\t\t\t\tresult += String(value);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn result;\n}\n\n// ─── Direct Expression Resolution ────────────────────────────────────────────\n// Used for single-expression templates and the fast-path, to return the raw\n// value without going through the Handlebars engine.\n\n/**\n * Resolves an AST expression by following the path through the data.\n *\n * If the expression contains an identifier (e.g. `meetingId:1`), resolution\n * is performed in `identifierData[1]` instead of `data`.\n *\n * @param expr - The AST expression to resolve\n * @param data - The main data context\n * @param identifierData - Data by identifier (optional)\n * @returns The raw value pointed to by the expression\n */\nfunction resolveExpression(\n\texpr: hbs.AST.Expression,\n\tdata: Record<string, unknown>,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): unknown {\n\t// this / . → return the entire context\n\tif (isThisExpression(expr)) {\n\t\treturn data;\n\t}\n\n\t// Literals\n\tif (expr.type === \"StringLiteral\")\n\t\treturn (expr as hbs.AST.StringLiteral).value;\n\tif (expr.type === \"NumberLiteral\")\n\t\treturn (expr as hbs.AST.NumberLiteral).value;\n\tif (expr.type === \"BooleanLiteral\")\n\t\treturn (expr as hbs.AST.BooleanLiteral).value;\n\tif (expr.type === \"NullLiteral\") return null;\n\tif (expr.type === \"UndefinedLiteral\") return undefined;\n\n\t// PathExpression — navigate through segments in the data object\n\tconst segments = extractPathSegments(expr);\n\tif (segments.length === 0) {\n\t\tthrow new TemplateRuntimeError(\n\t\t\t`Cannot resolve expression of type \"${expr.type}\"`,\n\t\t);\n\t}\n\n\t// Extract the potential identifier from the last segment\n\tconst { cleanSegments, identifier } = extractExpressionIdentifier(segments);\n\n\tif (identifier !== null && identifierData) {\n\t\tconst source = identifierData[identifier];\n\t\tif (source) {\n\t\t\treturn resolveDataPath(source, cleanSegments);\n\t\t}\n\t\t// Source does not exist → undefined (like a missing key)\n\t\treturn undefined;\n\t}\n\n\tif (identifier !== null && !identifierData) {\n\t\t// Template uses an identifier but no identifierData was provided\n\t\treturn undefined;\n\t}\n\n\treturn resolveDataPath(data, cleanSegments);\n}\n\n/**\n * Navigates through a data object by following a path of segments.\n *\n * @param data - The data object\n * @param segments - The path segments (e.g. `[\"user\", \"address\", \"city\"]`)\n * @returns The value at the end of the path, or `undefined` if an\n * intermediate segment is null/undefined\n */\nexport function resolveDataPath(data: unknown, segments: string[]): unknown {\n\tlet current: unknown = data;\n\n\tfor (const segment of segments) {\n\t\tif (current === null || current === undefined) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif (typeof current !== \"object\") {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tcurrent = (current as Record<string, unknown>)[segment];\n\t}\n\n\treturn current;\n}\n\n// ─── Data Merging ────────────────────────────────────────────────────────────\n// For Handlebars rendering (mixed templates / blocks), we cannot intercept\n// resolution on a per-expression basis. Instead, we merge identifier data\n// into the main object using the format `\"key:N\"`.\n//\n// Handlebars parses `{{meetingId:1}}` as a PathExpression with a single\n// segment `\"meetingId:1\"`, so it looks up the key `\"meetingId:1\"` in the\n// data object — which matches our flattened format exactly.\n\n/**\n * Merges the main data with identifier data.\n *\n * @param data - Main data\n * @param identifierData - Data by identifier\n * @returns A merged object where identifier data appears as `\"key:N\"` keys\n *\n * @example\n * ```\n * mergeDataWithIdentifiers(\n * { name: \"Alice\" },\n * { 1: { meetingId: \"val1\" }, 2: { meetingId: \"val2\" } }\n * )\n * // → { name: \"Alice\", \"meetingId:1\": \"val1\", \"meetingId:2\": \"val2\" }\n * ```\n */\nfunction mergeDataWithIdentifiers(\n\tdata: Record<string, unknown>,\n\tidentifierData?: Record<number, Record<string, unknown>>,\n): Record<string, unknown> {\n\tif (!identifierData) return data;\n\n\tconst merged: Record<string, unknown> = { ...data };\n\n\tfor (const [id, idData] of Object.entries(identifierData)) {\n\t\tfor (const [key, value] of Object.entries(idData)) {\n\t\t\tmerged[`${key}:${id}`] = value;\n\t\t}\n\t}\n\n\treturn merged;\n}\n\n// ─── Handlebars Rendering ────────────────────────────────────────────────────\n// For complex templates (blocks, helpers), we delegate to Handlebars.\n// Compilation is cached to avoid costly recompilations.\n\n/**\n * Compiles and executes a template via Handlebars.\n *\n * Uses a compilation cache (LRU) to avoid recompiling the same template\n * on repeated calls. The cache is either:\n * - The global cache (for the standalone `execute()` function)\n * - The instance cache provided via `ExecutorContext` (for `Typebars`)\n *\n * @param template - The template string\n * @param data - The context data\n * @param ctx - Optional execution context (cache, Handlebars env)\n * @returns Always a string\n */\nfunction renderWithHandlebars(\n\ttemplate: string,\n\tdata: Record<string, unknown>,\n\tctx?: ExecutorContext,\n): string {\n\ttry {\n\t\t// 1. Use the pre-compiled template if available (CompiledTemplate)\n\t\tif (ctx?.compiledTemplate) {\n\t\t\treturn ctx.compiledTemplate(data);\n\t\t}\n\n\t\t// 2. Look up in the cache (instance or global)\n\t\tconst cache = ctx?.compilationCache ?? globalCompilationCache;\n\t\tconst hbs = ctx?.hbs ?? Handlebars;\n\n\t\tlet compiled = cache.get(template);\n\t\tif (!compiled) {\n\t\t\tcompiled = hbs.compile(template, {\n\t\t\t\t// Disable HTML-escaping by default — this engine is not\n\t\t\t\t// HTML-specific, we want raw values.\n\t\t\t\tnoEscape: true,\n\t\t\t\t// Strict mode: throws if a path does not exist in the data.\n\t\t\t\tstrict: false,\n\t\t\t});\n\t\t\tcache.set(template, compiled);\n\t\t}\n\n\t\treturn compiled(data);\n\t} catch (error: unknown) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tthrow new TemplateRuntimeError(message);\n\t}\n}\n\n/**\n * Clears the global Handlebars compilation cache.\n * Useful for tests or to free memory.\n */\nexport function clearCompilationCache(): void {\n\tglobalCompilationCache.clear();\n}\n"],"names":["clearCompilationCache","execute","executeFromAst","resolveDataPath","globalCompilationCache","LRUCache","template","data","identifierData","isArrayInput","executeArrayTemplate","isObjectInput","executeObjectTemplate","isLiteralInput","ast","parse","result","element","push","key","value","Object","entries","ctx","isSingleExpression","stmt","body","params","length","hash","resolveExpression","path","singleExpr","getEffectivelySingleExpression","merged","mergeDataWithIdentifiers","raw","renderWithHandlebars","coerceValue","coerceSchema","canUseFastPath","executeFastPath","singleBlock","getEffectivelySingleBlock","effective","getEffectiveBody","allContent","every","s","type","targetType","Number","trim","coerceLiteral","String","expr","isThisExpression","undefined","segments","extractPathSegments","TemplateRuntimeError","cleanSegments","identifier","extractExpressionIdentifier","source","current","segment","id","idData","compiledTemplate","cache","compilationCache","hbs","Handlebars","compiled","get","compile","noEscape","strict","set","error","message","Error","clear"],"mappings":"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"}
|
package/dist/cjs/index.d.ts
CHANGED
package/dist/cjs/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/index.ts"],"sourcesContent":["export * from \"./errors\";\nexport { Typebars } from \"./typebars\";\nexport {\n\tdefineHelper,\n\tisArrayInput,\n\ttype TemplateInput,\n\ttype TemplateInputArray,\n} from \"./types\";\n"],"names":["Typebars","defineHelper","isArrayInput"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../src/index.ts"],"sourcesContent":["export type { AnalyzeOptions } from \"./analyzer\";\nexport * from \"./errors\";\nexport { Typebars } from \"./typebars\";\nexport {\n\tdefineHelper,\n\tisArrayInput,\n\ttype TemplateInput,\n\ttype TemplateInputArray,\n} from \"./types\";\n"],"names":["Typebars","defineHelper","isArrayInput"],"mappings":"mPAESA,kBAAAA,kBAAQ,MAEhBC,sBAAAA,mBAAY,MACZC,sBAAAA,mBAAY,yBAJC,6CACW,mCAMlB"}
|
package/dist/cjs/typebars.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { JSONSchema7 } from "json-schema";
|
|
2
|
+
import type { AnalyzeOptions } from "./analyzer.js";
|
|
2
3
|
import { CompiledTemplate } from "./compiled-template.js";
|
|
3
4
|
import type { AnalysisResult, AnalyzeAndExecuteOptions, ExecuteOptions, HelperDefinition, TemplateEngineOptions, TemplateInput, ValidationResult } from "./types.js";
|
|
4
5
|
export declare class Typebars {
|
|
@@ -30,11 +31,11 @@ export declare class Typebars {
|
|
|
30
31
|
* For objects, each property is analyzed recursively and the
|
|
31
32
|
* `outputSchema` reflects the object structure with resolved types.
|
|
32
33
|
*
|
|
33
|
-
* @param template
|
|
34
|
-
* @param inputSchema
|
|
35
|
-
* @param
|
|
34
|
+
* @param template - The template to analyze
|
|
35
|
+
* @param inputSchema - JSON Schema v7 describing the available variables
|
|
36
|
+
* @param options - (optional) Analysis options (identifierSchemas, coerceSchema)
|
|
36
37
|
*/
|
|
37
|
-
analyze(template: TemplateInput, inputSchema: JSONSchema7,
|
|
38
|
+
analyze(template: TemplateInput, inputSchema: JSONSchema7, options?: AnalyzeOptions): AnalysisResult;
|
|
38
39
|
/**
|
|
39
40
|
* Validates a template against a schema without returning the output type.
|
|
40
41
|
*
|
|
@@ -47,7 +48,7 @@ export declare class Typebars {
|
|
|
47
48
|
* @param inputSchema - JSON Schema v7 describing the available variables
|
|
48
49
|
* @param identifierSchemas - (optional) Schemas by identifier
|
|
49
50
|
*/
|
|
50
|
-
validate(template: TemplateInput, inputSchema: JSONSchema7,
|
|
51
|
+
validate(template: TemplateInput, inputSchema: JSONSchema7, options?: AnalyzeOptions): ValidationResult;
|
|
51
52
|
/**
|
|
52
53
|
* Checks only that the template syntax is valid (parsing).
|
|
53
54
|
* Does not require a schema — useful for quick feedback in an editor.
|
|
@@ -88,7 +89,9 @@ export declare class Typebars {
|
|
|
88
89
|
* @returns An object `{ analysis, value }` where `value` is `undefined`
|
|
89
90
|
* if analysis failed.
|
|
90
91
|
*/
|
|
91
|
-
analyzeAndExecute(template: TemplateInput, inputSchema: JSONSchema7, data: Record<string, unknown>, options?: AnalyzeAndExecuteOptions
|
|
92
|
+
analyzeAndExecute(template: TemplateInput, inputSchema: JSONSchema7, data: Record<string, unknown>, options?: AnalyzeAndExecuteOptions & {
|
|
93
|
+
coerceSchema?: JSONSchema7;
|
|
94
|
+
}): {
|
|
92
95
|
analysis: AnalysisResult;
|
|
93
96
|
value: unknown;
|
|
94
97
|
};
|
package/dist/cjs/typebars.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:true});Object.defineProperty(exports,"Typebars",{enumerable:true,get:function(){return Typebars}});const _handlebars=/*#__PURE__*/_interop_require_default(require("handlebars"));const _analyzerts=require("./analyzer.js");const _compiledtemplatets=require("./compiled-template.js");const _errorsts=require("./errors.js");const _executorts=require("./executor.js");const _indexts=require("./helpers/index.js");const _parserts=require("./parser.js");const _typests=require("./types.js");const _utils=require("./utils.js");function _define_property(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true})}else{obj[key]=value}return obj}function _interop_require_default(obj){return obj&&obj.__esModule?obj:{default:obj}}class Typebars{compile(template){if((0,_typests.isArrayInput)(template)){const children=[];for(const element of template){children.push(this.compile(element))}return _compiledtemplatets.CompiledTemplate.fromArray(children,{helpers:this.helpers,hbs:this.hbs,compilationCache:this.compilationCache})}if((0,_typests.isObjectInput)(template)){const children={};for(const[key,value]of Object.entries(template)){children[key]=this.compile(value)}return _compiledtemplatets.CompiledTemplate.fromObject(children,{helpers:this.helpers,hbs:this.hbs,compilationCache:this.compilationCache})}if((0,_typests.isLiteralInput)(template)){return _compiledtemplatets.CompiledTemplate.fromLiteral(template,{helpers:this.helpers,hbs:this.hbs,compilationCache:this.compilationCache})}const ast=this.getCachedAst(template);const options={helpers:this.helpers,hbs:this.hbs,compilationCache:this.compilationCache};return _compiledtemplatets.CompiledTemplate.fromTemplate(ast,template,options)}analyze(template,inputSchema,
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});Object.defineProperty(exports,"Typebars",{enumerable:true,get:function(){return Typebars}});const _handlebars=/*#__PURE__*/_interop_require_default(require("handlebars"));const _analyzerts=require("./analyzer.js");const _compiledtemplatets=require("./compiled-template.js");const _errorsts=require("./errors.js");const _executorts=require("./executor.js");const _indexts=require("./helpers/index.js");const _parserts=require("./parser.js");const _schemaresolverts=require("./schema-resolver.js");const _typests=require("./types.js");const _utils=require("./utils.js");function _define_property(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true})}else{obj[key]=value}return obj}function _interop_require_default(obj){return obj&&obj.__esModule?obj:{default:obj}}class Typebars{compile(template){if((0,_typests.isArrayInput)(template)){const children=[];for(const element of template){children.push(this.compile(element))}return _compiledtemplatets.CompiledTemplate.fromArray(children,{helpers:this.helpers,hbs:this.hbs,compilationCache:this.compilationCache})}if((0,_typests.isObjectInput)(template)){const children={};for(const[key,value]of Object.entries(template)){children[key]=this.compile(value)}return _compiledtemplatets.CompiledTemplate.fromObject(children,{helpers:this.helpers,hbs:this.hbs,compilationCache:this.compilationCache})}if((0,_typests.isLiteralInput)(template)){return _compiledtemplatets.CompiledTemplate.fromLiteral(template,{helpers:this.helpers,hbs:this.hbs,compilationCache:this.compilationCache})}const ast=this.getCachedAst(template);const options={helpers:this.helpers,hbs:this.hbs,compilationCache:this.compilationCache};return _compiledtemplatets.CompiledTemplate.fromTemplate(ast,template,options)}analyze(template,inputSchema,options){if((0,_typests.isArrayInput)(template)){return(0,_utils.aggregateArrayAnalysis)(template.length,index=>this.analyze(template[index],inputSchema,options))}if((0,_typests.isObjectInput)(template)){const coerceSchema=options?.coerceSchema;return(0,_utils.aggregateObjectAnalysis)(Object.keys(template),key=>{const childCoerceSchema=coerceSchema?(0,_schemaresolverts.resolveSchemaPath)(coerceSchema,[key]):undefined;return this.analyze(template[key],inputSchema,{identifierSchemas:options?.identifierSchemas,coerceSchema:childCoerceSchema})})}if((0,_typests.isLiteralInput)(template)){return{valid:true,diagnostics:[],outputSchema:(0,_typests.inferPrimitiveSchema)(template)}}const ast=this.getCachedAst(template);return(0,_analyzerts.analyzeFromAst)(ast,template,inputSchema,{identifierSchemas:options?.identifierSchemas,helpers:this.helpers,coerceSchema:options?.coerceSchema})}validate(template,inputSchema,options){const analysis=this.analyze(template,inputSchema,options);return{valid:analysis.valid,diagnostics:analysis.diagnostics}}isValidSyntax(template){if((0,_typests.isArrayInput)(template)){return template.every(v=>this.isValidSyntax(v))}if((0,_typests.isObjectInput)(template)){return Object.values(template).every(v=>this.isValidSyntax(v))}if((0,_typests.isLiteralInput)(template))return true;try{this.getCachedAst(template);return true}catch{return false}}execute(template,data,options){if((0,_typests.isArrayInput)(template)){const result=[];for(const element of template){result.push(this.execute(element,data,options))}return result}if((0,_typests.isObjectInput)(template)){const coerceSchema=options?.coerceSchema;const result={};for(const[key,value]of Object.entries(template)){const childCoerceSchema=coerceSchema?(0,_schemaresolverts.resolveSchemaPath)(coerceSchema,[key]):undefined;result[key]=this.execute(value,data,{...options,coerceSchema:childCoerceSchema})}return result}if((0,_typests.isLiteralInput)(template))return template;const ast=this.getCachedAst(template);if(options?.schema){const analysis=(0,_analyzerts.analyzeFromAst)(ast,template,options.schema,{identifierSchemas:options?.identifierSchemas,helpers:this.helpers});if(!analysis.valid){throw new _errorsts.TemplateAnalysisError(analysis.diagnostics)}}return(0,_executorts.executeFromAst)(ast,template,data,{identifierData:options?.identifierData,hbs:this.hbs,compilationCache:this.compilationCache,coerceSchema:options?.coerceSchema})}analyzeAndExecute(template,inputSchema,data,options){if((0,_typests.isArrayInput)(template)){return(0,_utils.aggregateArrayAnalysisAndExecution)(template.length,index=>this.analyzeAndExecute(template[index],inputSchema,data,options))}if((0,_typests.isObjectInput)(template)){const coerceSchema=options?.coerceSchema;return(0,_utils.aggregateObjectAnalysisAndExecution)(Object.keys(template),key=>{const childCoerceSchema=coerceSchema?(0,_schemaresolverts.resolveSchemaPath)(coerceSchema,[key]):undefined;return this.analyzeAndExecute(template[key],inputSchema,data,{identifierSchemas:options?.identifierSchemas,identifierData:options?.identifierData,coerceSchema:childCoerceSchema})})}if((0,_typests.isLiteralInput)(template)){return{analysis:{valid:true,diagnostics:[],outputSchema:(0,_typests.inferPrimitiveSchema)(template)},value:template}}const ast=this.getCachedAst(template);const analysis=(0,_analyzerts.analyzeFromAst)(ast,template,inputSchema,{identifierSchemas:options?.identifierSchemas,helpers:this.helpers,coerceSchema:options?.coerceSchema});if(!analysis.valid){return{analysis,value:undefined}}const value=(0,_executorts.executeFromAst)(ast,template,data,{identifierData:options?.identifierData,hbs:this.hbs,compilationCache:this.compilationCache,coerceSchema:options?.coerceSchema});return{analysis,value}}registerHelper(name,definition){this.helpers.set(name,definition);this.hbs.registerHelper(name,definition.fn);this.compilationCache.clear();return this}unregisterHelper(name){this.helpers.delete(name);this.hbs.unregisterHelper(name);this.compilationCache.clear();return this}hasHelper(name){return this.helpers.has(name)}clearCaches(){this.astCache.clear();this.compilationCache.clear()}getCachedAst(template){let ast=this.astCache.get(template);if(!ast){ast=(0,_parserts.parse)(template);this.astCache.set(template,ast)}return ast}constructor(options={}){_define_property(this,"hbs",void 0);_define_property(this,"astCache",void 0);_define_property(this,"compilationCache",void 0);_define_property(this,"helpers",new Map);this.hbs=_handlebars.default.create();this.astCache=new _utils.LRUCache(options.astCacheSize??256);this.compilationCache=new _utils.LRUCache(options.compilationCacheSize??256);new _indexts.MathHelpers().register(this);new _indexts.LogicalHelpers().register(this);if(options.helpers){for(const helper of options.helpers){const{name,...definition}=helper;this.registerHelper(name,definition)}}}}
|
|
2
2
|
//# sourceMappingURL=typebars.js.map
|
package/dist/cjs/typebars.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/typebars.ts"],"sourcesContent":["import Handlebars from \"handlebars\";\nimport type { JSONSchema7 } from \"json-schema\";\nimport { analyzeFromAst } from \"./analyzer.ts\";\nimport {\n\tCompiledTemplate,\n\ttype CompiledTemplateOptions,\n} from \"./compiled-template.ts\";\nimport { TemplateAnalysisError } from \"./errors.ts\";\nimport { executeFromAst } from \"./executor.ts\";\nimport { LogicalHelpers, MathHelpers } from \"./helpers/index.ts\";\nimport { parse } from \"./parser.ts\";\nimport type {\n\tAnalysisResult,\n\tAnalyzeAndExecuteOptions,\n\tExecuteOptions,\n\tHelperDefinition,\n\tTemplateEngineOptions,\n\tTemplateInput,\n\tValidationResult,\n} from \"./types.ts\";\nimport {\n\tinferPrimitiveSchema,\n\tisArrayInput,\n\tisLiteralInput,\n\tisObjectInput,\n} from \"./types.ts\";\nimport {\n\taggregateArrayAnalysis,\n\taggregateArrayAnalysisAndExecution,\n\taggregateObjectAnalysis,\n\taggregateObjectAnalysisAndExecution,\n\tLRUCache,\n} from \"./utils\";\n\n// ─── Typebars ────────────────────────────────────────────────────────────────\n// Public entry point of the template engine. Orchestrates three phases:\n//\n// 1. **Parsing** — transforms the template string into an AST (via Handlebars)\n// 2. **Analysis** — static validation + return type inference\n// 3. **Execution** — renders the template with real data\n//\n// ─── Architecture v2 ─────────────────────────────────────────────────────────\n// - **LRU cache** for parsed ASTs and compiled Handlebars templates\n// - **Isolated Handlebars environment** per instance (custom helpers)\n// - **`compile()` pattern**: parse-once / execute-many\n// - **`validate()` method**: API shortcut without `outputSchema`\n// - **`registerHelper()`**: custom helpers with static typing\n// - **`ExecuteOptions`**: options object for `execute()`\n//\n// ─── Template Identifiers ────────────────────────────────────────────────────\n// The `{{key:N}}` syntax allows referencing variables from specific data\n// sources, identified by an integer N.\n//\n// - `identifierSchemas`: mapping `{ [id]: JSONSchema7 }` for static analysis\n// - `identifierData`: mapping `{ [id]: Record<string, unknown> }` for execution\n//\n// Usage:\n// engine.execute(\"{{meetingId:1}}\", data, { identifierData: { 1: node1Data } });\n// engine.analyze(\"{{meetingId:1}}\", schema, { 1: node1Schema });\n\n// ─── Main Class ──────────────────────────────────────────────────────────────\n\nexport class Typebars {\n\t/** Isolated Handlebars environment — each engine has its own helpers */\n\tprivate readonly hbs: typeof Handlebars;\n\n\t/** LRU cache of parsed ASTs (avoids re-parsing) */\n\tprivate readonly astCache: LRUCache<string, hbs.AST.Program>;\n\n\t/** LRU cache of compiled Handlebars templates (avoids recompilation) */\n\tprivate readonly compilationCache: LRUCache<\n\t\tstring,\n\t\tHandlebarsTemplateDelegate\n\t>;\n\n\t/** Custom helpers registered on this instance */\n\tprivate readonly helpers = new Map<string, HelperDefinition>();\n\n\tconstructor(options: TemplateEngineOptions = {}) {\n\t\tthis.hbs = Handlebars.create();\n\t\tthis.astCache = new LRUCache(options.astCacheSize ?? 256);\n\t\tthis.compilationCache = new LRUCache(options.compilationCacheSize ?? 256);\n\n\t\t// ── Built-in helpers ─────────────────────────────────────────────\n\t\tnew MathHelpers().register(this);\n\t\tnew LogicalHelpers().register(this);\n\n\t\t// ── Custom helpers via options ───────────────────────────────────\n\t\tif (options.helpers) {\n\t\t\tfor (const helper of options.helpers) {\n\t\t\t\tconst { name, ...definition } = helper;\n\t\t\t\tthis.registerHelper(name, definition);\n\t\t\t}\n\t\t}\n\t}\n\n\t// ─── Compilation ───────────────────────────────────────────────────────\n\n\t/**\n\t * Compiles a template and returns a `CompiledTemplate` ready to be\n\t * executed or analyzed without re-parsing.\n\t *\n\t * Accepts a `TemplateInput`: string, number, boolean, null, or object.\n\t * For objects, each property is compiled recursively.\n\t *\n\t * @param template - The template to compile\n\t * @returns A reusable `CompiledTemplate`\n\t */\n\tcompile(template: TemplateInput): CompiledTemplate {\n\t\tif (isArrayInput(template)) {\n\t\t\tconst children: CompiledTemplate[] = [];\n\t\t\tfor (const element of template) {\n\t\t\t\tchildren.push(this.compile(element));\n\t\t\t}\n\t\t\treturn CompiledTemplate.fromArray(children, {\n\t\t\t\thelpers: this.helpers,\n\t\t\t\thbs: this.hbs,\n\t\t\t\tcompilationCache: this.compilationCache,\n\t\t\t});\n\t\t}\n\t\tif (isObjectInput(template)) {\n\t\t\tconst children: Record<string, CompiledTemplate> = {};\n\t\t\tfor (const [key, value] of Object.entries(template)) {\n\t\t\t\tchildren[key] = this.compile(value);\n\t\t\t}\n\t\t\treturn CompiledTemplate.fromObject(children, {\n\t\t\t\thelpers: this.helpers,\n\t\t\t\thbs: this.hbs,\n\t\t\t\tcompilationCache: this.compilationCache,\n\t\t\t});\n\t\t}\n\t\tif (isLiteralInput(template)) {\n\t\t\treturn CompiledTemplate.fromLiteral(template, {\n\t\t\t\thelpers: this.helpers,\n\t\t\t\thbs: this.hbs,\n\t\t\t\tcompilationCache: this.compilationCache,\n\t\t\t});\n\t\t}\n\t\tconst ast = this.getCachedAst(template);\n\t\tconst options: CompiledTemplateOptions = {\n\t\t\thelpers: this.helpers,\n\t\t\thbs: this.hbs,\n\t\t\tcompilationCache: this.compilationCache,\n\t\t};\n\t\treturn CompiledTemplate.fromTemplate(ast, template, options);\n\t}\n\n\t// ─── Static Analysis ─────────────────────────────────────────────────────\n\n\t/**\n\t * Statically analyzes a template against a JSON Schema v7 describing\n\t * the available context.\n\t *\n\t * Accepts a `TemplateInput`: string, number, boolean, null, or object.\n\t * For objects, each property is analyzed recursively and the\n\t * `outputSchema` reflects the object structure with resolved types.\n\t *\n\t * @param template - The template to analyze\n\t * @param inputSchema - JSON Schema v7 describing the available variables\n\t * @param identifierSchemas - (optional) Schemas by identifier `{ [id]: JSONSchema7 }`\n\t */\n\tanalyze(\n\t\ttemplate: TemplateInput,\n\t\tinputSchema: JSONSchema7,\n\t\tidentifierSchemas?: Record<number, JSONSchema7>,\n\t): AnalysisResult {\n\t\tif (isArrayInput(template)) {\n\t\t\treturn aggregateArrayAnalysis(template.length, (index) =>\n\t\t\t\tthis.analyze(\n\t\t\t\t\ttemplate[index] as TemplateInput,\n\t\t\t\t\tinputSchema,\n\t\t\t\t\tidentifierSchemas,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tif (isObjectInput(template)) {\n\t\t\treturn aggregateObjectAnalysis(Object.keys(template), (key) =>\n\t\t\t\tthis.analyze(\n\t\t\t\t\ttemplate[key] as TemplateInput,\n\t\t\t\t\tinputSchema,\n\t\t\t\t\tidentifierSchemas,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tif (isLiteralInput(template)) {\n\t\t\treturn {\n\t\t\t\tvalid: true,\n\t\t\t\tdiagnostics: [],\n\t\t\t\toutputSchema: inferPrimitiveSchema(template),\n\t\t\t};\n\t\t}\n\t\tconst ast = this.getCachedAst(template);\n\t\treturn analyzeFromAst(ast, template, inputSchema, {\n\t\t\tidentifierSchemas,\n\t\t\thelpers: this.helpers,\n\t\t});\n\t}\n\n\t// ─── Validation ──────────────────────────────────────────────────────────\n\n\t/**\n\t * Validates a template against a schema without returning the output type.\n\t *\n\t * This is an API shortcut for `analyze()` that only returns `valid` and\n\t * `diagnostics`, without `outputSchema`. The full analysis (including type\n\t * inference) is executed internally — this method provides no performance\n\t * gain, only a simplified API.\n\t *\n\t * @param template - The template to validate\n\t * @param inputSchema - JSON Schema v7 describing the available variables\n\t * @param identifierSchemas - (optional) Schemas by identifier\n\t */\n\tvalidate(\n\t\ttemplate: TemplateInput,\n\t\tinputSchema: JSONSchema7,\n\t\tidentifierSchemas?: Record<number, JSONSchema7>,\n\t): ValidationResult {\n\t\tconst analysis = this.analyze(template, inputSchema, identifierSchemas);\n\t\treturn {\n\t\t\tvalid: analysis.valid,\n\t\t\tdiagnostics: analysis.diagnostics,\n\t\t};\n\t}\n\n\t// ─── Syntax Validation ───────────────────────────────────────────────────\n\n\t/**\n\t * Checks only that the template syntax is valid (parsing).\n\t * Does not require a schema — useful for quick feedback in an editor.\n\t *\n\t * For objects, recursively checks each property.\n\t *\n\t * @param template - The template to validate\n\t * @returns `true` if the template is syntactically correct\n\t */\n\tisValidSyntax(template: TemplateInput): boolean {\n\t\tif (isArrayInput(template)) {\n\t\t\treturn template.every((v) => this.isValidSyntax(v));\n\t\t}\n\t\tif (isObjectInput(template)) {\n\t\t\treturn Object.values(template).every((v) => this.isValidSyntax(v));\n\t\t}\n\t\tif (isLiteralInput(template)) return true;\n\t\ttry {\n\t\t\tthis.getCachedAst(template);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// ─── Execution ───────────────────────────────────────────────────────────\n\n\t/**\n\t * Executes a template with the provided data.\n\t *\n\t * Accepts a `TemplateInput`: string, number, boolean, null, or object.\n\t * For objects, each property is executed recursively and an object with\n\t * resolved values is returned.\n\t *\n\t * If a `schema` is provided in options, static analysis is performed\n\t * before execution. A `TemplateAnalysisError` is thrown on errors.\n\t *\n\t * @param template - The template to execute\n\t * @param data - The context data for rendering\n\t * @param options - Execution options (schema, identifierData, identifierSchemas)\n\t * @returns The execution result\n\t */\n\texecute(\n\t\ttemplate: TemplateInput,\n\t\tdata: Record<string, unknown>,\n\t\toptions?: ExecuteOptions,\n\t): unknown {\n\t\t// ── Array template → recursive execution ─────────────────────────────\n\t\tif (isArrayInput(template)) {\n\t\t\tconst result: unknown[] = [];\n\t\t\tfor (const element of template) {\n\t\t\t\tresult.push(this.execute(element, data, options));\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\n\t\t// ── Object template → recursive execution ────────────────────────────\n\t\tif (isObjectInput(template)) {\n\t\t\tconst result: Record<string, unknown> = {};\n\t\t\tfor (const [key, value] of Object.entries(template)) {\n\t\t\t\tresult[key] = this.execute(value, data, options);\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\n\t\t// ── Passthrough for literal values ────────────────────────────────────\n\t\tif (isLiteralInput(template)) return template;\n\n\t\t// ── Parse once ───────────────────────────────────────────────────────\n\t\tconst ast = this.getCachedAst(template);\n\n\t\t// ── Pre-execution static validation ──────────────────────────────────\n\t\tif (options?.schema) {\n\t\t\tconst analysis = analyzeFromAst(ast, template, options.schema, {\n\t\t\t\tidentifierSchemas: options.identifierSchemas,\n\t\t\t\thelpers: this.helpers,\n\t\t\t});\n\t\t\tif (!analysis.valid) {\n\t\t\t\tthrow new TemplateAnalysisError(analysis.diagnostics);\n\t\t\t}\n\t\t}\n\n\t\t// ── Execution ────────────────────────────────────────────────────────\n\t\treturn executeFromAst(ast, template, data, {\n\t\t\tidentifierData: options?.identifierData,\n\t\t\thbs: this.hbs,\n\t\t\tcompilationCache: this.compilationCache,\n\t\t});\n\t}\n\n\t// ─── Combined Shortcuts ──────────────────────────────────────────────────\n\n\t/**\n\t * Analyzes a template and, if valid, executes it with the provided data.\n\t * Returns both the analysis result and the executed value.\n\t *\n\t * For objects, each property is analyzed and executed recursively.\n\t * The entire object is considered invalid if at least one property is.\n\t *\n\t * @param template - The template\n\t * @param inputSchema - JSON Schema v7 describing the available variables\n\t * @param data - The context data for rendering\n\t * @param options - (optional) Options for template identifiers\n\t * @returns An object `{ analysis, value }` where `value` is `undefined`\n\t * if analysis failed.\n\t */\n\tanalyzeAndExecute(\n\t\ttemplate: TemplateInput,\n\t\tinputSchema: JSONSchema7,\n\t\tdata: Record<string, unknown>,\n\t\toptions?: AnalyzeAndExecuteOptions,\n\t): { analysis: AnalysisResult; value: unknown } {\n\t\tif (isArrayInput(template)) {\n\t\t\treturn aggregateArrayAnalysisAndExecution(template.length, (index) =>\n\t\t\t\tthis.analyzeAndExecute(\n\t\t\t\t\ttemplate[index] as TemplateInput,\n\t\t\t\t\tinputSchema,\n\t\t\t\t\tdata,\n\t\t\t\t\toptions,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tif (isObjectInput(template)) {\n\t\t\treturn aggregateObjectAnalysisAndExecution(Object.keys(template), (key) =>\n\t\t\t\tthis.analyzeAndExecute(\n\t\t\t\t\ttemplate[key] as TemplateInput,\n\t\t\t\t\tinputSchema,\n\t\t\t\t\tdata,\n\t\t\t\t\toptions,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\n\t\tif (isLiteralInput(template)) {\n\t\t\treturn {\n\t\t\t\tanalysis: {\n\t\t\t\t\tvalid: true,\n\t\t\t\t\tdiagnostics: [],\n\t\t\t\t\toutputSchema: inferPrimitiveSchema(template),\n\t\t\t\t},\n\t\t\t\tvalue: template,\n\t\t\t};\n\t\t}\n\n\t\tconst ast = this.getCachedAst(template);\n\t\tconst analysis = analyzeFromAst(ast, template, inputSchema, {\n\t\t\tidentifierSchemas: options?.identifierSchemas,\n\t\t\thelpers: this.helpers,\n\t\t});\n\n\t\tif (!analysis.valid) {\n\t\t\treturn { analysis, value: undefined };\n\t\t}\n\n\t\tconst value = executeFromAst(ast, template, data, {\n\t\t\tidentifierData: options?.identifierData,\n\t\t\thbs: this.hbs,\n\t\t\tcompilationCache: this.compilationCache,\n\t\t});\n\t\treturn { analysis, value };\n\t}\n\n\t// ─── Custom Helper Management ──────────────────────────────────────────\n\n\t/**\n\t * Registers a custom helper on this engine instance.\n\t *\n\t * The helper is available for both execution (via Handlebars) and\n\t * static analysis (via its declared `returnType`).\n\t *\n\t * @param name - Helper name (e.g. `\"uppercase\"`)\n\t * @param definition - Helper definition (implementation + return type)\n\t * @returns `this` to allow chaining\n\t */\n\tregisterHelper(name: string, definition: HelperDefinition): this {\n\t\tthis.helpers.set(name, definition);\n\t\tthis.hbs.registerHelper(name, definition.fn);\n\n\t\t// Invalidate the compilation cache because helpers have changed\n\t\tthis.compilationCache.clear();\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Removes a custom helper from this engine instance.\n\t *\n\t * @param name - Name of the helper to remove\n\t * @returns `this` to allow chaining\n\t */\n\tunregisterHelper(name: string): this {\n\t\tthis.helpers.delete(name);\n\t\tthis.hbs.unregisterHelper(name);\n\n\t\t// Invalidate the compilation cache\n\t\tthis.compilationCache.clear();\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Checks whether a helper is registered on this instance.\n\t *\n\t * @param name - Helper name\n\t * @returns `true` if the helper is registered\n\t */\n\thasHelper(name: string): boolean {\n\t\treturn this.helpers.has(name);\n\t}\n\n\t// ─── Cache Management ──────────────────────────────────────────────────\n\n\t/**\n\t * Clears all internal caches (AST + compilation).\n\t *\n\t * Useful after a configuration change or to free memory.\n\t */\n\tclearCaches(): void {\n\t\tthis.astCache.clear();\n\t\tthis.compilationCache.clear();\n\t}\n\n\t// ─── Internals ─────────────────────────────────────────────────────────\n\n\t/**\n\t * Retrieves the AST of a template from the cache, or parses and caches it.\n\t */\n\tprivate getCachedAst(template: string): hbs.AST.Program {\n\t\tlet ast = this.astCache.get(template);\n\t\tif (!ast) {\n\t\t\tast = parse(template);\n\t\t\tthis.astCache.set(template, ast);\n\t\t}\n\t\treturn ast;\n\t}\n}\n"],"names":["Typebars","compile","template","isArrayInput","children","element","push","CompiledTemplate","fromArray","helpers","hbs","compilationCache","isObjectInput","key","value","Object","entries","fromObject","isLiteralInput","fromLiteral","ast","getCachedAst","options","fromTemplate","analyze","inputSchema","identifierSchemas","aggregateArrayAnalysis","length","index","aggregateObjectAnalysis","keys","valid","diagnostics","outputSchema","inferPrimitiveSchema","analyzeFromAst","validate","analysis","isValidSyntax","every","v","values","execute","data","result","schema","TemplateAnalysisError","executeFromAst","identifierData","analyzeAndExecute","aggregateArrayAnalysisAndExecution","aggregateObjectAnalysisAndExecution","undefined","registerHelper","name","definition","set","fn","clear","unregisterHelper","delete","hasHelper","has","clearCaches","astCache","get","parse","Map","Handlebars","create","LRUCache","astCacheSize","compilationCacheSize","MathHelpers","register","LogicalHelpers","helper"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/typebars.ts"],"sourcesContent":["import Handlebars from \"handlebars\";\nimport type { JSONSchema7 } from \"json-schema\";\nimport type { AnalyzeOptions } from \"./analyzer.ts\";\nimport { analyzeFromAst } from \"./analyzer.ts\";\nimport {\n\tCompiledTemplate,\n\ttype CompiledTemplateOptions,\n} from \"./compiled-template.ts\";\nimport { TemplateAnalysisError } from \"./errors.ts\";\nimport { executeFromAst } from \"./executor.ts\";\nimport { LogicalHelpers, MathHelpers } from \"./helpers/index.ts\";\nimport { parse } from \"./parser.ts\";\nimport { resolveSchemaPath } from \"./schema-resolver.ts\";\nimport type {\n\tAnalysisResult,\n\tAnalyzeAndExecuteOptions,\n\tExecuteOptions,\n\tHelperDefinition,\n\tTemplateEngineOptions,\n\tTemplateInput,\n\tValidationResult,\n} from \"./types.ts\";\nimport {\n\tinferPrimitiveSchema,\n\tisArrayInput,\n\tisLiteralInput,\n\tisObjectInput,\n} from \"./types.ts\";\nimport {\n\taggregateArrayAnalysis,\n\taggregateArrayAnalysisAndExecution,\n\taggregateObjectAnalysis,\n\taggregateObjectAnalysisAndExecution,\n\tLRUCache,\n} from \"./utils\";\n\n// ─── Typebars ────────────────────────────────────────────────────────────────\n// Public entry point of the template engine. Orchestrates three phases:\n//\n// 1. **Parsing** — transforms the template string into an AST (via Handlebars)\n// 2. **Analysis** — static validation + return type inference\n// 3. **Execution** — renders the template with real data\n//\n// ─── Architecture v2 ─────────────────────────────────────────────────────────\n// - **LRU cache** for parsed ASTs and compiled Handlebars templates\n// - **Isolated Handlebars environment** per instance (custom helpers)\n// - **`compile()` pattern**: parse-once / execute-many\n// - **`validate()` method**: API shortcut without `outputSchema`\n// - **`registerHelper()`**: custom helpers with static typing\n// - **`ExecuteOptions`**: options object for `execute()`\n//\n// ─── Template Identifiers ────────────────────────────────────────────────────\n// The `{{key:N}}` syntax allows referencing variables from specific data\n// sources, identified by an integer N.\n//\n// - `identifierSchemas`: mapping `{ [id]: JSONSchema7 }` for static analysis\n// - `identifierData`: mapping `{ [id]: Record<string, unknown> }` for execution\n//\n// Usage:\n// engine.execute(\"{{meetingId:1}}\", data, { identifierData: { 1: node1Data } });\n// engine.analyze(\"{{meetingId:1}}\", schema, { identifierSchemas: { 1: node1Schema } });\n\n// ─── Main Class ──────────────────────────────────────────────────────────────\n\nexport class Typebars {\n\t/** Isolated Handlebars environment — each engine has its own helpers */\n\tprivate readonly hbs: typeof Handlebars;\n\n\t/** LRU cache of parsed ASTs (avoids re-parsing) */\n\tprivate readonly astCache: LRUCache<string, hbs.AST.Program>;\n\n\t/** LRU cache of compiled Handlebars templates (avoids recompilation) */\n\tprivate readonly compilationCache: LRUCache<\n\t\tstring,\n\t\tHandlebarsTemplateDelegate\n\t>;\n\n\t/** Custom helpers registered on this instance */\n\tprivate readonly helpers = new Map<string, HelperDefinition>();\n\n\tconstructor(options: TemplateEngineOptions = {}) {\n\t\tthis.hbs = Handlebars.create();\n\t\tthis.astCache = new LRUCache(options.astCacheSize ?? 256);\n\t\tthis.compilationCache = new LRUCache(options.compilationCacheSize ?? 256);\n\n\t\t// ── Built-in helpers ─────────────────────────────────────────────\n\t\tnew MathHelpers().register(this);\n\t\tnew LogicalHelpers().register(this);\n\n\t\t// ── Custom helpers via options ───────────────────────────────────\n\t\tif (options.helpers) {\n\t\t\tfor (const helper of options.helpers) {\n\t\t\t\tconst { name, ...definition } = helper;\n\t\t\t\tthis.registerHelper(name, definition);\n\t\t\t}\n\t\t}\n\t}\n\n\t// ─── Compilation ───────────────────────────────────────────────────────\n\n\t/**\n\t * Compiles a template and returns a `CompiledTemplate` ready to be\n\t * executed or analyzed without re-parsing.\n\t *\n\t * Accepts a `TemplateInput`: string, number, boolean, null, or object.\n\t * For objects, each property is compiled recursively.\n\t *\n\t * @param template - The template to compile\n\t * @returns A reusable `CompiledTemplate`\n\t */\n\tcompile(template: TemplateInput): CompiledTemplate {\n\t\tif (isArrayInput(template)) {\n\t\t\tconst children: CompiledTemplate[] = [];\n\t\t\tfor (const element of template) {\n\t\t\t\tchildren.push(this.compile(element));\n\t\t\t}\n\t\t\treturn CompiledTemplate.fromArray(children, {\n\t\t\t\thelpers: this.helpers,\n\t\t\t\thbs: this.hbs,\n\t\t\t\tcompilationCache: this.compilationCache,\n\t\t\t});\n\t\t}\n\t\tif (isObjectInput(template)) {\n\t\t\tconst children: Record<string, CompiledTemplate> = {};\n\t\t\tfor (const [key, value] of Object.entries(template)) {\n\t\t\t\tchildren[key] = this.compile(value);\n\t\t\t}\n\t\t\treturn CompiledTemplate.fromObject(children, {\n\t\t\t\thelpers: this.helpers,\n\t\t\t\thbs: this.hbs,\n\t\t\t\tcompilationCache: this.compilationCache,\n\t\t\t});\n\t\t}\n\t\tif (isLiteralInput(template)) {\n\t\t\treturn CompiledTemplate.fromLiteral(template, {\n\t\t\t\thelpers: this.helpers,\n\t\t\t\thbs: this.hbs,\n\t\t\t\tcompilationCache: this.compilationCache,\n\t\t\t});\n\t\t}\n\t\tconst ast = this.getCachedAst(template);\n\t\tconst options: CompiledTemplateOptions = {\n\t\t\thelpers: this.helpers,\n\t\t\thbs: this.hbs,\n\t\t\tcompilationCache: this.compilationCache,\n\t\t};\n\t\treturn CompiledTemplate.fromTemplate(ast, template, options);\n\t}\n\n\t// ─── Static Analysis ─────────────────────────────────────────────────────\n\n\t/**\n\t * Statically analyzes a template against a JSON Schema v7 describing\n\t * the available context.\n\t *\n\t * Accepts a `TemplateInput`: string, number, boolean, null, or object.\n\t * For objects, each property is analyzed recursively and the\n\t * `outputSchema` reflects the object structure with resolved types.\n\t *\n\t * @param template - The template to analyze\n\t * @param inputSchema - JSON Schema v7 describing the available variables\n\t * @param options - (optional) Analysis options (identifierSchemas, coerceSchema)\n\t */\n\tanalyze(\n\t\ttemplate: TemplateInput,\n\t\tinputSchema: JSONSchema7,\n\t\toptions?: AnalyzeOptions,\n\t): AnalysisResult {\n\t\tif (isArrayInput(template)) {\n\t\t\treturn aggregateArrayAnalysis(template.length, (index) =>\n\t\t\t\tthis.analyze(template[index] as TemplateInput, inputSchema, options),\n\t\t\t);\n\t\t}\n\t\tif (isObjectInput(template)) {\n\t\t\tconst coerceSchema = options?.coerceSchema;\n\t\t\treturn aggregateObjectAnalysis(Object.keys(template), (key) => {\n\t\t\t\t// When a coerceSchema is provided, resolve the child property\n\t\t\t\t// schema from it. This allows deeply nested objects to propagate\n\t\t\t\t// coercion at every level.\n\t\t\t\tconst childCoerceSchema = coerceSchema\n\t\t\t\t\t? resolveSchemaPath(coerceSchema, [key])\n\t\t\t\t\t: undefined;\n\t\t\t\treturn this.analyze(template[key] as TemplateInput, inputSchema, {\n\t\t\t\t\tidentifierSchemas: options?.identifierSchemas,\n\t\t\t\t\tcoerceSchema: childCoerceSchema,\n\t\t\t\t});\n\t\t\t});\n\t\t}\n\t\tif (isLiteralInput(template)) {\n\t\t\treturn {\n\t\t\t\tvalid: true,\n\t\t\t\tdiagnostics: [],\n\t\t\t\toutputSchema: inferPrimitiveSchema(template),\n\t\t\t};\n\t\t}\n\t\tconst ast = this.getCachedAst(template);\n\t\treturn analyzeFromAst(ast, template, inputSchema, {\n\t\t\tidentifierSchemas: options?.identifierSchemas,\n\t\t\thelpers: this.helpers,\n\t\t\tcoerceSchema: options?.coerceSchema,\n\t\t});\n\t}\n\n\t// ─── Validation ──────────────────────────────────────────────────────────\n\n\t/**\n\t * Validates a template against a schema without returning the output type.\n\t *\n\t * This is an API shortcut for `analyze()` that only returns `valid` and\n\t * `diagnostics`, without `outputSchema`. The full analysis (including type\n\t * inference) is executed internally — this method provides no performance\n\t * gain, only a simplified API.\n\t *\n\t * @param template - The template to validate\n\t * @param inputSchema - JSON Schema v7 describing the available variables\n\t * @param identifierSchemas - (optional) Schemas by identifier\n\t */\n\tvalidate(\n\t\ttemplate: TemplateInput,\n\t\tinputSchema: JSONSchema7,\n\t\toptions?: AnalyzeOptions,\n\t): ValidationResult {\n\t\tconst analysis = this.analyze(template, 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// ─── Syntax Validation ───────────────────────────────────────────────────\n\n\t/**\n\t * Checks only that the template syntax is valid (parsing).\n\t * Does not require a schema — useful for quick feedback in an editor.\n\t *\n\t * For objects, recursively checks each property.\n\t *\n\t * @param template - The template to validate\n\t * @returns `true` if the template is syntactically correct\n\t */\n\tisValidSyntax(template: TemplateInput): boolean {\n\t\tif (isArrayInput(template)) {\n\t\t\treturn template.every((v) => this.isValidSyntax(v));\n\t\t}\n\t\tif (isObjectInput(template)) {\n\t\t\treturn Object.values(template).every((v) => this.isValidSyntax(v));\n\t\t}\n\t\tif (isLiteralInput(template)) return true;\n\t\ttry {\n\t\t\tthis.getCachedAst(template);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// ─── Execution ───────────────────────────────────────────────────────────\n\n\t/**\n\t * Executes a template with the provided data.\n\t *\n\t * Accepts a `TemplateInput`: string, number, boolean, null, or object.\n\t * For objects, each property is executed recursively and an object with\n\t * resolved values is returned.\n\t *\n\t * If a `schema` is provided in options, static analysis is performed\n\t * before execution. A `TemplateAnalysisError` is thrown on errors.\n\t *\n\t * @param template - The template to execute\n\t * @param data - The context data for rendering\n\t * @param options - Execution options (schema, identifierData, identifierSchemas)\n\t * @returns The execution result\n\t */\n\texecute(\n\t\ttemplate: TemplateInput,\n\t\tdata: Record<string, unknown>,\n\t\toptions?: ExecuteOptions,\n\t): unknown {\n\t\t// ── Array template → recursive execution ─────────────────────────────\n\t\tif (isArrayInput(template)) {\n\t\t\tconst result: unknown[] = [];\n\t\t\tfor (const element of template) {\n\t\t\t\tresult.push(this.execute(element, data, options));\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\n\t\t// ── Object template → recursive execution ────────────────────────────\n\t\tif (isObjectInput(template)) {\n\t\t\tconst coerceSchema = options?.coerceSchema;\n\t\t\tconst result: Record<string, unknown> = {};\n\t\t\tfor (const [key, value] of Object.entries(template)) {\n\t\t\t\tconst childCoerceSchema = coerceSchema\n\t\t\t\t\t? resolveSchemaPath(coerceSchema, [key])\n\t\t\t\t\t: undefined;\n\t\t\t\tresult[key] = this.execute(value, data, {\n\t\t\t\t\t...options,\n\t\t\t\t\tcoerceSchema: childCoerceSchema,\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\n\t\t// ── Passthrough for literal values ────────────────────────────────────\n\t\tif (isLiteralInput(template)) return template;\n\n\t\t// ── Parse once ───────────────────────────────────────────────────────\n\t\tconst ast = this.getCachedAst(template);\n\n\t\t// ── Pre-execution static validation ──────────────────────────────────\n\t\tif (options?.schema) {\n\t\t\tconst analysis = analyzeFromAst(ast, template, options.schema, {\n\t\t\t\tidentifierSchemas: options?.identifierSchemas,\n\t\t\t\thelpers: this.helpers,\n\t\t\t});\n\t\t\tif (!analysis.valid) {\n\t\t\t\tthrow new TemplateAnalysisError(analysis.diagnostics);\n\t\t\t}\n\t\t}\n\n\t\t// ── Execution ────────────────────────────────────────────────────────\n\t\treturn executeFromAst(ast, template, data, {\n\t\t\tidentifierData: options?.identifierData,\n\t\t\thbs: this.hbs,\n\t\t\tcompilationCache: this.compilationCache,\n\t\t\tcoerceSchema: options?.coerceSchema,\n\t\t});\n\t}\n\n\t// ─── Combined Shortcuts ──────────────────────────────────────────────────\n\n\t/**\n\t * Analyzes a template and, if valid, executes it with the provided data.\n\t * Returns both the analysis result and the executed value.\n\t *\n\t * For objects, each property is analyzed and executed recursively.\n\t * The entire object is considered invalid if at least one property is.\n\t *\n\t * @param template - The template\n\t * @param inputSchema - JSON Schema v7 describing the available variables\n\t * @param data - The context data for rendering\n\t * @param options - (optional) Options for template identifiers\n\t * @returns An object `{ analysis, value }` where `value` is `undefined`\n\t * if analysis failed.\n\t */\n\tanalyzeAndExecute(\n\t\ttemplate: TemplateInput,\n\t\tinputSchema: JSONSchema7,\n\t\tdata: Record<string, unknown>,\n\t\toptions?: AnalyzeAndExecuteOptions & { coerceSchema?: JSONSchema7 },\n\t): { analysis: AnalysisResult; value: unknown } {\n\t\tif (isArrayInput(template)) {\n\t\t\treturn aggregateArrayAnalysisAndExecution(template.length, (index) =>\n\t\t\t\tthis.analyzeAndExecute(\n\t\t\t\t\ttemplate[index] as TemplateInput,\n\t\t\t\t\tinputSchema,\n\t\t\t\t\tdata,\n\t\t\t\t\toptions,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tif (isObjectInput(template)) {\n\t\t\tconst coerceSchema = options?.coerceSchema;\n\t\t\treturn aggregateObjectAnalysisAndExecution(\n\t\t\t\tObject.keys(template),\n\t\t\t\t(key) => {\n\t\t\t\t\t// When a coerceSchema is provided, resolve the child property\n\t\t\t\t\t// schema from it for deeply nested coercion propagation.\n\t\t\t\t\tconst childCoerceSchema = coerceSchema\n\t\t\t\t\t\t? resolveSchemaPath(coerceSchema, [key])\n\t\t\t\t\t\t: undefined;\n\t\t\t\t\treturn this.analyzeAndExecute(\n\t\t\t\t\t\ttemplate[key] as TemplateInput,\n\t\t\t\t\t\tinputSchema,\n\t\t\t\t\t\tdata,\n\t\t\t\t\t\t{\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\t\t}\n\n\t\tif (isLiteralInput(template)) {\n\t\t\treturn {\n\t\t\t\tanalysis: {\n\t\t\t\t\tvalid: true,\n\t\t\t\t\tdiagnostics: [],\n\t\t\t\t\toutputSchema: inferPrimitiveSchema(template),\n\t\t\t\t},\n\t\t\t\tvalue: template,\n\t\t\t};\n\t\t}\n\n\t\tconst ast = this.getCachedAst(template);\n\t\tconst analysis = analyzeFromAst(ast, template, inputSchema, {\n\t\t\tidentifierSchemas: options?.identifierSchemas,\n\t\t\thelpers: this.helpers,\n\t\t\tcoerceSchema: options?.coerceSchema,\n\t\t});\n\n\t\tif (!analysis.valid) {\n\t\t\treturn { analysis, value: undefined };\n\t\t}\n\n\t\tconst value = executeFromAst(ast, template, data, {\n\t\t\tidentifierData: options?.identifierData,\n\t\t\thbs: this.hbs,\n\t\t\tcompilationCache: this.compilationCache,\n\t\t\tcoerceSchema: options?.coerceSchema,\n\t\t});\n\t\treturn { analysis, value };\n\t}\n\n\t// ─── Custom Helper Management ──────────────────────────────────────────\n\n\t/**\n\t * Registers a custom helper on this engine instance.\n\t *\n\t * The helper is available for both execution (via Handlebars) and\n\t * static analysis (via its declared `returnType`).\n\t *\n\t * @param name - Helper name (e.g. `\"uppercase\"`)\n\t * @param definition - Helper definition (implementation + return type)\n\t * @returns `this` to allow chaining\n\t */\n\tregisterHelper(name: string, definition: HelperDefinition): this {\n\t\tthis.helpers.set(name, definition);\n\t\tthis.hbs.registerHelper(name, definition.fn);\n\n\t\t// Invalidate the compilation cache because helpers have changed\n\t\tthis.compilationCache.clear();\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Removes a custom helper from this engine instance.\n\t *\n\t * @param name - Name of the helper to remove\n\t * @returns `this` to allow chaining\n\t */\n\tunregisterHelper(name: string): this {\n\t\tthis.helpers.delete(name);\n\t\tthis.hbs.unregisterHelper(name);\n\n\t\t// Invalidate the compilation cache\n\t\tthis.compilationCache.clear();\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Checks whether a helper is registered on this instance.\n\t *\n\t * @param name - Helper name\n\t * @returns `true` if the helper is registered\n\t */\n\thasHelper(name: string): boolean {\n\t\treturn this.helpers.has(name);\n\t}\n\n\t// ─── Cache Management ──────────────────────────────────────────────────\n\n\t/**\n\t * Clears all internal caches (AST + compilation).\n\t *\n\t * Useful after a configuration change or to free memory.\n\t */\n\tclearCaches(): void {\n\t\tthis.astCache.clear();\n\t\tthis.compilationCache.clear();\n\t}\n\n\t// ─── Internals ─────────────────────────────────────────────────────────\n\n\t/**\n\t * Retrieves the AST of a template from the cache, or parses and caches it.\n\t */\n\tprivate getCachedAst(template: string): hbs.AST.Program {\n\t\tlet ast = this.astCache.get(template);\n\t\tif (!ast) {\n\t\t\tast = parse(template);\n\t\t\tthis.astCache.set(template, ast);\n\t\t}\n\t\treturn ast;\n\t}\n}\n"],"names":["Typebars","compile","template","isArrayInput","children","element","push","CompiledTemplate","fromArray","helpers","hbs","compilationCache","isObjectInput","key","value","Object","entries","fromObject","isLiteralInput","fromLiteral","ast","getCachedAst","options","fromTemplate","analyze","inputSchema","aggregateArrayAnalysis","length","index","coerceSchema","aggregateObjectAnalysis","keys","childCoerceSchema","resolveSchemaPath","undefined","identifierSchemas","valid","diagnostics","outputSchema","inferPrimitiveSchema","analyzeFromAst","validate","analysis","isValidSyntax","every","v","values","execute","data","result","schema","TemplateAnalysisError","executeFromAst","identifierData","analyzeAndExecute","aggregateArrayAnalysisAndExecution","aggregateObjectAnalysisAndExecution","registerHelper","name","definition","set","fn","clear","unregisterHelper","delete","hasHelper","has","clearCaches","astCache","get","parse","Map","Handlebars","create","LRUCache","astCacheSize","compilationCacheSize","MathHelpers","register","LogicalHelpers","helper"],"mappings":"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"}
|